You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1283 lines
				
				28 KiB
			
		
		
			
		
	
	
					1283 lines
				
				28 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const stream = require('stream') | ||
|  | const split = require('split2') | ||
|  | const t = require('tap') | ||
|  | const test = t.test | ||
|  | const sget = require('simple-get').concat | ||
|  | const joi = require('@hapi/joi') | ||
|  | const Fastify = require('..') | ||
|  | const proxyquire = require('proxyquire') | ||
|  | const { FST_ERR_INVALID_URL } = require('../lib/errors') | ||
|  | 
 | ||
|  | test('route', t => { | ||
|  |   t.plan(9) | ||
|  |   const test = t.test | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   test('route - get', t => { | ||
|  |     t.plan(1) | ||
|  |     try { | ||
|  |       fastify.route({ | ||
|  |         method: 'GET', | ||
|  |         url: '/', | ||
|  |         schema: { | ||
|  |           response: { | ||
|  |             '2xx': { | ||
|  |               type: 'object', | ||
|  |               properties: { | ||
|  |                 hello: { | ||
|  |                   type: 'string' | ||
|  |                 } | ||
|  |               } | ||
|  |             } | ||
|  |           } | ||
|  |         }, | ||
|  |         handler: function (req, reply) { | ||
|  |           reply.send({ hello: 'world' }) | ||
|  |         } | ||
|  |       }) | ||
|  |       t.pass() | ||
|  |     } catch (e) { | ||
|  |       t.fail() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   test('missing schema - route', t => { | ||
|  |     t.plan(1) | ||
|  |     try { | ||
|  |       fastify.route({ | ||
|  |         method: 'GET', | ||
|  |         url: '/missing', | ||
|  |         handler: function (req, reply) { | ||
|  |           reply.send({ hello: 'world' }) | ||
|  |         } | ||
|  |       }) | ||
|  |       t.pass() | ||
|  |     } catch (e) { | ||
|  |       t.fail() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   test('invalid handler attribute - route', t => { | ||
|  |     t.plan(1) | ||
|  |     try { | ||
|  |       fastify.get('/', { handler: 'not a function' }, () => {}) | ||
|  |       t.fail() | ||
|  |     } catch (e) { | ||
|  |       t.pass() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   test('Multiple methods', t => { | ||
|  |     t.plan(1) | ||
|  |     try { | ||
|  |       fastify.route({ | ||
|  |         method: ['GET', 'DELETE'], | ||
|  |         url: '/multiple', | ||
|  |         handler: function (req, reply) { | ||
|  |           reply.send({ hello: 'world' }) | ||
|  |         } | ||
|  |       }) | ||
|  |       t.pass() | ||
|  |     } catch (e) { | ||
|  |       t.fail() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   test('Add multiple methods', t => { | ||
|  |     t.plan(1) | ||
|  |     try { | ||
|  |       fastify.get('/add-multiple', function (req, reply) { | ||
|  |         reply.send({ hello: 'Bob!' }) | ||
|  |       }) | ||
|  |       fastify.route({ | ||
|  |         method: ['PUT', 'DELETE'], | ||
|  |         url: '/add-multiple', | ||
|  |         handler: function (req, reply) { | ||
|  |           reply.send({ hello: 'world' }) | ||
|  |         } | ||
|  |       }) | ||
|  |       t.pass() | ||
|  |     } catch (e) { | ||
|  |       t.fail() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, function (err) { | ||
|  |     if (err) t.error(err) | ||
|  |     fastify.server.unref() | ||
|  | 
 | ||
|  |     test('cannot add another route after binding', t => { | ||
|  |       t.plan(1) | ||
|  |       try { | ||
|  |         fastify.route({ | ||
|  |           method: 'GET', | ||
|  |           url: '/another-get-route', | ||
|  |           handler: function (req, reply) { | ||
|  |             reply.send({ hello: 'world' }) | ||
|  |           } | ||
|  |         }) | ||
|  |         t.fail() | ||
|  |       } catch (e) { | ||
|  |         t.pass() | ||
|  |       } | ||
|  |     }) | ||
|  | 
 | ||
|  |     test('route - get', t => { | ||
|  |       t.plan(3) | ||
|  |       sget({ | ||
|  |         method: 'GET', | ||
|  |         url: 'http://localhost:' + fastify.server.address().port | ||
|  |       }, (err, response, body) => { | ||
|  |         t.error(err) | ||
|  |         t.equal(response.statusCode, 200) | ||
|  |         t.same(JSON.parse(body), { hello: 'world' }) | ||
|  |       }) | ||
|  |     }) | ||
|  | 
 | ||
|  |     test('route - missing schema', t => { | ||
|  |       t.plan(3) | ||
|  |       sget({ | ||
|  |         method: 'GET', | ||
|  |         url: 'http://localhost:' + fastify.server.address().port + '/missing' | ||
|  |       }, (err, response, body) => { | ||
|  |         t.error(err) | ||
|  |         t.equal(response.statusCode, 200) | ||
|  |         t.same(JSON.parse(body), { hello: 'world' }) | ||
|  |       }) | ||
|  |     }) | ||
|  | 
 | ||
|  |     test('route - multiple methods', t => { | ||
|  |       t.plan(6) | ||
|  |       sget({ | ||
|  |         method: 'GET', | ||
|  |         url: 'http://localhost:' + fastify.server.address().port + '/multiple' | ||
|  |       }, (err, response, body) => { | ||
|  |         t.error(err) | ||
|  |         t.equal(response.statusCode, 200) | ||
|  |         t.same(JSON.parse(body), { hello: 'world' }) | ||
|  |       }) | ||
|  | 
 | ||
|  |       sget({ | ||
|  |         method: 'DELETE', | ||
|  |         url: 'http://localhost:' + fastify.server.address().port + '/multiple' | ||
|  |       }, (err, response, body) => { | ||
|  |         t.error(err) | ||
|  |         t.equal(response.statusCode, 200) | ||
|  |         t.same(JSON.parse(body), { hello: 'world' }) | ||
|  |       }) | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('invalid schema - route', t => { | ||
|  |   t.plan(3) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.route({ | ||
|  |     handler: () => {}, | ||
|  |     method: 'GET', | ||
|  |     url: '/invalid', | ||
|  |     schema: { | ||
|  |       querystring: { | ||
|  |         id: 'string' | ||
|  |       } | ||
|  |     } | ||
|  |   }) | ||
|  |   fastify.after(err => { | ||
|  |     t.notOk(err, 'the error is throw on preReady') | ||
|  |   }) | ||
|  |   fastify.ready(err => { | ||
|  |     t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD') | ||
|  |     t.match(err.message, /Failed building the validation schema for GET: \/invalid/) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('same route definition object on multiple prefixes', async t => { | ||
|  |   t.plan(2) | ||
|  | 
 | ||
|  |   const routeObject = { | ||
|  |     handler: () => {}, | ||
|  |     method: 'GET', | ||
|  |     url: '/simple' | ||
|  |   } | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.register(async function (f) { | ||
|  |     f.addHook('onRoute', (routeOptions) => { | ||
|  |       t.equal(routeOptions.url, '/v1/simple') | ||
|  |     }) | ||
|  |     f.route(routeObject) | ||
|  |   }, { prefix: '/v1' }) | ||
|  |   fastify.register(async function (f) { | ||
|  |     f.addHook('onRoute', (routeOptions) => { | ||
|  |       t.equal(routeOptions.url, '/v2/simple') | ||
|  |     }) | ||
|  |     f.route(routeObject) | ||
|  |   }, { prefix: '/v2' }) | ||
|  | 
 | ||
|  |   await fastify.ready() | ||
|  | }) | ||
|  | 
 | ||
|  | test('path can be specified in place of uri', t => { | ||
|  |   t.plan(3) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/path', | ||
|  |     handler: function (req, reply) { | ||
|  |       reply.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const reqOpts = { | ||
|  |     method: 'GET', | ||
|  |     url: '/path' | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.inject(reqOpts, (err, res) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.same(JSON.parse(res.payload), { hello: 'world' }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('invalid bodyLimit option - route', t => { | ||
|  |   t.plan(2) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   try { | ||
|  |     fastify.route({ | ||
|  |       bodyLimit: false, | ||
|  |       method: 'PUT', | ||
|  |       handler: () => null | ||
|  |     }) | ||
|  |     t.fail('bodyLimit must be an integer') | ||
|  |   } catch (err) { | ||
|  |     t.equal(err.message, "'bodyLimit' option must be an integer > 0. Got 'false'") | ||
|  |   } | ||
|  | 
 | ||
|  |   try { | ||
|  |     fastify.post('/url', { bodyLimit: 10000.1 }, () => null) | ||
|  |     t.fail('bodyLimit must be an integer') | ||
|  |   } catch (err) { | ||
|  |     t.equal(err.message, "'bodyLimit' option must be an integer > 0. Got '10000.1'") | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | test('handler function in options of shorthand route should works correctly', t => { | ||
|  |   t.plan(3) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.get('/foo', { | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/foo' | ||
|  |   }, (err, res) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.same(JSON.parse(res.payload), { hello: 'world' }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('does not mutate joi schemas', t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  |   function validatorCompiler ({ schema, method, url, httpPart }) { | ||
|  |     // Needed to extract the params part,
 | ||
|  |     // without the JSON-schema encapsulation
 | ||
|  |     // that is automatically added by the short
 | ||
|  |     // form of params.
 | ||
|  |     schema = joi.object(schema.properties) | ||
|  | 
 | ||
|  |     return validateHttpData | ||
|  | 
 | ||
|  |     function validateHttpData (data) { | ||
|  |       return schema.validate(data) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.setValidatorCompiler(validatorCompiler) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     path: '/foo/:an_id', | ||
|  |     method: 'GET', | ||
|  |     schema: { | ||
|  |       params: { an_id: joi.number() } | ||
|  |     }, | ||
|  |     handler (req, res) { | ||
|  |       t.same(req.params, { an_id: 42 }) | ||
|  |       res.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/foo/42' | ||
|  |   }, (err, result) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(result.statusCode, 200) | ||
|  |     t.same(JSON.parse(result.payload), { hello: 'world' }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('multiple routes with one schema', t => { | ||
|  |   t.plan(2) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const schema = { | ||
|  |     query: { | ||
|  |       id: { type: 'number' } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     schema, | ||
|  |     method: 'GET', | ||
|  |     path: '/first/:id', | ||
|  |     handler (req, res) { | ||
|  |       res.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     schema, | ||
|  |     method: 'GET', | ||
|  |     path: '/second/:id', | ||
|  |     handler (req, res) { | ||
|  |       res.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.ready(error => { | ||
|  |     t.error(error) | ||
|  |     t.same(schema, schema) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('route error handler overrides default error handler', t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const customRouteErrorHandler = (error, request, reply) => { | ||
|  |     t.equal(error.message, 'Wrong Pot Error') | ||
|  | 
 | ||
|  |     reply.code(418).send({ | ||
|  |       message: 'Make a brew', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Wrong Pot Error' | ||
|  |     }) | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/coffee', | ||
|  |     handler: (req, res) => { | ||
|  |       res.send(new Error('Wrong Pot Error')) | ||
|  |     }, | ||
|  |     errorHandler: customRouteErrorHandler | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 418) | ||
|  |     t.same(JSON.parse(res.payload), { | ||
|  |       message: 'Make a brew', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Wrong Pot Error' | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('route error handler does not affect other routes', t => { | ||
|  |   t.plan(3) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const customRouteErrorHandler = (error, request, reply) => { | ||
|  |     t.equal(error.message, 'Wrong Pot Error') | ||
|  | 
 | ||
|  |     reply.code(418).send({ | ||
|  |       message: 'Make a brew', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Wrong Pot Error' | ||
|  |     }) | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/coffee', | ||
|  |     handler: (req, res) => { | ||
|  |       res.send(new Error('Wrong Pot Error')) | ||
|  |     }, | ||
|  |     errorHandler: customRouteErrorHandler | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/tea', | ||
|  |     handler: (req, res) => { | ||
|  |       res.send(new Error('No tea today')) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/tea' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 500) | ||
|  |     t.same(JSON.parse(res.payload), { | ||
|  |       message: 'No tea today', | ||
|  |       statusCode: 500, | ||
|  |       error: 'Internal Server Error' | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('async error handler for a route', t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const customRouteErrorHandler = async (error, request, reply) => { | ||
|  |     t.equal(error.message, 'Delayed Pot Error') | ||
|  |     reply.code(418) | ||
|  |     return { | ||
|  |       message: 'Make a brew sometime later', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Delayed Pot Error' | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/late-coffee', | ||
|  |     handler: (req, res) => { | ||
|  |       res.send(new Error('Delayed Pot Error')) | ||
|  |     }, | ||
|  |     errorHandler: customRouteErrorHandler | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/late-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 418) | ||
|  |     t.same(JSON.parse(res.payload), { | ||
|  |       message: 'Make a brew sometime later', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Delayed Pot Error' | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('route error handler overrides global custom error handler', t => { | ||
|  |   t.plan(4) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const customGlobalErrorHandler = (error, request, reply) => { | ||
|  |     t.error(error) | ||
|  |     reply.code(429).send({ message: 'Too much coffee' }) | ||
|  |   } | ||
|  | 
 | ||
|  |   const customRouteErrorHandler = (error, request, reply) => { | ||
|  |     t.equal(error.message, 'Wrong Pot Error') | ||
|  |     reply.code(418).send({ | ||
|  |       message: 'Make a brew', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Wrong Pot Error' | ||
|  |     }) | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.setErrorHandler(customGlobalErrorHandler) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     handler: (req, res) => { | ||
|  |       res.send(new Error('Wrong Pot Error')) | ||
|  |     }, | ||
|  |     errorHandler: customRouteErrorHandler | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/more-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 418) | ||
|  |     t.same(JSON.parse(res.payload), { | ||
|  |       message: 'Make a brew', | ||
|  |       statusCode: 418, | ||
|  |       error: 'Wrong Pot Error' | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('throws when route with empty url', async t => { | ||
|  |   t.plan(1) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  |   try { | ||
|  |     await fastify.route({ | ||
|  |       method: 'GET', | ||
|  |       url: '', | ||
|  |       handler: (req, res) => { | ||
|  |         res.send('hi!') | ||
|  |       } | ||
|  |     }).ready() | ||
|  |   } catch (err) { | ||
|  |     t.equal(err.message, 'The first character of a path should be `/` or `*`') | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | test('throws when route with empty url in shorthand declaration', async t => { | ||
|  |   t.plan(1) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  |   try { | ||
|  |     await fastify.get( | ||
|  |       '', | ||
|  |       async function handler () { return {} } | ||
|  |     ).ready() | ||
|  |   } catch (err) { | ||
|  |     t.equal(err.message, 'The path could not be empty') | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | test('throws when route-level error handler is not a function', t => { | ||
|  |   t.plan(1) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   try { | ||
|  |     fastify.route({ | ||
|  |       method: 'GET', | ||
|  |       url: '/tea', | ||
|  |       handler: (req, res) => { | ||
|  |         res.send('hi!') | ||
|  |       }, | ||
|  |       errorHandler: 'teapot' | ||
|  |     }) | ||
|  |   } catch (err) { | ||
|  |     t.equal(err.message, 'Error Handler for GET:/tea route, if defined, must be a function') | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | test('Creates a HEAD route for each GET one', t => { | ||
|  |   t.plan(8) | ||
|  | 
 | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send({ here: 'is coffee' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/some-light', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send('Get some light!') | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/more-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/json; charset=utf-8') | ||
|  |     t.same(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/some-light' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'text/plain; charset=utf-8') | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Creates a HEAD route for a GET one with prefixTrailingSlash', async (t) => { | ||
|  |   t.plan(1) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const arr = [] | ||
|  |   fastify.register((instance, opts, next) => { | ||
|  |     instance.addHook('onRoute', (routeOptions) => { | ||
|  |       arr.push(`${routeOptions.method} ${routeOptions.url}`) | ||
|  |     }) | ||
|  | 
 | ||
|  |     instance.route({ | ||
|  |       method: 'GET', | ||
|  |       path: '/', | ||
|  |       exposeHeadRoute: true, | ||
|  |       prefixTrailingSlash: 'both', | ||
|  |       handler: (req, reply) => { | ||
|  |         reply.send({ here: 'is coffee' }) | ||
|  |       } | ||
|  |     }) | ||
|  | 
 | ||
|  |     next() | ||
|  |   }, { prefix: '/v1' }) | ||
|  | 
 | ||
|  |   await fastify.ready() | ||
|  | 
 | ||
|  |   t.ok(true) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Will not create a HEAD route that is not GET', t => { | ||
|  |   t.plan(11) | ||
|  | 
 | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send({ here: 'is coffee' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/some-light', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'POST', | ||
|  |     path: '/something', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send({ look: 'It is something!' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/more-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/json; charset=utf-8') | ||
|  |     t.same(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/some-light' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], undefined) | ||
|  |     t.equal(res.headers['content-length'], '0') | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/something' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 404) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD route should handle properly each response type', t => { | ||
|  |   t.plan(25) | ||
|  | 
 | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  |   const resString = 'Found me!' | ||
|  |   const resJSON = { here: 'is Johnny' } | ||
|  |   const resBuffer = Buffer.from('I am a buffer!') | ||
|  |   const resStream = stream.Readable.from('I am a stream!') | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/json', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send(resJSON) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/string', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send(resString) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/buffer', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send(resBuffer) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/buffer-with-content-type', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.headers({ 'content-type': 'image/jpeg' }) | ||
|  |       reply.send(resBuffer) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/stream', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resStream | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/json' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/json; charset=utf-8') | ||
|  |     t.equal(res.headers['content-length'], `${Buffer.byteLength(JSON.stringify(resJSON))}`) | ||
|  |     t.same(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/string' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'text/plain; charset=utf-8') | ||
|  |     t.equal(res.headers['content-length'], `${Buffer.byteLength(resString)}`) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/buffer' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/buffer-with-content-type' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'image/jpeg') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/stream' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], undefined) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD route should respect custom onSend handlers', t => { | ||
|  |   t.plan(6) | ||
|  | 
 | ||
|  |   let counter = 0 | ||
|  |   const resBuffer = Buffer.from('I am a coffee!') | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  |   const customOnSend = (res, reply, payload, done) => { | ||
|  |     counter = counter + 1 | ||
|  |     done(null, payload) | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.send(resBuffer) | ||
|  |     }, | ||
|  |     onSend: [customOnSend, customOnSend] | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/more-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |     t.equal(counter, 2) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('route onSend can be function or array of functions', t => { | ||
|  |   t.plan(12) | ||
|  |   const counters = { single: 0, multiple: 0 } | ||
|  | 
 | ||
|  |   const resBuffer = Buffer.from('I am a coffee!') | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/coffee', | ||
|  |     handler: () => resBuffer, | ||
|  |     onSend: (res, reply, payload, done) => { | ||
|  |       counters.single += 1 | ||
|  |       done(null, payload) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const customOnSend = (res, reply, payload, done) => { | ||
|  |     counters.multiple += 1 | ||
|  |     done(null, payload) | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     handler: () => resBuffer, | ||
|  |     onSend: [customOnSend, customOnSend] | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ method: 'HEAD', url: '/coffee' }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |     t.equal(counters.single, 1) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ method: 'HEAD', url: '/more-coffee' }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |     t.equal(counters.multiple, 2) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('no warning for exposeHeadRoute', async t => { | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     exposeHeadRoute: true, | ||
|  |     async handler () { | ||
|  |       return 'hello world' | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   const listener = (w) => { | ||
|  |     t.fail('no warning') | ||
|  |   } | ||
|  | 
 | ||
|  |   process.on('warning', listener) | ||
|  | 
 | ||
|  |   await fastify.listen(0) | ||
|  | 
 | ||
|  |   process.removeListener('warning', listener) | ||
|  | 
 | ||
|  |   await fastify.close() | ||
|  | }) | ||
|  | 
 | ||
|  | test("HEAD route should handle stream.on('error')", t => { | ||
|  |   t.plan(6) | ||
|  | 
 | ||
|  |   const resStream = stream.Readable.from('Hello with error!') | ||
|  |   const logStream = split(JSON.parse) | ||
|  |   const expectedError = new Error('Hello!') | ||
|  |   const fastify = Fastify({ | ||
|  |     logger: { | ||
|  |       stream: logStream, | ||
|  |       level: 'error' | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/more-coffee', | ||
|  |     exposeHeadRoute: true, | ||
|  |     handler: (req, reply) => { | ||
|  |       process.nextTick(() => resStream.emit('error', expectedError)) | ||
|  |       return resStream | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   logStream.once('data', line => { | ||
|  |     const { message, stack } = expectedError | ||
|  |     t.same(line.err, { type: 'Error', message, stack }) | ||
|  |     t.equal(line.msg, 'Error on Stream found for HEAD route') | ||
|  |     t.equal(line.level, 50) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/more-coffee' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD route should not be exposed by default', t => { | ||
|  |   t.plan(7) | ||
|  | 
 | ||
|  |   const resStream = stream.Readable.from('Hello with error!') | ||
|  |   const resJson = { hello: 'world' } | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/without-flag', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resStream | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     exposeHeadRoute: true, | ||
|  |     method: 'GET', | ||
|  |     path: '/with-flag', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resJson | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/without-flag' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 404) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/with-flag' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/json; charset=utf-8') | ||
|  |     t.equal(res.headers['content-length'], `${Buffer.byteLength(JSON.stringify(resJson))}`) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD route should be exposed if route exposeHeadRoute is set', t => { | ||
|  |   t.plan(7) | ||
|  | 
 | ||
|  |   const resBuffer = Buffer.from('I am a coffee!') | ||
|  |   const resJson = { hello: 'world' } | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: false }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     exposeHeadRoute: true, | ||
|  |     method: 'GET', | ||
|  |     path: '/one', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resBuffer | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/two', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resJson | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/one' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/octet-stream') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/two' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 404) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Set a custom HEAD route before GET one without disabling exposeHeadRoutes (global)', t => { | ||
|  |   t.plan(6) | ||
|  | 
 | ||
|  |   const resBuffer = Buffer.from('I am a coffee!') | ||
|  |   const fastify = Fastify({ | ||
|  |     exposeHeadRoutes: true | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'HEAD', | ||
|  |     path: '/one', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.header('content-type', 'application/pdf') | ||
|  |       reply.header('content-length', `${resBuffer.byteLength}`) | ||
|  |       reply.header('x-custom-header', 'some-custom-header') | ||
|  |       reply.send() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     path: '/one', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resBuffer | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/one' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/pdf') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.headers['x-custom-header'], 'some-custom-header') | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Set a custom HEAD route before GET one without disabling exposeHeadRoutes (route)', t => { | ||
|  |   t.plan(7) | ||
|  | 
 | ||
|  |   function onWarning (code) { | ||
|  |     t.equal(code, 'FSTDEP007') | ||
|  |   } | ||
|  |   const warning = { | ||
|  |     emit: onWarning | ||
|  |   } | ||
|  | 
 | ||
|  |   const route = proxyquire('../lib/route', { './warnings': warning }) | ||
|  |   const fastify = proxyquire('..', { './lib/route.js': route })() | ||
|  | 
 | ||
|  |   const resBuffer = Buffer.from('I am a coffee!') | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'HEAD', | ||
|  |     path: '/one', | ||
|  |     handler: (req, reply) => { | ||
|  |       reply.header('content-type', 'application/pdf') | ||
|  |       reply.header('content-length', `${resBuffer.byteLength}`) | ||
|  |       reply.header('x-custom-header', 'some-custom-header') | ||
|  |       reply.send() | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     exposeHeadRoute: true, | ||
|  |     path: '/one', | ||
|  |     handler: (req, reply) => { | ||
|  |       return resBuffer | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'HEAD', | ||
|  |     url: '/one' | ||
|  |   }, (error, res) => { | ||
|  |     t.error(error) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     t.equal(res.headers['content-type'], 'application/pdf') | ||
|  |     t.equal(res.headers['content-length'], `${resBuffer.byteLength}`) | ||
|  |     t.equal(res.headers['x-custom-header'], 'some-custom-header') | ||
|  |     t.equal(res.body, '') | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD routes properly auto created for GET routes when prefixTrailingSlash: \'no-slash\'', t => { | ||
|  |   t.plan(2) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.register(function routes (f, opts, next) { | ||
|  |     f.route({ | ||
|  |       method: 'GET', | ||
|  |       url: '/', | ||
|  |       exposeHeadRoute: true, | ||
|  |       prefixTrailingSlash: 'no-slash', | ||
|  |       handler: (req, reply) => { | ||
|  |         reply.send({ hello: 'world' }) | ||
|  |       } | ||
|  |     }) | ||
|  | 
 | ||
|  |     next() | ||
|  |   }, { prefix: '/prefix' }) | ||
|  | 
 | ||
|  |   fastify.inject({ url: '/prefix/prefix', method: 'HEAD' }, (err, res) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(res.statusCode, 404) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('HEAD routes properly auto created for GET routes when prefixTrailingSlash: \'both\'', async t => { | ||
|  |   t.plan(3) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.register(function routes (f, opts, next) { | ||
|  |     f.route({ | ||
|  |       method: 'GET', | ||
|  |       url: '/', | ||
|  |       exposeHeadRoute: true, | ||
|  |       prefixTrailingSlash: 'both', | ||
|  |       handler: (req, reply) => { | ||
|  |         reply.send({ hello: 'world' }) | ||
|  |       } | ||
|  |     }) | ||
|  | 
 | ||
|  |     next() | ||
|  |   }, { prefix: '/prefix' }) | ||
|  | 
 | ||
|  |   const doublePrefixReply = await fastify.inject({ url: '/prefix/prefix', method: 'HEAD' }) | ||
|  |   const trailingSlashReply = await fastify.inject({ url: '/prefix/', method: 'HEAD' }) | ||
|  |   const noneTrailingReply = await fastify.inject({ url: '/prefix', method: 'HEAD' }) | ||
|  | 
 | ||
|  |   t.equal(doublePrefixReply.statusCode, 404) | ||
|  |   t.equal(trailingSlashReply.statusCode, 200) | ||
|  |   t.equal(noneTrailingReply.statusCode, 200) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Request and Reply share the route config', async t => { | ||
|  |   t.plan(3) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   const config = { | ||
|  |     this: 'is a string', | ||
|  |     thisIs: function aFunction () {} | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.route({ | ||
|  |     method: 'GET', | ||
|  |     url: '/', | ||
|  |     config, | ||
|  |     handler: (req, reply) => { | ||
|  |       t.same(req.context, reply.context) | ||
|  |       t.same(req.context.config, reply.context.config) | ||
|  |       t.match(req.context.config, config, 'there are url and method additional properties') | ||
|  | 
 | ||
|  |       reply.send({ hello: 'world' }) | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   await fastify.inject('/') | ||
|  | }) | ||
|  | 
 | ||
|  | test('Will not try to re-createprefixed HEAD route if it already exists and exposeHeadRoutes is true', async (t) => { | ||
|  |   t.plan(1) | ||
|  | 
 | ||
|  |   const fastify = Fastify({ exposeHeadRoutes: true }) | ||
|  | 
 | ||
|  |   fastify.register((scope, opts, next) => { | ||
|  |     scope.route({ | ||
|  |       method: 'HEAD', | ||
|  |       path: '/route', | ||
|  |       handler: (req, reply) => { | ||
|  |         reply.header('content-type', 'text/plain') | ||
|  |         reply.send('custom HEAD response') | ||
|  |       } | ||
|  |     }) | ||
|  |     scope.route({ | ||
|  |       method: 'GET', | ||
|  |       path: '/route', | ||
|  |       handler: (req, reply) => { | ||
|  |         reply.send({ ok: true }) | ||
|  |       } | ||
|  |     }) | ||
|  | 
 | ||
|  |     next() | ||
|  |   }, { prefix: '/prefix' }) | ||
|  | 
 | ||
|  |   await fastify.ready() | ||
|  | 
 | ||
|  |   t.ok(true) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Correct error message is produced if "path" option is used', t => { | ||
|  |   t.plan(2) | ||
|  | 
 | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   t.throws(() => { | ||
|  |     fastify.route({ | ||
|  |       method: 'GET', | ||
|  |       path: '/test' | ||
|  |     }) | ||
|  |   }, new Error('Missing handler function for GET:/test route.')) | ||
|  | 
 | ||
|  |   t.throws(() => { | ||
|  |     fastify.route({ | ||
|  |       method: 'POST', | ||
|  |       url: '/test', | ||
|  |       handler: function () {}, | ||
|  |       errorHandler: '' | ||
|  |     }) | ||
|  |   }, new Error('Error Handler for POST:/test route, if defined, must be a function')) | ||
|  | }) | ||
|  | 
 | ||
|  | test('invalid url attribute - non string URL', t => { | ||
|  |   t.plan(1) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   try { | ||
|  |     fastify.get(/^\/(donations|skills|blogs)/, () => {}) | ||
|  |   } catch (error) { | ||
|  |     t.equal(error.code, FST_ERR_INVALID_URL().code) | ||
|  |   } | ||
|  | }) |