fastify 3.27.3 → 4.0.0-alpha.2

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 (164) hide show
  1. package/.taprc +3 -0
  2. package/README.md +7 -7
  3. package/build/build-error-serializer.js +27 -0
  4. package/build/build-validation.js +47 -35
  5. package/docs/Guides/Database.md +320 -0
  6. package/docs/Guides/Getting-Started.md +7 -7
  7. package/docs/Guides/Plugins-Guide.md +1 -1
  8. package/docs/Guides/Serverless.md +3 -3
  9. package/docs/Guides/Testing.md +2 -2
  10. package/docs/Migration-Guide-V4.md +12 -0
  11. package/docs/Reference/ContentTypeParser.md +4 -0
  12. package/docs/Reference/Decorators.md +2 -2
  13. package/docs/Reference/Encapsulation.md +2 -2
  14. package/docs/Reference/Errors.md +51 -6
  15. package/docs/Reference/HTTP2.md +3 -3
  16. package/docs/Reference/Hooks.md +4 -7
  17. package/docs/Reference/LTS.md +5 -4
  18. package/docs/Reference/Plugins.md +3 -3
  19. package/docs/Reference/Reply.md +23 -22
  20. package/docs/Reference/Request.md +1 -3
  21. package/docs/Reference/Routes.md +22 -15
  22. package/docs/Reference/Server.md +69 -119
  23. package/docs/Reference/TypeScript.md +20 -22
  24. package/docs/Reference/Validation-and-Serialization.md +30 -55
  25. package/docs/Type-Providers.md +257 -0
  26. package/examples/asyncawait.js +1 -1
  27. package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
  28. package/examples/benchmark/hooks-benchmark.js +1 -1
  29. package/examples/benchmark/simple.js +1 -1
  30. package/examples/hooks.js +2 -2
  31. package/examples/http2.js +1 -1
  32. package/examples/https.js +1 -1
  33. package/examples/parser.js +1 -1
  34. package/examples/route-prefix.js +1 -1
  35. package/examples/shared-schema.js +1 -1
  36. package/examples/simple-stream.js +18 -0
  37. package/examples/simple.js +1 -1
  38. package/examples/simple.mjs +1 -1
  39. package/examples/typescript-server.ts +1 -1
  40. package/examples/use-plugin.js +1 -1
  41. package/fastify.d.ts +34 -22
  42. package/fastify.js +40 -36
  43. package/lib/configValidator.js +902 -1023
  44. package/lib/contentTypeParser.js +6 -16
  45. package/lib/context.js +36 -10
  46. package/lib/decorate.js +3 -1
  47. package/lib/error-handler.js +158 -0
  48. package/lib/error-serializer.js +257 -0
  49. package/lib/errors.js +43 -9
  50. package/lib/fourOhFour.js +31 -20
  51. package/lib/handleRequest.js +10 -13
  52. package/lib/hooks.js +14 -9
  53. package/lib/pluginOverride.js +0 -3
  54. package/lib/pluginUtils.js +3 -2
  55. package/lib/reply.js +29 -158
  56. package/lib/request.js +13 -10
  57. package/lib/route.js +131 -138
  58. package/lib/schema-controller.js +2 -2
  59. package/lib/schemas.js +27 -1
  60. package/lib/server.js +241 -116
  61. package/lib/symbols.js +4 -3
  62. package/lib/validation.js +2 -1
  63. package/lib/warnings.js +4 -12
  64. package/lib/wrapThenable.js +4 -11
  65. package/package.json +37 -39
  66. package/test/404s.test.js +258 -125
  67. package/test/500s.test.js +3 -3
  68. package/test/als.test.js +1 -1
  69. package/test/async-await.test.js +20 -76
  70. package/test/bodyLimit.test.js +1 -1
  71. package/test/build-certificate.js +6 -7
  72. package/test/case-insensitive.test.js +4 -4
  73. package/test/close-pipelining.test.js +2 -2
  74. package/test/close.test.js +11 -11
  75. package/test/content-parser.test.js +32 -0
  76. package/test/context-config.test.js +52 -0
  77. package/test/custom-http-server.test.js +14 -7
  78. package/test/custom-parser-async.test.js +1 -66
  79. package/test/custom-parser.test.js +92 -159
  80. package/test/custom-querystring-parser.test.js +3 -3
  81. package/test/decorator.test.js +11 -13
  82. package/test/delete.test.js +6 -6
  83. package/test/encapsulated-error-handler.test.js +50 -0
  84. package/test/esm/index.test.js +0 -14
  85. package/test/fastify-instance.test.js +4 -4
  86. package/test/fluent-schema.test.js +4 -4
  87. package/test/genReqId.test.js +1 -1
  88. package/test/get.test.js +4 -4
  89. package/test/handler-context.test.js +2 -2
  90. package/test/head.test.js +1 -1
  91. package/test/helper.js +19 -4
  92. package/test/hooks-async.test.js +15 -48
  93. package/test/hooks.on-ready.test.js +10 -5
  94. package/test/hooks.test.js +78 -119
  95. package/test/http2/closing.test.js +10 -16
  96. package/test/http2/constraint.test.js +1 -1
  97. package/test/http2/head.test.js +1 -1
  98. package/test/http2/plain.test.js +1 -1
  99. package/test/http2/secure-with-fallback.test.js +1 -1
  100. package/test/http2/secure.test.js +1 -1
  101. package/test/http2/unknown-http-method.test.js +4 -10
  102. package/test/https/custom-https-server.test.js +12 -6
  103. package/test/https/https.test.js +1 -1
  104. package/test/input-validation.js +3 -3
  105. package/test/internals/handleRequest.test.js +6 -43
  106. package/test/internals/initialConfig.test.js +41 -12
  107. package/test/internals/logger.test.js +2 -2
  108. package/test/internals/reply.test.js +281 -40
  109. package/test/internals/request.test.js +13 -7
  110. package/test/internals/server.test.js +88 -0
  111. package/test/listen.deprecated.test.js +202 -0
  112. package/test/listen.test.js +118 -150
  113. package/test/logger.test.js +82 -42
  114. package/test/maxRequestsPerSocket.test.js +8 -6
  115. package/test/middleware.test.js +2 -25
  116. package/test/nullable-validation.test.js +53 -16
  117. package/test/output-validation.test.js +1 -1
  118. package/test/plugin.test.js +47 -21
  119. package/test/pretty-print.test.js +22 -10
  120. package/test/promises.test.js +1 -1
  121. package/test/proto-poisoning.test.js +6 -6
  122. package/test/register.test.js +3 -3
  123. package/test/reply-error.test.js +126 -15
  124. package/test/request-error.test.js +3 -6
  125. package/test/route-hooks.test.js +18 -18
  126. package/test/route-prefix.test.js +2 -1
  127. package/test/route.test.js +206 -22
  128. package/test/router-options.test.js +2 -2
  129. package/test/schema-examples.test.js +11 -5
  130. package/test/schema-feature.test.js +25 -20
  131. package/test/schema-serialization.test.js +9 -9
  132. package/test/schema-special-usage.test.js +5 -153
  133. package/test/schema-validation.test.js +9 -9
  134. package/test/skip-reply-send.test.js +2 -2
  135. package/test/stream.test.js +82 -23
  136. package/test/throw.test.js +8 -5
  137. package/test/trust-proxy.test.js +6 -6
  138. package/test/type-provider.test.js +20 -0
  139. package/test/types/fastify.test-d.ts +10 -18
  140. package/test/types/import.js +2 -0
  141. package/test/types/import.ts +1 -0
  142. package/test/types/instance.test-d.ts +68 -17
  143. package/test/types/logger.test-d.ts +44 -15
  144. package/test/types/reply.test-d.ts +2 -1
  145. package/test/types/route.test-d.ts +8 -2
  146. package/test/types/schema.test-d.ts +2 -39
  147. package/test/types/type-provider.test-d.ts +417 -0
  148. package/test/url-rewriting.test.js +3 -3
  149. package/test/validation-error-handling.test.js +8 -8
  150. package/test/versioned-routes.test.js +30 -18
  151. package/test/wrapThenable.test.js +7 -6
  152. package/types/content-type-parser.d.ts +17 -8
  153. package/types/hooks.d.ts +102 -59
  154. package/types/instance.d.ts +244 -118
  155. package/types/logger.d.ts +18 -104
  156. package/types/plugin.d.ts +10 -4
  157. package/types/reply.d.ts +18 -12
  158. package/types/request.d.ts +10 -5
  159. package/types/route.d.ts +42 -31
  160. package/types/schema.d.ts +1 -1
  161. package/types/type-provider.d.ts +99 -0
  162. package/types/utils.d.ts +1 -1
  163. package/lib/schema-compilers.js +0 -12
  164. package/test/emit-warning.test.js +0 -166
@@ -7,11 +7,11 @@ const Fastify = require('../')
7
7
 
8
8
  process.removeAllListeners('warning')
9
9
 
10
- function endRouteHook (doneOrPayload, done) {
10
+ function endRouteHook (doneOrPayload, done, doneValue) {
11
11
  if (typeof doneOrPayload === 'function') {
12
- doneOrPayload()
12
+ doneOrPayload(doneValue)
13
13
  } else {
14
- done()
14
+ done(doneValue)
15
15
  }
16
16
  }
17
17
 
@@ -152,9 +152,9 @@ function testBeforeHandlerHook (hook) {
152
152
  const fastify = Fastify()
153
153
 
154
154
  fastify.post('/', {
155
- [hook]: (req, reply, done) => {
155
+ [hook]: (req, reply, doneOrPayload, done) => {
156
156
  req.hello = 'earth'
157
- done()
157
+ endRouteHook(doneOrPayload, done)
158
158
  }
159
159
  }, (req, reply) => {
160
160
  reply.send({ hello: req.hello })
@@ -190,8 +190,8 @@ function testBeforeHandlerHook (hook) {
190
190
  const fastify = Fastify()
191
191
 
192
192
  fastify.post('/', {
193
- [hook]: (req, reply, done) => {
194
- done(new Error('kaboom'))
193
+ [hook]: (req, reply, doneOrPayload, done) => {
194
+ endRouteHook(doneOrPayload, done, new Error('kaboom'))
195
195
  }
196
196
  }, (req, reply) => {
197
197
  reply.send(req.body)
@@ -221,7 +221,7 @@ function testBeforeHandlerHook (hook) {
221
221
 
222
222
  fastify.setErrorHandler(async (error, request, reply) => {
223
223
  t.same(error, myError, 'the error object throws by the user')
224
- reply.send({ this: 'is', my: 'error' })
224
+ return reply.code(500).send({ this: 'is', my: 'error' })
225
225
  })
226
226
 
227
227
  fastify.get('/', {
@@ -271,9 +271,9 @@ function testBeforeHandlerHook (hook) {
271
271
  const fastify = Fastify()
272
272
 
273
273
  fastify.post('/', {
274
- [hook]: (req, reply, done) => {
274
+ [hook]: (req, reply, doneOrPayload, done) => {
275
275
  reply.code(401)
276
- done(new Error('go away'))
276
+ endRouteHook(doneOrPayload, done, new Error('go away'))
277
277
  }
278
278
  }, (req, reply) => {
279
279
  reply.send(req.body)
@@ -302,10 +302,10 @@ function testBeforeHandlerHook (hook) {
302
302
  fastify.decorate('foo', 42)
303
303
 
304
304
  fastify.post('/', {
305
- [hook]: function (req, reply, done) {
305
+ [hook]: function (req, reply, doneOrPayload, done) {
306
306
  t.equal(this.foo, 42)
307
307
  this.foo += 1
308
- done()
308
+ endRouteHook(doneOrPayload, done)
309
309
  }
310
310
  }, function (req, reply) {
311
311
  reply.send({ foo: this.foo })
@@ -329,10 +329,10 @@ function testBeforeHandlerHook (hook) {
329
329
  fastify.decorate('foo', 42)
330
330
 
331
331
  fastify.post('/', {
332
- [hook]: [function (req, reply, done) {
332
+ [hook]: [function (req, reply, doneOrPayload, done) {
333
333
  t.equal(this.foo, 42)
334
334
  this.foo += 1
335
- done()
335
+ endRouteHook(doneOrPayload, done)
336
336
  }]
337
337
  }, function (req, reply) {
338
338
  reply.send({ foo: this.foo })
@@ -423,7 +423,7 @@ test('preParsing option should be called before preValidation hook', t => {
423
423
  })
424
424
 
425
425
  fastify.post('/', {
426
- preParsing: (req, reply, done) => {
426
+ preParsing: (req, reply, payload, done) => {
427
427
  req.called = true
428
428
  done()
429
429
  }
@@ -473,7 +473,7 @@ test('onRequest option should be called before preParsing', t => {
473
473
  t.plan(3)
474
474
  const fastify = Fastify()
475
475
 
476
- fastify.addHook('preParsing', (req, reply, done) => {
476
+ fastify.addHook('preParsing', (req, reply, payload, done) => {
477
477
  t.ok(req.called)
478
478
  done()
479
479
  })
@@ -503,14 +503,14 @@ test('onTimeout on route', t => {
503
503
  const fastify = Fastify({ connectionTimeout: 500 })
504
504
 
505
505
  fastify.get('/timeout', {
506
- async handler (request, reply) { },
506
+ handler (request, reply) { },
507
507
  onTimeout (request, reply, done) {
508
508
  t.pass('onTimeout called')
509
509
  done()
510
510
  }
511
511
  })
512
512
 
513
- fastify.listen(0, (err, address) => {
513
+ fastify.listen({ port: 0 }, (err, address) => {
514
514
  t.error(err)
515
515
  t.teardown(() => fastify.close())
516
516
 
@@ -565,7 +565,8 @@ test('matches only /prefix/ with a / route - prefixTrailingSlash: "slash", igno
565
565
  test('calls onRoute only once when prefixing', async t => {
566
566
  t.plan(1)
567
567
  const fastify = Fastify({
568
- ignoreTrailingSlash: false
568
+ ignoreTrailingSlash: false,
569
+ exposeHeadRoutes: false
569
570
  })
570
571
 
571
572
  let onRouteCalled = 0
@@ -5,11 +5,20 @@ const split = require('split2')
5
5
  const t = require('tap')
6
6
  const test = t.test
7
7
  const sget = require('simple-get').concat
8
- const joi = require('@hapi/joi')
8
+ const joi = require('joi')
9
9
  const Fastify = require('..')
10
10
  const proxyquire = require('proxyquire')
11
11
  const { FST_ERR_INVALID_URL } = require('../lib/errors')
12
12
 
13
+ function getUrl (app) {
14
+ const { address, port } = app.server.address()
15
+ if (address === '::1') {
16
+ return `http://[${address}]:${port}`
17
+ } else {
18
+ return `http://${address}:${port}`
19
+ }
20
+ }
21
+
13
22
  test('route', t => {
14
23
  t.plan(9)
15
24
  const test = t.test
@@ -104,7 +113,7 @@ test('route', t => {
104
113
  }
105
114
  })
106
115
 
107
- fastify.listen(0, function (err) {
116
+ fastify.listen({ port: 0 }, function (err) {
108
117
  if (err) t.error(err)
109
118
  fastify.server.unref()
110
119
 
@@ -203,7 +212,7 @@ test('same route definition object on multiple prefixes', async t => {
203
212
  url: '/simple'
204
213
  }
205
214
 
206
- const fastify = Fastify()
215
+ const fastify = Fastify({ exposeHeadRoutes: false })
207
216
 
208
217
  fastify.register(async function (f) {
209
218
  f.addHook('onRoute', (routeOptions) => {
@@ -536,15 +545,15 @@ test('throws when route with empty url', async t => {
536
545
 
537
546
  const fastify = Fastify()
538
547
  try {
539
- await fastify.route({
548
+ fastify.route({
540
549
  method: 'GET',
541
550
  url: '',
542
551
  handler: (req, res) => {
543
552
  res.send('hi!')
544
553
  }
545
- }).ready()
554
+ })
546
555
  } catch (err) {
547
- t.equal(err.message, 'The first character of a path should be `/` or `*`')
556
+ t.equal(err.message, 'The path could not be empty')
548
557
  }
549
558
  })
550
559
 
@@ -553,10 +562,10 @@ test('throws when route with empty url in shorthand declaration', async t => {
553
562
 
554
563
  const fastify = Fastify()
555
564
  try {
556
- await fastify.get(
565
+ fastify.get(
557
566
  '',
558
567
  async function handler () { return {} }
559
- ).ready()
568
+ )
560
569
  } catch (err) {
561
570
  t.equal(err.message, 'The path could not be empty')
562
571
  }
@@ -581,6 +590,86 @@ test('throws when route-level error handler is not a function', t => {
581
590
  }
582
591
  })
583
592
 
593
+ test('Creates a HEAD route for each GET one (default)', t => {
594
+ t.plan(8)
595
+
596
+ const fastify = Fastify()
597
+
598
+ fastify.route({
599
+ method: 'GET',
600
+ path: '/more-coffee',
601
+ handler: (req, reply) => {
602
+ reply.send({ here: 'is coffee' })
603
+ }
604
+ })
605
+
606
+ fastify.route({
607
+ method: 'GET',
608
+ path: '/some-light',
609
+ handler: (req, reply) => {
610
+ reply.send('Get some light!')
611
+ }
612
+ })
613
+
614
+ fastify.inject({
615
+ method: 'HEAD',
616
+ url: '/more-coffee'
617
+ }, (error, res) => {
618
+ t.error(error)
619
+ t.equal(res.statusCode, 200)
620
+ t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
621
+ t.same(res.body, '')
622
+ })
623
+
624
+ fastify.inject({
625
+ method: 'HEAD',
626
+ url: '/some-light'
627
+ }, (error, res) => {
628
+ t.error(error)
629
+ t.equal(res.statusCode, 200)
630
+ t.equal(res.headers['content-type'], 'text/plain; charset=utf-8')
631
+ t.equal(res.body, '')
632
+ })
633
+ })
634
+
635
+ test('Do not create a HEAD route for each GET one (exposeHeadRoutes: false)', t => {
636
+ t.plan(4)
637
+
638
+ const fastify = Fastify({ exposeHeadRoutes: false })
639
+
640
+ fastify.route({
641
+ method: 'GET',
642
+ path: '/more-coffee',
643
+ handler: (req, reply) => {
644
+ reply.send({ here: 'is coffee' })
645
+ }
646
+ })
647
+
648
+ fastify.route({
649
+ method: 'GET',
650
+ path: '/some-light',
651
+ handler: (req, reply) => {
652
+ reply.send('Get some light!')
653
+ }
654
+ })
655
+
656
+ fastify.inject({
657
+ method: 'HEAD',
658
+ url: '/more-coffee'
659
+ }, (error, res) => {
660
+ t.error(error)
661
+ t.equal(res.statusCode, 404)
662
+ })
663
+
664
+ fastify.inject({
665
+ method: 'HEAD',
666
+ url: '/some-light'
667
+ }, (error, res) => {
668
+ t.error(error)
669
+ t.equal(res.statusCode, 404)
670
+ })
671
+ })
672
+
584
673
  test('Creates a HEAD route for each GET one', t => {
585
674
  t.plan(8)
586
675
 
@@ -811,7 +900,7 @@ test('HEAD route should handle properly each response type', t => {
811
900
  }, (error, res) => {
812
901
  t.error(error)
813
902
  t.equal(res.statusCode, 200)
814
- t.equal(res.headers['content-type'], 'application/octet-stream')
903
+ t.equal(res.headers['content-type'], undefined)
815
904
  t.equal(res.headers['content-length'], undefined)
816
905
  t.equal(res.body, '')
817
906
  })
@@ -916,7 +1005,7 @@ test('no warning for exposeHeadRoute', async t => {
916
1005
 
917
1006
  process.on('warning', listener)
918
1007
 
919
- await fastify.listen(0)
1008
+ await fastify.listen({ port: 0 })
920
1009
 
921
1010
  process.removeListener('warning', listener)
922
1011
 
@@ -959,11 +1048,11 @@ test("HEAD route should handle stream.on('error')", t => {
959
1048
  }, (error, res) => {
960
1049
  t.error(error)
961
1050
  t.equal(res.statusCode, 200)
962
- t.equal(res.headers['content-type'], 'application/octet-stream')
1051
+ t.equal(res.headers['content-type'], undefined)
963
1052
  })
964
1053
  })
965
1054
 
966
- test('HEAD route should not be exposed by default', t => {
1055
+ test('HEAD route should be exposed by default', t => {
967
1056
  t.plan(7)
968
1057
 
969
1058
  const resStream = stream.Readable.from('Hello with error!')
@@ -992,7 +1081,7 @@ test('HEAD route should not be exposed by default', t => {
992
1081
  url: '/without-flag'
993
1082
  }, (error, res) => {
994
1083
  t.error(error)
995
- t.equal(res.statusCode, 404)
1084
+ t.equal(res.statusCode, 200)
996
1085
  })
997
1086
 
998
1087
  fastify.inject({
@@ -1248,26 +1337,121 @@ test('Will not try to re-createprefixed HEAD route if it already exists and expo
1248
1337
  t.ok(true)
1249
1338
  })
1250
1339
 
1251
- test('Correct error message is produced if "path" option is used', t => {
1252
- t.plan(2)
1340
+ test('GET route with body schema should throw', t => {
1341
+ t.plan(1)
1253
1342
 
1254
1343
  const fastify = Fastify()
1255
1344
 
1256
1345
  t.throws(() => {
1257
1346
  fastify.route({
1258
1347
  method: 'GET',
1259
- path: '/test'
1348
+ path: '/get',
1349
+ schema: {
1350
+ body: {}
1351
+ },
1352
+ handler: function (req, reply) {
1353
+ reply.send({ hello: 'world' })
1354
+ }
1260
1355
  })
1261
- }, new Error('Missing handler function for GET:/test route.'))
1356
+ }, new Error('Body validation schema for GET:/get route is not supported!'))
1357
+ })
1358
+
1359
+ test('HEAD route with body schema should throw', t => {
1360
+ t.plan(1)
1361
+
1362
+ const fastify = Fastify()
1262
1363
 
1263
1364
  t.throws(() => {
1264
1365
  fastify.route({
1265
- method: 'POST',
1266
- url: '/test',
1267
- handler: function () {},
1268
- errorHandler: ''
1366
+ method: 'HEAD',
1367
+ path: '/shouldThrow',
1368
+ schema: {
1369
+ body: {}
1370
+ },
1371
+ handler: function (req, reply) {
1372
+ reply.send({ hello: 'world' })
1373
+ }
1374
+ })
1375
+ }, new Error('Body validation schema for HEAD:/shouldThrow route is not supported!'))
1376
+ })
1377
+
1378
+ test('[HEAD, GET] route with body schema should throw', t => {
1379
+ t.plan(1)
1380
+
1381
+ const fastify = Fastify()
1382
+
1383
+ t.throws(() => {
1384
+ fastify.route({
1385
+ method: ['HEAD', 'GET'],
1386
+ path: '/shouldThrowHead',
1387
+ schema: {
1388
+ body: {}
1389
+ },
1390
+ handler: function (req, reply) {
1391
+ reply.send({ hello: 'world' })
1392
+ }
1393
+ })
1394
+ }, new Error('Body validation schema for HEAD:/shouldThrowHead route is not supported!'))
1395
+ })
1396
+
1397
+ test('GET route with body schema should throw - shorthand', t => {
1398
+ t.plan(1)
1399
+
1400
+ const fastify = Fastify()
1401
+
1402
+ t.throws(() => {
1403
+ fastify.get('/shouldThrow', {
1404
+ schema: {
1405
+ body: {}
1406
+ }
1407
+ },
1408
+ function (req, reply) {
1409
+ reply.send({ hello: 'world' })
1410
+ }
1411
+ )
1412
+ }, new Error('Body validation schema for GET:/shouldThrow route is not supported!'))
1413
+ })
1414
+
1415
+ test('HEAD route with body schema should throw - shorthand', t => {
1416
+ t.plan(1)
1417
+
1418
+ const fastify = Fastify()
1419
+
1420
+ t.throws(() => {
1421
+ fastify.head('/shouldThrow2', {
1422
+ schema: {
1423
+ body: {}
1424
+ }
1425
+ },
1426
+ function (req, reply) {
1427
+ reply.send({ hello: 'world' })
1428
+ }
1429
+ )
1430
+ }, new Error('Body validation schema for HEAD:/shouldThrow2 route is not supported!'))
1431
+ })
1432
+
1433
+ test('route with non-english characters', t => {
1434
+ t.plan(4)
1435
+
1436
+ const fastify = Fastify()
1437
+
1438
+ fastify.get('/föö', (request, reply) => {
1439
+ reply.send('here /föö')
1440
+ })
1441
+
1442
+ fastify.listen({ port: 0 }, err => {
1443
+ t.error(err)
1444
+ fastify.server.unref()
1445
+
1446
+ sget({
1447
+ method: 'GET',
1448
+ url: getUrl(fastify) + encodeURI('/föö')
1449
+ }, (err, response, body) => {
1450
+ t.error(err)
1451
+ t.equal(response.statusCode, 200)
1452
+ t.equal(body.toString(), 'here /föö')
1269
1453
  })
1270
- }, new Error('Error Handler for POST:/test route, if defined, must be a function'))
1454
+ })
1271
1455
  })
1272
1456
 
1273
1457
  test('invalid url attribute - non string URL', t => {
@@ -24,7 +24,7 @@ test('Should honor ignoreTrailingSlash option', t => {
24
24
  res.send('test')
25
25
  })
26
26
 
27
- fastify.listen(0, (err) => {
27
+ fastify.listen({ port: 0 }, (err) => {
28
28
  fastify.server.unref()
29
29
  if (err) t.threw(err)
30
30
 
@@ -133,7 +133,7 @@ test('Should honor frameworkErrors option', t => {
133
133
  },
134
134
  (err, res) => {
135
135
  t.error(err)
136
- t.equal(res.body, '\'%world\' is not a valid url component - FST_ERR_BAD_URL')
136
+ t.equal(res.body, '\'/test/%world\' is not a valid url component - FST_ERR_BAD_URL')
137
137
  }
138
138
  )
139
139
  })
@@ -98,7 +98,13 @@ test('Example - get schema encapsulated', async t => {
98
98
 
99
99
  test('Example - validation', t => {
100
100
  t.plan(1)
101
- const fastify = Fastify()
101
+ const fastify = Fastify({
102
+ ajv: {
103
+ customOptions: {
104
+ allowUnionTypes: true
105
+ }
106
+ }
107
+ })
102
108
  const handler = () => { }
103
109
 
104
110
  const bodyJsonSchema = {
@@ -224,7 +230,7 @@ test('Example Joi', t => {
224
230
  const fastify = Fastify()
225
231
  const handler = () => { }
226
232
 
227
- const Joi = require('@hapi/joi')
233
+ const Joi = require('joi')
228
234
  fastify.post('/the/url', {
229
235
  schema: {
230
236
  body: Joi.object().keys({
@@ -431,7 +437,7 @@ test('Example - schemas examples', t => {
431
437
  }
432
438
  }
433
439
 
434
- fastify.get('/', {
440
+ fastify.post('/', {
435
441
  handler,
436
442
  schema: {
437
443
  body: refToId,
@@ -450,7 +456,7 @@ test('should return custom error messages with ajv-errors', t => {
450
456
 
451
457
  const fastify = Fastify({
452
458
  ajv: {
453
- customOptions: { allErrors: true, jsonPointers: true },
459
+ customOptions: { allErrors: true },
454
460
  plugins: [
455
461
  require('ajv-errors')
456
462
  ]
@@ -545,7 +551,7 @@ test('should return localized error messages with ajv-i18n', t => {
545
551
  }, (err, res) => {
546
552
  t.error(err)
547
553
  t.same(JSON.parse(res.payload), [{
548
- dataPath: '',
554
+ instancePath: '',
549
555
  keyword: 'required',
550
556
  message: 'должно иметь обязательное поле work',
551
557
  params: { missingProperty: 'work' },
@@ -3,6 +3,7 @@
3
3
  const { test } = require('tap')
4
4
  const Fastify = require('..')
5
5
  const fp = require('fastify-plugin')
6
+ const deepClone = require('rfdc')({ circles: true, proto: false })
6
7
  const Ajv = require('ajv')
7
8
  const { kSchemaController } = require('../lib/symbols.js')
8
9
 
@@ -22,7 +23,7 @@ const echoBody = (req, reply) => { reply.send(req.body) }
22
23
  t.plan(2)
23
24
  const fastify = Fastify()
24
25
  t.teardown(fastify.close.bind(fastify))
25
- fastify.listen(0, err => {
26
+ fastify.listen({ port: 0 }, err => {
26
27
  t.error(err)
27
28
  try {
28
29
  fastify[f](() => { })
@@ -287,7 +288,7 @@ test('First level $ref', t => {
287
288
 
288
289
  test('Customize validator compiler in instance and route', t => {
289
290
  t.plan(28)
290
- const fastify = Fastify()
291
+ const fastify = Fastify({ exposeHeadRoutes: false })
291
292
 
292
293
  fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
293
294
  t.equal(method, 'POST') // run 4 times
@@ -827,7 +828,7 @@ test('Validation context in validation result', t => {
827
828
  t.equal(err.validationContext, 'body')
828
829
  reply.send()
829
830
  })
830
- fastify.get('/', {
831
+ fastify.post('/', {
831
832
  handler: echoParams,
832
833
  schema: {
833
834
  body: {
@@ -840,7 +841,7 @@ test('Validation context in validation result', t => {
840
841
  }
841
842
  })
842
843
  fastify.inject({
843
- method: 'GET',
844
+ method: 'POST',
844
845
  url: '/',
845
846
  payload: {} // body lacks required field, will fail validation
846
847
  }, (err, res) => {
@@ -881,7 +882,7 @@ test('The schema build should not modify the input', t => {
881
882
  ]
882
883
  })
883
884
 
884
- fastify.get('/', {
885
+ fastify.post('/', {
885
886
  schema: {
886
887
  description: 'get',
887
888
  body: { $ref: 'second#' },
@@ -918,7 +919,11 @@ test('Cross schema reference with encapsulation references', t => {
918
919
  t.plan(1)
919
920
 
920
921
  const fastify = Fastify()
921
- fastify.addSchema({ $id: 'http://foo/item', type: 'object', properties: { foo: { type: 'string' } } })
922
+ fastify.addSchema({
923
+ $id: 'http://foo/item',
924
+ type: 'object',
925
+ properties: { foo: { type: 'string' } }
926
+ })
922
927
 
923
928
  const refItem = { $ref: 'http://foo/item#' }
924
929
 
@@ -949,15 +954,15 @@ test('Cross schema reference with encapsulation references', t => {
949
954
  }
950
955
  }
951
956
 
952
- instance.get('/get', { schema: { response: { 200: multipleRef } } }, () => { })
953
- instance.get('/double-get', { schema: { body: multipleRef, response: { 200: multipleRef } } }, () => { })
957
+ instance.get('/get', { schema: { response: { 200: deepClone(multipleRef) } } }, () => { })
958
+ instance.get('/double-get', { schema: { querystring: multipleRef, response: { 200: multipleRef } } }, () => { })
954
959
  instance.post('/post', { schema: { body: multipleRef, response: { 200: multipleRef } } }, () => { })
955
960
  instance.post('/double', { schema: { response: { 200: { $ref: 'encapsulation' } } } }, () => { })
956
961
  done()
957
962
  }, { prefix: '/foo' })
958
963
 
959
964
  fastify.post('/post', { schema: { body: refItem, response: { 200: refItem } } }, () => { })
960
- fastify.get('/get', { schema: { body: refItem, response: { 200: refItem } } }, () => { })
965
+ fastify.get('/get', { schema: { params: refItem, response: { 200: refItem } } }, () => { })
961
966
 
962
967
  fastify.ready(err => {
963
968
  t.error(err)
@@ -1009,7 +1014,7 @@ test('onReady hook has the compilers ready', t => {
1009
1014
  fastify.get(`/${Math.random()}`, {
1010
1015
  handler: (req, reply) => reply.send(),
1011
1016
  schema: {
1012
- body: { type: 'object' },
1017
+ headers: { type: 'object' },
1013
1018
  response: { 200: { type: 'object' } }
1014
1019
  }
1015
1020
  })
@@ -1099,7 +1104,7 @@ test('Check how many AJV instances are built #2 - verify validatorPool', t => {
1099
1104
  })
1100
1105
 
1101
1106
  function addRandomRoute (server) {
1102
- server.get(`/${Math.random()}`,
1107
+ server.post(`/${Math.random()}`,
1103
1108
  { schema: { body: { type: 'object' } } },
1104
1109
  (req, reply) => reply.send()
1105
1110
  )
@@ -1370,7 +1375,7 @@ test('setSchemaController: Inherits correctly parent schemas with a customized v
1370
1375
  })
1371
1376
  const json = res.json()
1372
1377
 
1373
- t.equal(json.message, 'querystring.msg should be array')
1378
+ t.equal(json.message, 'querystring/msg must be array')
1374
1379
  t.equal(json.statusCode, 400)
1375
1380
  t.equal(res.statusCode, 400, 'Should not coearce the string into array')
1376
1381
  })
@@ -1481,7 +1486,7 @@ test('setSchemaController: Inherits buildSerializer from parent if not present w
1481
1486
  const json = res.json()
1482
1487
 
1483
1488
  t.equal(json.statusCode, 400)
1484
- t.equal(json.message, 'querystring.msg should be array')
1489
+ t.equal(json.message, 'querystring/msg must be array')
1485
1490
  t.equal(rootSerializerCalled, 1, 'Should be called from the child')
1486
1491
  t.equal(rootValidatorCalled, 0, 'Should not be called from the child')
1487
1492
  t.equal(childValidatorCalled, 1, 'Should be called from the child')
@@ -1598,7 +1603,7 @@ test('setSchemaController: Inherits buildValidator from parent if not present wi
1598
1603
  const json = res.json()
1599
1604
 
1600
1605
  t.equal(json.statusCode, 400)
1601
- t.equal(json.message, 'querystring.msg should be array')
1606
+ t.equal(json.message, 'querystring/msg must be array')
1602
1607
  t.equal(rootSerializerCalled, 0, 'Should be called from the child')
1603
1608
  t.equal(rootValidatorCalled, 1, 'Should not be called from the child')
1604
1609
  t.equal(childSerializerCalled, 1, 'Should be called from the child')
@@ -1683,14 +1688,14 @@ test('Should throw if not default validator passed', async t => {
1683
1688
  }
1684
1689
  })
1685
1690
 
1686
- t.equal(res.json().message, 'querystring.msg should be array')
1691
+ t.equal(res.json().message, 'querystring/msg must be array')
1687
1692
  t.equal(res.statusCode, 400, 'Should not coearce the string into array')
1688
1693
  } catch (err) {
1689
1694
  t.error(err)
1690
1695
  }
1691
1696
  })
1692
1697
 
1693
- test('Should throw if not default validator passed', async t => {
1698
+ test('Should coerce the array if the default validator is used', async t => {
1694
1699
  t.plan(2)
1695
1700
  const someSchema = {
1696
1701
  $id: 'some',
@@ -1728,7 +1733,7 @@ test('Should throw if not default validator passed', async t => {
1728
1733
  }
1729
1734
  },
1730
1735
  (req, reply) => {
1731
- reply.send({ noop: 'noop' })
1736
+ reply.send(req.query)
1732
1737
  }
1733
1738
  )
1734
1739
 
@@ -1740,12 +1745,12 @@ test('Should throw if not default validator passed', async t => {
1740
1745
  method: 'POST',
1741
1746
  url: '/',
1742
1747
  query: {
1743
- msg: ['string']
1748
+ msg: 'string'
1744
1749
  }
1745
1750
  })
1746
1751
 
1747
- t.equal(res.json().message, 'querystring.msg should be array')
1748
- t.equal(res.statusCode, 400, 'Should not coearce the string into array')
1752
+ t.equal(res.statusCode, 200)
1753
+ t.same(res.json(), { msg: ['string'] }, 'Should coearce the string into array')
1749
1754
  } catch (err) {
1750
1755
  t.error(err)
1751
1756
  }