fastify 2.7.1 → 2.11.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 (72) hide show
  1. package/README.md +15 -4
  2. package/build/build-validation.js +8 -0
  3. package/docs/Benchmarking.md +2 -2
  4. package/docs/ContentTypeParser.md +12 -10
  5. package/docs/Decorators.md +14 -14
  6. package/docs/Ecosystem.md +7 -1
  7. package/docs/Errors.md +13 -8
  8. package/docs/Fluent-Schema.md +9 -12
  9. package/docs/Getting-Started.md +29 -25
  10. package/docs/HTTP2.md +1 -1
  11. package/docs/Hooks.md +201 -186
  12. package/docs/LTS.md +6 -7
  13. package/docs/Logging.md +10 -10
  14. package/docs/Middleware.md +59 -0
  15. package/docs/Plugins-Guide.md +52 -52
  16. package/docs/Plugins.md +3 -0
  17. package/docs/Reply.md +47 -3
  18. package/docs/Routes.md +120 -8
  19. package/docs/Server.md +69 -3
  20. package/docs/Serverless.md +76 -4
  21. package/docs/TypeScript.md +33 -10
  22. package/docs/Validation-and-Serialization.md +137 -1
  23. package/examples/typescript-server.ts +1 -1
  24. package/fastify.d.ts +52 -13
  25. package/fastify.js +68 -7
  26. package/lib/configValidator.js +99 -52
  27. package/lib/contentTypeParser.js +4 -4
  28. package/lib/context.js +2 -1
  29. package/lib/errors.js +21 -18
  30. package/lib/fourOhFour.js +10 -10
  31. package/lib/handleRequest.js +1 -2
  32. package/lib/logger.js +2 -2
  33. package/lib/pluginUtils.js +32 -0
  34. package/lib/reply.js +41 -6
  35. package/lib/route.js +37 -9
  36. package/lib/schemas.js +23 -12
  37. package/lib/symbols.js +4 -1
  38. package/lib/validation.js +15 -9
  39. package/lib/wrapThenable.js +1 -1
  40. package/package.json +34 -26
  41. package/test/404s.test.js +41 -1
  42. package/test/async-await.js +66 -0
  43. package/test/custom-parser.test.js +1 -1
  44. package/test/custom-querystring-parser.test.js +1 -1
  45. package/test/decorator.test.js +48 -0
  46. package/test/emit-warning.test.js +3 -3
  47. package/test/fastify-instance.test.js +29 -0
  48. package/test/helper.js +7 -7
  49. package/test/hooks-async.js +4 -3
  50. package/test/hooks.test.js +27 -8
  51. package/test/input-validation.test.js +126 -0
  52. package/test/internals/errors.test.js +9 -1
  53. package/test/internals/initialConfig.test.js +4 -2
  54. package/test/internals/plugin.test.js +4 -4
  55. package/test/internals/reply.test.js +78 -6
  56. package/test/internals/schemas.test.js +30 -0
  57. package/test/internals/validation.test.js +18 -0
  58. package/test/listen.test.js +1 -1
  59. package/test/logger.test.js +314 -1
  60. package/test/plugin.test.js +171 -0
  61. package/test/promises.test.js +55 -0
  62. package/test/proto-poisoning.test.js +76 -0
  63. package/test/route-hooks.test.js +109 -91
  64. package/test/route-prefix.test.js +1 -1
  65. package/test/schemas.test.js +450 -0
  66. package/test/shared-schemas.test.js +2 -2
  67. package/test/stream.test.js +10 -6
  68. package/test/throw.test.js +48 -2
  69. package/test/types/index.ts +86 -1
  70. package/test/validation-error-handling.test.js +3 -3
  71. package/test/versioned-routes.test.js +1 -1
  72. package/docs/Middlewares.md +0 -59
@@ -567,6 +567,40 @@ test('Should set a custom logLevel for a plugin', t => {
567
567
  })
568
568
  })
569
569
 
570
+ test('Should set a custom logSerializers for a plugin', t => {
571
+ t.plan(3)
572
+
573
+ const splitStream = split(JSON.parse)
574
+ splitStream.on('data', (line) => {
575
+ if (line.test) {
576
+ t.is(line.test, 'XHello')
577
+ }
578
+ })
579
+
580
+ const logger = pino({ level: 'error' }, splitStream)
581
+
582
+ const fastify = Fastify({
583
+ logger
584
+ })
585
+
586
+ fastify.register(function (instance, opts, next) {
587
+ instance.get('/plugin', (req, reply) => {
588
+ req.log.info({ test: 'Hello' }) // we should see this log
589
+ reply.send({ hello: 'world' })
590
+ })
591
+ next()
592
+ }, { logLevel: 'info', logSerializers: { test: value => 'X' + value } })
593
+
594
+ fastify.inject({
595
+ method: 'GET',
596
+ url: '/plugin'
597
+ }, (err, res) => {
598
+ t.error(err)
599
+ const payload = JSON.parse(res.payload)
600
+ t.deepEqual(payload, { hello: 'world' })
601
+ })
602
+ })
603
+
570
604
  test('Should set a custom logLevel for every plugin', t => {
571
605
  const lines = ['incoming request', 'request completed', 'info', 'debug']
572
606
  t.plan(18)
@@ -634,6 +668,281 @@ test('Should set a custom logLevel for every plugin', t => {
634
668
  })
635
669
  })
636
670
 
671
+ test('Should set a custom logSerializers for every plugin', t => {
672
+ const lines = ['Hello', 'XHello', 'ZHello']
673
+ t.plan(9)
674
+
675
+ const splitStream = split(JSON.parse)
676
+ splitStream.on('data', (line) => {
677
+ if (line.test) {
678
+ t.is(line.test, lines.shift())
679
+ }
680
+ })
681
+
682
+ const logger = pino({ level: 'info' }, splitStream)
683
+ const fastify = Fastify({
684
+ logger
685
+ })
686
+
687
+ fastify.get('/', (req, reply) => {
688
+ req.log.warn({ test: 'Hello' })
689
+ reply.send({ hello: 'world' })
690
+ })
691
+
692
+ fastify.register(function (instance, opts, next) {
693
+ instance.get('/test1', (req, reply) => {
694
+ req.log.info({ test: 'Hello' })
695
+ reply.send({ hello: 'world' })
696
+ })
697
+ next()
698
+ }, { logSerializers: { test: value => 'X' + value } })
699
+
700
+ fastify.register(function (instance, opts, next) {
701
+ instance.get('/test2', (req, reply) => {
702
+ req.log.info({ test: 'Hello' })
703
+ reply.send({ hello: 'world' })
704
+ })
705
+ next()
706
+ }, { logSerializers: { test: value => 'Z' + value } })
707
+
708
+ fastify.inject({
709
+ method: 'GET',
710
+ url: '/'
711
+ }, (err, res) => {
712
+ t.error(err)
713
+ const payload = JSON.parse(res.payload)
714
+ t.deepEqual(payload, { hello: 'world' })
715
+ })
716
+
717
+ fastify.inject({
718
+ method: 'GET',
719
+ url: '/test1'
720
+ }, (err, res) => {
721
+ t.error(err)
722
+ const payload = JSON.parse(res.payload)
723
+ t.deepEqual(payload, { hello: 'world' })
724
+ })
725
+
726
+ fastify.inject({
727
+ method: 'GET',
728
+ url: '/test2'
729
+ }, (err, res) => {
730
+ t.error(err)
731
+ const payload = JSON.parse(res.payload)
732
+ t.deepEqual(payload, { hello: 'world' })
733
+ })
734
+ })
735
+
736
+ test('Should override serializers from route', t => {
737
+ t.plan(3)
738
+
739
+ const splitStream = split(JSON.parse)
740
+ splitStream.on('data', (line) => {
741
+ if (line.test) {
742
+ t.is(line.test, 'ZHello')
743
+ }
744
+ })
745
+
746
+ const logger = pino({ level: 'info' }, splitStream)
747
+ const fastify = Fastify({
748
+ logger
749
+ })
750
+
751
+ fastify.register(function (instance, opts, next) {
752
+ instance.get('/', {
753
+ logSerializers: {
754
+ test: value => 'Z' + value // should override
755
+ }
756
+ }, (req, reply) => {
757
+ req.log.info({ test: 'Hello' })
758
+ reply.send({ hello: 'world' })
759
+ })
760
+ next()
761
+ }, { logSerializers: { test: value => 'X' + value } })
762
+
763
+ fastify.inject({
764
+ method: 'GET',
765
+ url: '/'
766
+ }, (err, res) => {
767
+ t.error(err)
768
+ const payload = JSON.parse(res.payload)
769
+ t.deepEqual(payload, { hello: 'world' })
770
+ })
771
+ })
772
+
773
+ test('Should override serializers from plugin', t => {
774
+ t.plan(3)
775
+
776
+ const splitStream = split(JSON.parse)
777
+ splitStream.on('data', (line) => {
778
+ if (line.test) {
779
+ t.is(line.test, 'ZHello')
780
+ }
781
+ })
782
+
783
+ const logger = pino({ level: 'info' }, splitStream)
784
+ const fastify = Fastify({
785
+ logger
786
+ })
787
+
788
+ fastify.register(function (instance, opts, next) {
789
+ instance.register(context1, {
790
+ logSerializers: {
791
+ test: value => 'Z' + value // should override
792
+ }
793
+ })
794
+ next()
795
+ }, { logSerializers: { test: value => 'X' + value } })
796
+
797
+ function context1 (instance, opts, next) {
798
+ instance.get('/', (req, reply) => {
799
+ req.log.info({ test: 'Hello' })
800
+ reply.send({ hello: 'world' })
801
+ })
802
+ next()
803
+ }
804
+
805
+ fastify.inject({
806
+ method: 'GET',
807
+ url: '/'
808
+ }, (err, res) => {
809
+ t.error(err)
810
+ const payload = JSON.parse(res.payload)
811
+ t.deepEqual(payload, { hello: 'world' })
812
+ })
813
+ })
814
+
815
+ test('Should use serializers from plugin and route', t => {
816
+ t.plan(4)
817
+
818
+ const splitStream = split(JSON.parse)
819
+ splitStream.on('data', (line) => {
820
+ if (line.test) {
821
+ t.is(line.test, 'XHello')
822
+ }
823
+ if (line.test2) {
824
+ t.is(line.test2, 'ZHello')
825
+ }
826
+ })
827
+
828
+ const logger = pino({ level: 'info' }, splitStream)
829
+ const fastify = Fastify({
830
+ logger
831
+ })
832
+
833
+ fastify.register(context1, {
834
+ logSerializers: { test: value => 'X' + value }
835
+ })
836
+
837
+ function context1 (instance, opts, next) {
838
+ instance.get('/', {
839
+ logSerializers: {
840
+ test2: value => 'Z' + value
841
+ }
842
+ }, (req, reply) => {
843
+ req.log.info({ test: 'Hello', test2: 'Hello' }) // { test: 'XHello', test2: 'ZHello' }
844
+ reply.send({ hello: 'world' })
845
+ })
846
+ next()
847
+ }
848
+
849
+ fastify.inject({
850
+ method: 'GET',
851
+ url: '/'
852
+ }, (err, res) => {
853
+ t.error(err)
854
+ const payload = JSON.parse(res.payload)
855
+ t.deepEqual(payload, { hello: 'world' })
856
+ })
857
+ })
858
+
859
+ test('Should use serializers from instance fastify and route', t => {
860
+ t.plan(4)
861
+
862
+ const splitStream = split(JSON.parse)
863
+ splitStream.on('data', (line) => {
864
+ if (line.test) {
865
+ t.is(line.test, 'XHello')
866
+ }
867
+ if (line.test2) {
868
+ t.is(line.test2, 'ZHello')
869
+ }
870
+ })
871
+
872
+ const logger = pino({
873
+ level: 'info',
874
+ serializers: {
875
+ test: value => 'X' + value,
876
+ test2: value => 'This should be override - ' + value
877
+ }
878
+ }, splitStream)
879
+ const fastify = Fastify({
880
+ logger
881
+ })
882
+
883
+ fastify.get('/', {
884
+ logSerializers: {
885
+ test2: value => 'Z' + value
886
+ }
887
+ }, (req, reply) => {
888
+ req.log.info({ test: 'Hello', test2: 'Hello' }) // { test: 'XHello', test2: 'ZHello' }
889
+ reply.send({ hello: 'world' })
890
+ })
891
+
892
+ fastify.inject({
893
+ method: 'GET',
894
+ url: '/'
895
+ }, (err, res) => {
896
+ t.error(err)
897
+ const payload = JSON.parse(res.payload)
898
+ t.deepEqual(payload, { hello: 'world' })
899
+ })
900
+ })
901
+
902
+ test('Should use serializers inherit from contexts', t => {
903
+ t.plan(5)
904
+
905
+ const splitStream = split(JSON.parse)
906
+ splitStream.on('data', (line) => {
907
+ if (line.test && line.test2 && line.test3) {
908
+ t.is(line.test, 'XHello')
909
+ t.is(line.test2, 'YHello')
910
+ t.is(line.test3, 'ZHello')
911
+ }
912
+ })
913
+
914
+ const logger = pino({
915
+ level: 'info',
916
+ serializers: {
917
+ test: value => 'X' + value
918
+ }
919
+ }, splitStream)
920
+
921
+ const fastify = Fastify({ logger })
922
+ fastify.register(context1, { logSerializers: { test2: value => 'Y' + value } })
923
+
924
+ function context1 (instance, opts, next) {
925
+ instance.get('/', {
926
+ logSerializers: {
927
+ test3: value => 'Z' + value
928
+ }
929
+ }, (req, reply) => {
930
+ req.log.info({ test: 'Hello', test2: 'Hello', test3: 'Hello' }) // { test: 'XHello', test2: 'YHello', test3: 'ZHello' }
931
+ reply.send({ hello: 'world' })
932
+ })
933
+ next()
934
+ }
935
+
936
+ fastify.inject({
937
+ method: 'GET',
938
+ url: '/'
939
+ }, (err, res) => {
940
+ t.error(err)
941
+ const payload = JSON.parse(res.payload)
942
+ t.deepEqual(payload, { hello: 'world' })
943
+ })
944
+ })
945
+
637
946
  test('Should increase the log level for a specific plugin', t => {
638
947
  t.plan(4)
639
948
 
@@ -783,7 +1092,11 @@ test('Should set a custom log level for a specific route', t => {
783
1092
  test('The default 404 handler logs the incoming request', t => {
784
1093
  t.plan(5)
785
1094
 
786
- const expectedMessages = ['incoming request', 'Not Found', 'request completed']
1095
+ const expectedMessages = [
1096
+ 'incoming request',
1097
+ 'Route GET:/not-found not found',
1098
+ 'request completed'
1099
+ ]
787
1100
 
788
1101
  const splitStream = split(JSON.parse)
789
1102
  splitStream.on('data', (line) => {
@@ -18,6 +18,29 @@ test('require a plugin', t => {
18
18
  })
19
19
  })
20
20
 
21
+ test('plugin metadata - ignore prefix', t => {
22
+ t.plan(2)
23
+ const fastify = Fastify()
24
+
25
+ plugin[Symbol.for('skip-override')] = true
26
+ fastify.register(plugin, { prefix: 'foo' })
27
+
28
+ fastify.inject({
29
+ method: 'GET',
30
+ url: '/'
31
+ }, function (err, res) {
32
+ t.error(err)
33
+ t.equals(res.payload, 'hello')
34
+ })
35
+
36
+ function plugin (instance, opts, next) {
37
+ instance.get('/', function (request, reply) {
38
+ reply.send('hello')
39
+ })
40
+ next()
41
+ }
42
+ })
43
+
21
44
  test('fastify.register with fastify-plugin should not incapsulate his code', t => {
22
45
  t.plan(10)
23
46
  const fastify = Fastify()
@@ -312,6 +335,154 @@ test('check dependencies - should throw', t => {
312
335
  })
313
336
  })
314
337
 
338
+ test('set the plugin name based on the plugin displayName symbol', t => {
339
+ t.plan(5)
340
+ const fastify = Fastify()
341
+
342
+ fastify.register(fp((fastify, opts, next) => {
343
+ t.strictEqual(fastify.pluginName, 'plugin-A')
344
+ fastify.register(fp((fastify, opts, next) => {
345
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB')
346
+ next()
347
+ }, { name: 'plugin-AB' }))
348
+ fastify.register(fp((fastify, opts, next) => {
349
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
350
+ next()
351
+ }, { name: 'plugin-AC' }))
352
+ next()
353
+ }, { name: 'plugin-A' }))
354
+
355
+ fastify.register(fp((fastify, opts, next) => {
356
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC -> plugin-B')
357
+ next()
358
+ }, { name: 'plugin-B' }))
359
+
360
+ fastify.listen(0, err => {
361
+ t.error(err)
362
+ fastify.close()
363
+ })
364
+ })
365
+
366
+ test('plugin name will change when using no encapsulation', t => {
367
+ t.plan(5)
368
+ const fastify = Fastify()
369
+
370
+ fastify.register(fp((fastify, opts, next) => {
371
+ // store it in a different variable will hold the correct name
372
+ const pluginName = fastify.pluginName
373
+ fastify.register(fp((fastify, opts, next) => {
374
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB')
375
+ next()
376
+ }, { name: 'plugin-AB' }))
377
+ fastify.register(fp((fastify, opts, next) => {
378
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
379
+ next()
380
+ }, { name: 'plugin-AC' }))
381
+ setImmediate(() => {
382
+ // normally we would expect the name plugin-A
383
+ // but we operate on the same instance in each plugin
384
+ t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
385
+ t.strictEqual(pluginName, 'plugin-A')
386
+ })
387
+ next()
388
+ }, { name: 'plugin-A' }))
389
+
390
+ fastify.listen(0, err => {
391
+ t.error(err)
392
+ fastify.close()
393
+ })
394
+ })
395
+
396
+ test('plugin name is undefined when accessing in no plugin context', t => {
397
+ t.plan(2)
398
+ const fastify = Fastify()
399
+
400
+ t.strictEqual(fastify.pluginName, undefined)
401
+
402
+ fastify.listen(0, err => {
403
+ t.error(err)
404
+ fastify.close()
405
+ })
406
+ })
407
+
408
+ test('set the plugin name based on the plugin function name', t => {
409
+ t.plan(5)
410
+ const fastify = Fastify()
411
+
412
+ fastify.register(function myPluginA (fastify, opts, next) {
413
+ t.strictEqual(fastify.pluginName, 'myPluginA')
414
+ fastify.register(function myPluginAB (fastify, opts, next) {
415
+ t.strictEqual(fastify.pluginName, 'myPluginAB')
416
+ next()
417
+ })
418
+ setImmediate(() => {
419
+ // exact name due to encapsulation
420
+ t.strictEqual(fastify.pluginName, 'myPluginA')
421
+ })
422
+ next()
423
+ })
424
+
425
+ fastify.register(function myPluginB (fastify, opts, next) {
426
+ t.strictEqual(fastify.pluginName, 'myPluginB')
427
+ next()
428
+ })
429
+
430
+ fastify.listen(0, err => {
431
+ t.error(err)
432
+ fastify.close()
433
+ })
434
+ })
435
+
436
+ test('approximate a plugin name when no meta data is available', t => {
437
+ t.plan(7)
438
+ const fastify = Fastify()
439
+
440
+ fastify.register((fastify, opts, next) => {
441
+ // A
442
+ t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
443
+ t.is(fastify.pluginName.includes('// A'), true)
444
+ fastify.register((fastify, opts, next) => {
445
+ // B
446
+ t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
447
+ t.is(fastify.pluginName.includes('// B'), true)
448
+ next()
449
+ })
450
+ setImmediate(() => {
451
+ t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
452
+ t.is(fastify.pluginName.includes('// A'), true)
453
+ })
454
+ next()
455
+ })
456
+
457
+ fastify.listen(0, err => {
458
+ t.error(err)
459
+ fastify.close()
460
+ })
461
+ })
462
+
463
+ test('approximate a plugin name also when fastify-plugin has no meta data', t => {
464
+ t.plan(4)
465
+ const fastify = Fastify()
466
+
467
+ fastify.register(fp((fastify, opts, next) => {
468
+ t.is(fastify.pluginName, 'plugin.test')
469
+ fastify.register(fp(function B (fastify, opts, next) {
470
+ // function has name
471
+ t.is(fastify.pluginName, 'plugin.test -> B')
472
+ next()
473
+ }))
474
+ setImmediate(() => {
475
+ t.is(fastify.pluginName, 'plugin.test -> B')
476
+ })
477
+ next()
478
+ }))
479
+
480
+ fastify.listen(0, err => {
481
+ t.error(err)
482
+ fastify.close()
483
+ })
484
+ })
485
+
315
486
  test('plugin incapsulation', t => {
316
487
  t.plan(10)
317
488
  const fastify = Fastify()
@@ -42,6 +42,24 @@ fastify.get('/double', function (req, reply) {
42
42
  return Promise.resolve({ hello: '42' })
43
43
  })
44
44
 
45
+ fastify.get('/thenable', opts, function (req, reply) {
46
+ setImmediate(function () {
47
+ reply.send({ hello: 'world' })
48
+ })
49
+ return reply
50
+ })
51
+
52
+ fastify.get('/thenable-error', opts, function (req, reply) {
53
+ setImmediate(function () {
54
+ reply.send(new Error('kaboom'))
55
+ })
56
+ return reply
57
+ })
58
+
59
+ fastify.get('/return-reply', opts, function (req, reply) {
60
+ return reply.send({ hello: 'world' })
61
+ })
62
+
45
63
  fastify.listen(0, err => {
46
64
  t.error(err)
47
65
  fastify.server.unref()
@@ -82,4 +100,41 @@ fastify.listen(0, err => {
82
100
  t.deepEqual(JSON.parse(body), { hello: '42' })
83
101
  })
84
102
  })
103
+
104
+ test('thenable', t => {
105
+ t.plan(4)
106
+ sget({
107
+ method: 'GET',
108
+ url: 'http://localhost:' + fastify.server.address().port + '/thenable'
109
+ }, (err, response, body) => {
110
+ t.error(err)
111
+ t.strictEqual(response.statusCode, 200)
112
+ t.strictEqual(response.headers['content-length'], '' + body.length)
113
+ t.deepEqual(JSON.parse(body), { hello: 'world' })
114
+ })
115
+ })
116
+
117
+ test('thenable (error)', t => {
118
+ t.plan(2)
119
+ sget({
120
+ method: 'GET',
121
+ url: 'http://localhost:' + fastify.server.address().port + '/thenable-error'
122
+ }, (err, response, body) => {
123
+ t.error(err)
124
+ t.strictEqual(response.statusCode, 500)
125
+ })
126
+ })
127
+
128
+ test('return-reply', t => {
129
+ t.plan(4)
130
+ sget({
131
+ method: 'GET',
132
+ url: 'http://localhost:' + fastify.server.address().port + '/return-reply'
133
+ }, (err, response, body) => {
134
+ t.error(err)
135
+ t.strictEqual(response.statusCode, 200)
136
+ t.strictEqual(response.headers['content-length'], '' + body.length)
137
+ t.deepEqual(JSON.parse(body), { hello: 'world' })
138
+ })
139
+ })
85
140
  })
@@ -81,3 +81,79 @@ test('proto-poisoning ignore', t => {
81
81
  })
82
82
  })
83
83
  })
84
+
85
+ test('constructor-poisoning ignore (default in v2)', t => {
86
+ t.plan(3)
87
+
88
+ const fastify = Fastify()
89
+ t.tearDown(fastify.close.bind(fastify))
90
+
91
+ fastify.post('/', (request, reply) => {
92
+ reply.send('ok')
93
+ })
94
+
95
+ fastify.listen(0, function (err) {
96
+ t.error(err)
97
+
98
+ sget({
99
+ method: 'POST',
100
+ url: 'http://localhost:' + fastify.server.address().port,
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
103
+ }, (err, response, body) => {
104
+ t.error(err)
105
+ t.strictEqual(response.statusCode, 200)
106
+ })
107
+ })
108
+ })
109
+
110
+ test('constructor-poisoning error', t => {
111
+ t.plan(3)
112
+
113
+ const fastify = Fastify({ onConstructorPoisoning: 'error' })
114
+ t.tearDown(fastify.close.bind(fastify))
115
+
116
+ fastify.post('/', (request, reply) => {
117
+ t.fail('handler should not be called')
118
+ })
119
+
120
+ fastify.listen(0, function (err) {
121
+ t.error(err)
122
+
123
+ sget({
124
+ method: 'POST',
125
+ url: 'http://localhost:' + fastify.server.address().port,
126
+ headers: { 'Content-Type': 'application/json' },
127
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
128
+ }, (err, response, body) => {
129
+ t.error(err)
130
+ t.strictEqual(response.statusCode, 400)
131
+ })
132
+ })
133
+ })
134
+
135
+ test('constructor-poisoning remove', t => {
136
+ t.plan(4)
137
+
138
+ const fastify = Fastify({ onProtoPoisoning: 'remove' })
139
+ t.tearDown(fastify.close.bind(fastify))
140
+
141
+ fastify.post('/', (request, reply) => {
142
+ t.equal(undefined, Object.assign({}, request.body).foo)
143
+ reply.send({ ok: true })
144
+ })
145
+
146
+ fastify.listen(0, function (err) {
147
+ t.error(err)
148
+
149
+ sget({
150
+ method: 'POST',
151
+ url: 'http://localhost:' + fastify.server.address().port,
152
+ headers: { 'Content-Type': 'application/json' },
153
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
154
+ }, (err, response, body) => {
155
+ t.error(err)
156
+ t.strictEqual(response.statusCode, 200)
157
+ })
158
+ })
159
+ })