import { FastifyError } from '@fastify/error' import * as http from 'http' import { expectAssignable, expectError, expectType } from 'tsd' import fastify, { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from '../../fastify' import { RequestPayload } from '../../types/hooks' import { FindMyWayFindResult } from '../../types/instance' import { HTTPMethods, RawServerDefault } from '../../types/utils' /* * Testing Fastify HTTP Routes and Route Shorthands. * Verifies Request and Reply types as well. * For the route shorthand tests the argument orders are: * - `(path, handler)` * - `(path, options, handler)` * - `(path, options)` */ declare module '../../fastify' { interface FastifyContextConfig { foo: string; bar: number; } } const routeHandler: RouteHandlerMethod = function (request, reply) { expectType(this) expectType(request) expectType(reply) } const routeHandlerWithReturnValue: RouteHandlerMethod = function (request, reply) { expectType(this) expectType(request) expectType(reply) return reply.send() } type LowerCaseHTTPMethods = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | 'options'; ['GET', 'POST', 'PUT', 'PATCH', 'HEAD', 'DELETE', 'OPTIONS'].forEach(method => { // route method expectType(fastify().route({ method: method as HTTPMethods, url: '/', handler: routeHandler })) const lowerCaseMethod: LowerCaseHTTPMethods = method.toLowerCase() as LowerCaseHTTPMethods // method as method expectType(fastify()[lowerCaseMethod]('/', routeHandler)) expectType(fastify()[lowerCaseMethod]('/', {}, routeHandler)) expectType(fastify()[lowerCaseMethod]('/', { handler: routeHandler })) expectType(fastify()[lowerCaseMethod]('/', { handler: routeHandler, errorHandler: (error, request, reply) => { expectType(error) reply.send('error') }, childLoggerFactory: function (logger, bindings, opts) { return logger.child(bindings, opts) } })) interface BodyInterface { prop: string } interface QuerystringInterface { prop: number } interface ParamsInterface { prop: boolean } interface HeadersInterface { prop: string } interface RouteSpecificContextConfigType { extra: boolean } interface RouteGeneric { Body: BodyInterface; Querystring: QuerystringInterface; Params: ParamsInterface; Headers: HeadersInterface; } fastify()[lowerCaseMethod]('/', { config: { foo: 'bar', bar: 100, extra: true } }, (req, res) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.extra) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(res.context.config.extra) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }) fastify().route({ url: '/', method: method as HTTPMethods, config: { foo: 'bar', bar: 100 }, prefixTrailingSlash: 'slash', onRequest: (req, res, done) => { // these handlers are tested in `hooks.test-d.ts` expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preParsing: (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) expectType(payload) expectAssignable<(err?: FastifyError | null, res?: RequestPayload) => void>(done) expectAssignable<(err?: NodeJS.ErrnoException) => void>(done) }, preValidation: (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preHandler: (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, onResponse: (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) expectType(res.statusCode) }, onError: (req, res, error, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preSerialization: (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, onSend: (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, handler: (req, res) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) } }) fastify().route({ url: '/', method: method as HTTPMethods, config: { foo: 'bar', bar: 100 }, prefixTrailingSlash: 'slash', onRequest: async (req, res, done) => { // these handlers are tested in `hooks.test-d.ts` expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preParsing: async (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) expectType(payload) expectAssignable<(err?: FastifyError | null, res?: RequestPayload) => void>(done) expectAssignable<(err?: NodeJS.ErrnoException) => void>(done) }, preValidation: async (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preHandler: async (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, onResponse: async (req, res, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) expectType(res.statusCode) }, onError: async (req, res, error, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, preSerialization: async (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, onSend: async (req, res, payload, done) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) }, handler: (req, res) => { expectType(req.body) expectType(req.query) expectType(req.params) expectType(req.headers) expectType(req.context.config.foo) expectType(req.context.config.bar) expectType(req.context.config.url) expectType(req.context.config.method) expectType(res.context.config.foo) expectType(res.context.config.bar) expectType(req.routeConfig.url) expectType(req.routeConfig.method) } }) }) expectError(fastify().route({ url: '/', method: 'CONNECT', // not a valid method handler: routeHandler })) expectType(fastify().route({ url: '/', method: ['GET', 'POST'], handler: routeHandler })) expectError(fastify().route({ url: '/', method: ['GET', 'POST', 'OPTION'], // OPTION is a typo for OPTIONS handler: routeHandler })) expectError(fastify().route({ url: '/', method: 'GET', handler: routeHandler, schemaErrorFormatter: 500 // Not a valid formatter })) expectType(fastify().route({ url: '/', method: 'GET', handler: routeHandler, schemaErrorFormatter: (errors, dataVar) => new Error('') })) expectError(fastify().route({ prefixTrailingSlash: true // Not a valid value })) expectType(fastify().route({ url: '/', method: 'GET', handler: routeHandlerWithReturnValue })) expectType(fastify().hasRoute({ url: '/', method: 'GET' })) expectType(fastify().hasRoute({ url: '/', method: 'GET', constraints: { version: '1.2.0' } })) expectType(fastify().hasRoute({ url: '/', method: 'GET', constraints: { host: 'auth.fastify.dev' } })) expectType(fastify().hasRoute({ url: '/', method: 'GET', constraints: { host: /.*\.fastify\.dev$/ } })) expectType(fastify().hasRoute({ url: '/', method: 'GET', constraints: { host: /.*\.fastify\.dev$/, version: '1.2.3' } })) expectType(fastify().hasRoute({ url: '/', method: 'GET', constraints: { // constraints value should accept any value number: 12, date: new Date(), boolean: true, function: () => {}, object: { foo: 'bar' } } })) expectType, 'store'>>( fastify().findRoute({ url: '/', method: 'get' }) ) // we should not expose store expectError(fastify().findRoute({ url: '/', method: 'get' }).store) expectType(fastify().route({ url: '/', method: 'get', handler: routeHandlerWithReturnValue })) expectType(fastify().route({ url: '/', method: ['put', 'patch'], handler: routeHandlerWithReturnValue }))