fastify 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/docs/Guides/Ecosystem.md +19 -8
  2. package/docs/Guides/Plugins-Guide.md +44 -0
  3. package/docs/Reference/Reply.md +5 -5
  4. package/docs/Reference/Request.md +19 -6
  5. package/docs/Reference/Routes.md +2 -2
  6. package/docs/Reference/Type-Providers.md +3 -3
  7. package/fastify.js +1 -1
  8. package/lib/contentTypeParser.js +10 -9
  9. package/lib/context.js +27 -3
  10. package/lib/error-handler.js +7 -3
  11. package/lib/handleRequest.js +15 -13
  12. package/lib/reply.js +42 -34
  13. package/lib/request.js +36 -18
  14. package/lib/route.js +16 -10
  15. package/lib/schema-controller.js +7 -1
  16. package/lib/server.js +2 -2
  17. package/lib/symbols.js +2 -0
  18. package/lib/validation.js +4 -3
  19. package/lib/warnings.js +2 -0
  20. package/package.json +26 -26
  21. package/test/404s.test.js +19 -1
  22. package/test/context-config.test.js +2 -1
  23. package/test/handler-context.test.js +12 -30
  24. package/test/internals/contentTypeParser.test.js +3 -3
  25. package/test/internals/handleRequest.test.js +4 -3
  26. package/test/internals/reply-serialize.test.js +9 -9
  27. package/test/internals/reply.test.js +10 -9
  28. package/test/internals/request-validate.test.js +97 -9
  29. package/test/internals/request.test.js +57 -3
  30. package/test/internals/validation.test.js +15 -0
  31. package/test/plugin.test.js +1 -1
  32. package/test/reply-code.test.js +59 -0
  33. package/test/route.test.js +29 -0
  34. package/test/router-options.test.js +33 -66
  35. package/test/schema-feature.test.js +25 -0
  36. package/test/schema-validation.test.js +19 -0
  37. package/test/search.test.js +169 -30
  38. package/test/server.test.js +54 -0
  39. package/types/request.d.ts +9 -3
@@ -1,19 +1,9 @@
1
1
  'use strict'
2
-
3
- const http = require('http')
4
2
  const test = require('tap').test
3
+ const { kRouteContext } = require('../lib/symbols')
5
4
  const fastify = require('../')
6
5
 
7
- function getUrl (app) {
8
- const { address, port } = app.server.address()
9
- if (address === '::1') {
10
- return `http://[${address}]:${port}`
11
- } else {
12
- return `http://${address}:${port}`
13
- }
14
- }
15
-
16
- test('handlers receive correct `this` context', (t) => {
6
+ test('handlers receive correct `this` context', async (t) => {
17
7
  t.plan(4)
18
8
 
19
9
  // simulate plugin that uses fastify-plugin
@@ -32,32 +22,24 @@ test('handlers receive correct `this` context', (t) => {
32
22
  reply.send()
33
23
  })
34
24
 
35
- instance.listen({ port: 0 }, (err) => {
36
- instance.server.unref()
37
- if (err) t.threw(err)
38
- t.ok(instance.foo)
39
- t.equal(instance.foo, 'foo')
25
+ await instance.inject('/')
40
26
 
41
- http.get(getUrl(instance), () => {}).on('error', t.threw)
42
- })
27
+ t.ok(instance.foo)
28
+ t.equal(instance.foo, 'foo')
43
29
  })
44
30
 
45
- test('handlers have access to the internal context', (t) => {
31
+ test('handlers have access to the internal context', async (t) => {
46
32
  t.plan(5)
47
33
 
48
34
  const instance = fastify()
49
35
  instance.get('/', { config: { foo: 'bar' } }, function (req, reply) {
50
- t.ok(reply.context)
51
- t.ok(reply.context.config)
52
- t.type(reply.context.config, Object)
53
- t.ok(reply.context.config.foo)
54
- t.equal(reply.context.config.foo, 'bar')
36
+ t.ok(reply[kRouteContext])
37
+ t.ok(reply[kRouteContext].config)
38
+ t.type(reply[kRouteContext].config, Object)
39
+ t.ok(reply[kRouteContext].config.foo)
40
+ t.equal(reply[kRouteContext].config.foo, 'bar')
55
41
  reply.send()
56
42
  })
57
43
 
58
- instance.listen({ port: 0 }, (err) => {
59
- instance.server.unref()
60
- if (err) t.threw(err)
61
- http.get(getUrl(instance), () => {}).on('error', t.threw)
62
- })
44
+ await instance.inject('/')
63
45
  })
@@ -4,7 +4,7 @@ const t = require('tap')
4
4
  const proxyquire = require('proxyquire')
5
5
  const test = t.test
6
6
  const { Readable } = require('stream')
7
- const { kTestInternals } = require('../../lib/symbols')
7
+ const { kTestInternals, kRouteContext } = require('../../lib/symbols')
8
8
  const Request = require('../../lib/request')
9
9
  const Reply = require('../../lib/reply')
10
10
 
@@ -50,7 +50,7 @@ test('rawBody function', t => {
50
50
  internals.rawBody(
51
51
  request,
52
52
  reply,
53
- reply.context._parserOptions,
53
+ reply[kRouteContext]._parserOptions,
54
54
  parser,
55
55
  done
56
56
  )
@@ -103,7 +103,7 @@ test('Should support Webpack and faux modules', t => {
103
103
  internals.rawBody(
104
104
  request,
105
105
  reply,
106
- reply.context._parserOptions,
106
+ reply[kRouteContext]._parserOptions,
107
107
  parser,
108
108
  done
109
109
  )
@@ -5,6 +5,7 @@ const handleRequest = require('../../lib/handleRequest')
5
5
  const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
6
6
  const Request = require('../../lib/request')
7
7
  const Reply = require('../../lib/reply')
8
+ const { kRouteContext } = require('../../lib/symbols')
8
9
  const buildSchema = require('../../lib/validation').compileSchemasForValidation
9
10
  const sget = require('simple-get').concat
10
11
 
@@ -69,7 +70,7 @@ test('handler function - invalid schema', t => {
69
70
  buildSchema(context, schemaValidator)
70
71
  const request = {
71
72
  body: { hello: 'world' },
72
- context
73
+ [kRouteContext]: context
73
74
  }
74
75
  internals.handler(request, new Reply(res, request))
75
76
  })
@@ -96,7 +97,7 @@ test('handler function - reply', t => {
96
97
  onError: []
97
98
  }
98
99
  buildSchema(context, schemaValidator)
99
- internals.handler({}, new Reply(res, { context }))
100
+ internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
100
101
  })
101
102
 
102
103
  test('handler function - preValidationCallback with finished response', t => {
@@ -121,7 +122,7 @@ test('handler function - preValidationCallback with finished response', t => {
121
122
  onError: []
122
123
  }
123
124
  buildSchema(context, schemaValidator)
124
- internals.handler({}, new Reply(res, { context }))
125
+ internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
125
126
  })
126
127
 
127
128
  test('request should be defined in onSend Hook on post request with content type application/json', t => {
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('tap')
4
- const { kReplySerializeWeakMap } = require('../../lib/symbols')
4
+ const { kReplySerializeWeakMap, kRouteContext } = require('../../lib/symbols')
5
5
  const Fastify = require('../../fastify')
6
6
 
7
7
  function getDefaultSchema () {
@@ -172,9 +172,9 @@ test('Reply#compileSerializationSchema', t => {
172
172
  fastify.get('/', (req, reply) => {
173
173
  const input = { hello: 'world' }
174
174
 
175
- t.equal(reply.context[kReplySerializeWeakMap], null)
175
+ t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
176
176
  t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
177
- t.type(reply.context[kReplySerializeWeakMap], WeakMap)
177
+ t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)
178
178
  t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
179
179
 
180
180
  reply.send({ hello: 'world' })
@@ -231,7 +231,7 @@ test('Reply#getSerializationFunction', t => {
231
231
  (req, reply) => {
232
232
  const { id } = req.params
233
233
 
234
- if (parseInt(id) === 1) {
234
+ if (Number(id) === 1) {
235
235
  const serialize4xx = reply.getSerializationFunction('4xx')
236
236
  const serialize201 = reply.getSerializationFunction(201)
237
237
  const serializeUndefined = reply.getSerializationFunction(undefined)
@@ -308,7 +308,7 @@ test('Reply#getSerializationFunction', t => {
308
308
  (req, reply) => {
309
309
  const { id } = req.params
310
310
 
311
- if (parseInt(id) === 1) {
311
+ if (Number(id) === 1) {
312
312
  const serialize = reply.compileSerializationSchema(schemaObj)
313
313
 
314
314
  t.type(serialize, Function)
@@ -352,9 +352,9 @@ test('Reply#getSerializationFunction', t => {
352
352
 
353
353
  fastify.get('/', (req, reply) => {
354
354
  t.notOk(reply.getSerializationFunction(getDefaultSchema()))
355
- t.equal(reply.context[kReplySerializeWeakMap], null)
355
+ t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
356
356
  t.notOk(reply.getSerializationFunction('200'))
357
- t.equal(reply.context[kReplySerializeWeakMap], null)
357
+ t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
358
358
 
359
359
  reply.send({ hello: 'world' })
360
360
  })
@@ -568,9 +568,9 @@ test('Reply#serializeInput', t => {
568
568
 
569
569
  fastify.get('/', (req, reply) => {
570
570
  const input = { hello: 'world' }
571
- t.equal(reply.context[kReplySerializeWeakMap], null)
571
+ t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
572
572
  t.equal(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
573
- t.type(reply.context[kReplySerializeWeakMap], WeakMap)
573
+ t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)
574
574
 
575
575
  reply.send({ hello: 'world' })
576
576
  })
@@ -12,7 +12,8 @@ const {
12
12
  kReplyHeaders,
13
13
  kReplySerializer,
14
14
  kReplyIsError,
15
- kReplySerializerDefault
15
+ kReplySerializerDefault,
16
+ kRouteContext
16
17
  } = require('../../lib/symbols')
17
18
  const fs = require('fs')
18
19
  const path = require('path')
@@ -34,7 +35,7 @@ test('Once called, Reply should return an object with methods', t => {
34
35
  t.plan(13)
35
36
  const response = { res: 'res' }
36
37
  const context = {}
37
- const request = { context }
38
+ const request = { [kRouteContext]: context }
38
39
  const reply = new Reply(response, request)
39
40
  t.equal(typeof reply, 'object')
40
41
  t.equal(typeof reply[kReplyIsError], 'boolean')
@@ -47,7 +48,7 @@ test('Once called, Reply should return an object with methods', t => {
47
48
  t.equal(typeof reply.getResponseTime, 'function')
48
49
  t.equal(typeof reply[kReplyHeaders], 'object')
49
50
  t.same(reply.raw, response)
50
- t.equal(reply.context, context)
51
+ t.equal(reply[kRouteContext], context)
51
52
  t.equal(reply.request, request)
52
53
  })
53
54
 
@@ -76,7 +77,7 @@ test('reply.send will logStream error and destroy the stream', t => {
76
77
  warn: () => {}
77
78
  }
78
79
 
79
- const reply = new Reply(response, { context: { onSend: null } }, log)
80
+ const reply = new Reply(response, { [kRouteContext]: { onSend: null } }, log)
80
81
  reply.send(payload)
81
82
  payload.destroy(new Error('stream error'))
82
83
 
@@ -93,7 +94,7 @@ test('reply.send throw with circular JSON', t => {
93
94
  write: () => {},
94
95
  end: () => {}
95
96
  }
96
- const reply = new Reply(response, { context: { onSend: [] } })
97
+ const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
97
98
  t.throws(() => {
98
99
  const obj = {}
99
100
  obj.obj = obj
@@ -111,7 +112,7 @@ test('reply.send returns itself', t => {
111
112
  write: () => {},
112
113
  end: () => {}
113
114
  }
114
- const reply = new Reply(response, { context: { onSend: [] } })
115
+ const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
115
116
  t.equal(reply.send('hello'), reply)
116
117
  })
117
118
 
@@ -152,7 +153,7 @@ test('reply.serialize should serialize payload', t => {
152
153
  t.plan(1)
153
154
  const response = { statusCode: 200 }
154
155
  const context = {}
155
- const reply = new Reply(response, { context })
156
+ const reply = new Reply(response, { [kRouteContext]: context })
156
157
  t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
157
158
  })
158
159
 
@@ -161,7 +162,7 @@ test('reply.serialize should serialize payload with a custom serializer', t => {
161
162
  let customSerializerCalled = false
162
163
  const response = { statusCode: 200 }
163
164
  const context = {}
164
- const reply = new Reply(response, { context })
165
+ const reply = new Reply(response, { [kRouteContext]: context })
165
166
  reply.serializer((x) => (customSerializerCalled = true) && JSON.stringify(x))
166
167
  t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
167
168
  t.equal(customSerializerCalled, true, 'custom serializer not called')
@@ -172,7 +173,7 @@ test('reply.serialize should serialize payload with a context default serializer
172
173
  let customSerializerCalled = false
173
174
  const response = { statusCode: 200 }
174
175
  const context = { [kReplySerializerDefault]: (x) => (customSerializerCalled = true) && JSON.stringify(x) }
175
- const reply = new Reply(response, { context })
176
+ const reply = new Reply(response, { [kRouteContext]: context })
176
177
  t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')
177
178
  t.equal(customSerializerCalled, true, 'custom serializer not called')
178
179
  })
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { test } = require('tap')
4
4
  const Ajv = require('ajv')
5
- const { kRequestValidateWeakMap } = require('../../lib/symbols')
5
+ const { kRequestValidateWeakMap, kRouteContext } = require('../../lib/symbols')
6
6
  const Fastify = require('../../fastify')
7
7
 
8
8
  const defaultSchema = {
@@ -36,7 +36,7 @@ const requestSchema = {
36
36
  }
37
37
 
38
38
  test('#compileValidationSchema', subtest => {
39
- subtest.plan(5)
39
+ subtest.plan(7)
40
40
 
41
41
  subtest.test('Should return a function - Route without schema', async t => {
42
42
  const fastify = Fastify()
@@ -59,6 +59,49 @@ test('#compileValidationSchema', subtest => {
59
59
  })
60
60
  })
61
61
 
62
+ subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
63
+ const fastify = Fastify()
64
+
65
+ t.plan(3)
66
+
67
+ fastify.get('/', (req, reply) => {
68
+ const validate = req.compileValidationSchema(defaultSchema)
69
+
70
+ t.ok(validate({ hello: 'world' }))
71
+ t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
72
+ t.equal(validate.errors, null)
73
+
74
+ reply.send({ hello: 'world' })
75
+ })
76
+
77
+ await fastify.inject({
78
+ path: '/',
79
+ method: 'GET'
80
+ })
81
+ })
82
+
83
+ subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
84
+ const fastify = Fastify()
85
+
86
+ t.plan(4)
87
+
88
+ fastify.get('/', (req, reply) => {
89
+ const validate = req.compileValidationSchema(defaultSchema)
90
+
91
+ t.notOk(validate({ world: 'foo' }))
92
+ t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
93
+ t.ok(Array.isArray(validate.errors))
94
+ t.ok(validate.errors.length > 0)
95
+
96
+ reply.send({ hello: 'world' })
97
+ })
98
+
99
+ await fastify.inject({
100
+ path: '/',
101
+ method: 'GET'
102
+ })
103
+ })
104
+
62
105
  subtest.test(
63
106
  'Should reuse the validate fn across multiple invocations - Route without schema',
64
107
  async t => {
@@ -188,11 +231,11 @@ test('#compileValidationSchema', subtest => {
188
231
  t.plan(5)
189
232
 
190
233
  fastify.get('/', (req, reply) => {
191
- t.equal(req.context[kRequestValidateWeakMap], null)
234
+ t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
192
235
  t.type(req.compileValidationSchema(defaultSchema), Function)
193
- t.type(req.context[kRequestValidateWeakMap], WeakMap)
236
+ t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
194
237
  t.type(req.compileValidationSchema(Object.assign({}, defaultSchema)), Function)
195
- t.type(req.context[kRequestValidateWeakMap], WeakMap)
238
+ t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
196
239
 
197
240
  reply.send({ hello: 'world' })
198
241
  })
@@ -206,7 +249,7 @@ test('#compileValidationSchema', subtest => {
206
249
  })
207
250
 
208
251
  test('#getValidationFunction', subtest => {
209
- subtest.plan(4)
252
+ subtest.plan(6)
210
253
 
211
254
  subtest.test('Should return a validation function', async t => {
212
255
  const fastify = Fastify()
@@ -228,6 +271,51 @@ test('#getValidationFunction', subtest => {
228
271
  })
229
272
  })
230
273
 
274
+ subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
275
+ const fastify = Fastify()
276
+
277
+ t.plan(3)
278
+
279
+ fastify.get('/', (req, reply) => {
280
+ req.compileValidationSchema(defaultSchema)
281
+ const validate = req.getValidationFunction(defaultSchema)
282
+
283
+ t.ok(validate({ hello: 'world' }))
284
+ t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
285
+ t.equal(validate.errors, null)
286
+
287
+ reply.send({ hello: 'world' })
288
+ })
289
+
290
+ await fastify.inject({
291
+ path: '/',
292
+ method: 'GET'
293
+ })
294
+ })
295
+
296
+ subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
297
+ const fastify = Fastify()
298
+
299
+ t.plan(4)
300
+
301
+ fastify.get('/', (req, reply) => {
302
+ req.compileValidationSchema(defaultSchema)
303
+ const validate = req.getValidationFunction(defaultSchema)
304
+
305
+ t.notOk(validate({ world: 'foo' }))
306
+ t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
307
+ t.ok(Array.isArray(validate.errors))
308
+ t.ok(validate.errors.length > 0)
309
+
310
+ reply.send({ hello: 'world' })
311
+ })
312
+
313
+ await fastify.inject({
314
+ path: '/',
315
+ method: 'GET'
316
+ })
317
+ })
318
+
231
319
  subtest.test('Should return undefined if no schema compiled', async t => {
232
320
  const fastify = Fastify()
233
321
 
@@ -336,7 +424,7 @@ test('#getValidationFunction', subtest => {
336
424
  req.getValidationFunction(defaultSchema)
337
425
  req.getValidationFunction('body')
338
426
 
339
- t.equal(req.context[kRequestValidateWeakMap], null)
427
+ t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
340
428
  reply.send({ hello: 'world' })
341
429
  })
342
430
 
@@ -636,9 +724,9 @@ test('#validate', subtest => {
636
724
  t.plan(3)
637
725
 
638
726
  fastify.get('/', (req, reply) => {
639
- t.equal(req.context[kRequestValidateWeakMap], null)
727
+ t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
640
728
  t.equal(req.validateInput({ hello: 'world' }, defaultSchema), true)
641
- t.type(req.context[kRequestValidateWeakMap], WeakMap)
729
+ t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
642
730
 
643
731
  reply.send({ hello: 'world' })
644
732
  })
@@ -3,6 +3,12 @@
3
3
  const { test } = require('tap')
4
4
 
5
5
  const Request = require('../../lib/request')
6
+ const Context = require('../../lib/context')
7
+ const {
8
+ kPublicRouteContext,
9
+ kReply,
10
+ kRequest
11
+ } = require('../../lib/symbols')
6
12
 
7
13
  process.removeAllListeners('warning')
8
14
 
@@ -16,8 +22,28 @@ test('Regular request', t => {
16
22
  socket: { remoteAddress: 'ip' },
17
23
  headers
18
24
  }
25
+ const context = new Context({
26
+ schema: {
27
+ body: {
28
+ type: 'object',
29
+ required: ['hello'],
30
+ properties: {
31
+ hello: { type: 'string' }
32
+ }
33
+ }
34
+ },
35
+ config: {
36
+ some: 'config',
37
+ url: req.url,
38
+ method: req.method
39
+ },
40
+ server: {
41
+ [kReply]: {},
42
+ [kRequest]: Request
43
+ }
44
+ })
19
45
  req.connection = req.socket
20
- const request = new Request('id', 'params', req, 'query', 'log')
46
+ const request = new Request('id', 'params', req, 'query', 'log', context)
21
47
  t.type(request, Request)
22
48
  t.type(request.validateInput, Function)
23
49
  t.type(request.getValidationFunction, Function)
@@ -36,6 +62,10 @@ test('Regular request', t => {
36
62
  t.equal(request.url, '/')
37
63
  t.equal(request.socket, req.socket)
38
64
  t.equal(request.protocol, 'http')
65
+ t.equal(request.routerPath, context.config.url)
66
+ t.equal(request.routerMethod, context.config.method)
67
+ t.equal(request.routeConfig, context[kPublicRouteContext].config)
68
+ t.equal(request.routeSchema, context[kPublicRouteContext].schema)
39
69
 
40
70
  // This will be removed, it's deprecated
41
71
  t.equal(request.connection, req.connection)
@@ -77,7 +107,7 @@ test('Regular request - host header has precedence over authority', t => {
77
107
  })
78
108
 
79
109
  test('Request with trust proxy', t => {
80
- t.plan(18)
110
+ t.plan(22)
81
111
  const headers = {
82
112
  'x-forwarded-for': '2.2.2.2, 1.1.1.1',
83
113
  'x-forwarded-host': 'example.com'
@@ -88,9 +118,29 @@ test('Request with trust proxy', t => {
88
118
  socket: { remoteAddress: 'ip' },
89
119
  headers
90
120
  }
121
+ const context = new Context({
122
+ schema: {
123
+ body: {
124
+ type: 'object',
125
+ required: ['hello'],
126
+ properties: {
127
+ hello: { type: 'string' }
128
+ }
129
+ }
130
+ },
131
+ config: {
132
+ some: 'config',
133
+ url: req.url,
134
+ method: req.method
135
+ },
136
+ server: {
137
+ [kReply]: {},
138
+ [kRequest]: Request
139
+ }
140
+ })
91
141
 
92
142
  const TpRequest = Request.buildRequest(Request, true)
93
- const request = new TpRequest('id', 'params', req, 'query', 'log')
143
+ const request = new TpRequest('id', 'params', req, 'query', 'log', context)
94
144
  t.type(request, TpRequest)
95
145
  t.equal(request.id, 'id')
96
146
  t.equal(request.params, 'params')
@@ -109,6 +159,10 @@ test('Request with trust proxy', t => {
109
159
  t.type(request.validateInput, Function)
110
160
  t.type(request.getValidationFunction, Function)
111
161
  t.type(request.compileValidationSchema, Function)
162
+ t.equal(request.routerPath, context.config.url)
163
+ t.equal(request.routerMethod, context.config.method)
164
+ t.equal(request.routeConfig, context[kPublicRouteContext].config)
165
+ t.equal(request.routeSchema, context[kPublicRouteContext].schema)
112
166
  })
113
167
 
114
168
  test('Request with trust proxy, encrypted', t => {
@@ -268,6 +268,21 @@ test('build schema - headers are not lowercased in case of custom object', t =>
268
268
  })
269
269
  })
270
270
 
271
+ test('build schema - headers are not lowercased in case of custom validator provided', t => {
272
+ t.plan(1)
273
+
274
+ class Headers {}
275
+ const opts = {
276
+ schema: {
277
+ headers: new Headers()
278
+ }
279
+ }
280
+ validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
281
+ t.type(schema, Headers)
282
+ return () => {}
283
+ }, true)
284
+ })
285
+
271
286
  test('build schema - uppercased headers are not included', t => {
272
287
  t.plan(1)
273
288
  const opts = {
@@ -885,7 +885,7 @@ test('pluginTimeout', t => {
885
885
  })
886
886
  })
887
887
 
888
- test('pluginTimeout - named function', { only: true }, t => {
888
+ test('pluginTimeout - named function', t => {
889
889
  t.plan(5)
890
890
  const fastify = Fastify({
891
891
  pluginTimeout: 10
@@ -0,0 +1,59 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const Fastify = require('..')
6
+
7
+ test('code should handle null/undefined/float', t => {
8
+ t.plan(8)
9
+
10
+ const fastify = Fastify()
11
+
12
+ fastify.get('/null', function (request, reply) {
13
+ reply.status(null).send()
14
+ })
15
+
16
+ fastify.get('/undefined', function (request, reply) {
17
+ reply.status(undefined).send()
18
+ })
19
+
20
+ fastify.get('/404.5', function (request, reply) {
21
+ reply.status(404.5).send()
22
+ })
23
+
24
+ fastify.inject({
25
+ method: 'GET',
26
+ url: '/null'
27
+ }, (error, res) => {
28
+ t.error(error)
29
+ t.equal(res.statusCode, 500)
30
+ t.same(res.json(), {
31
+ statusCode: 500,
32
+ code: 'FST_ERR_BAD_STATUS_CODE',
33
+ error: 'Internal Server Error',
34
+ message: 'Called reply with an invalid status code: null'
35
+ })
36
+ })
37
+
38
+ fastify.inject({
39
+ method: 'GET',
40
+ url: '/undefined'
41
+ }, (error, res) => {
42
+ t.error(error)
43
+ t.equal(res.statusCode, 500)
44
+ t.same(res.json(), {
45
+ statusCode: 500,
46
+ code: 'FST_ERR_BAD_STATUS_CODE',
47
+ error: 'Internal Server Error',
48
+ message: 'Called reply with an invalid status code: undefined'
49
+ })
50
+ })
51
+
52
+ fastify.inject({
53
+ method: 'GET',
54
+ url: '/404.5'
55
+ }, (error, res) => {
56
+ t.error(error)
57
+ t.equal(res.statusCode, 404)
58
+ })
59
+ })
@@ -1464,3 +1464,32 @@ test('invalid url attribute - non string URL', t => {
1464
1464
  t.equal(error.code, FST_ERR_INVALID_URL().code)
1465
1465
  }
1466
1466
  })
1467
+
1468
+ test('exposeHeadRoute should not reuse the same route option', async t => {
1469
+ t.plan(2)
1470
+
1471
+ const fastify = Fastify()
1472
+
1473
+ // we update the onRequest hook in onRoute hook
1474
+ // if we reuse the same route option
1475
+ // that means we will append another function inside the array
1476
+ fastify.addHook('onRoute', function (routeOption) {
1477
+ if (Array.isArray(routeOption.onRequest)) {
1478
+ routeOption.onRequest.push(() => {})
1479
+ } else {
1480
+ routeOption.onRequest = [() => {}]
1481
+ }
1482
+ })
1483
+
1484
+ fastify.addHook('onRoute', function (routeOption) {
1485
+ t.equal(routeOption.onRequest.length, 1)
1486
+ })
1487
+
1488
+ fastify.route({
1489
+ method: 'GET',
1490
+ path: '/more-coffee',
1491
+ async handler () {
1492
+ return 'hello world'
1493
+ }
1494
+ })
1495
+ })