fastify 4.0.0-rc.3 → 4.0.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.
- package/README.md +2 -1
- package/build/build-validation.js +14 -2
- package/docs/Guides/Ecosystem.md +23 -11
- package/docs/Guides/Index.md +1 -1
- package/docs/Guides/Migration-Guide-V3.md +1 -1
- package/docs/Guides/Plugins-Guide.md +3 -3
- package/docs/Guides/Prototype-Poisoning.md +1 -1
- package/docs/Guides/Recommendations.md +2 -2
- package/docs/Guides/Serverless.md +5 -5
- package/docs/Guides/Testing.md +3 -1
- package/docs/Guides/Write-Plugin.md +3 -3
- package/docs/Migration-Guide-V4.md +1 -1
- package/docs/Reference/Logging.md +10 -5
- package/docs/Reference/Middleware.md +3 -3
- package/docs/Reference/Routes.md +2 -2
- package/docs/Reference/Server.md +59 -10
- package/docs/Reference/TypeScript.md +2 -2
- package/docs/Reference/Validation-and-Serialization.md +11 -1
- package/fastify.d.ts +2 -1
- package/fastify.js +38 -14
- package/lib/configValidator.js +456 -332
- package/lib/errors.js +4 -0
- package/lib/request.js +2 -2
- package/lib/route.js +5 -2
- package/lib/schemas.js +3 -0
- package/lib/validation.js +8 -2
- package/package.json +34 -34
- package/test/close-pipelining.test.js +4 -2
- package/test/close.test.js +93 -3
- package/test/custom-http-server.test.js +22 -0
- package/test/decorator.test.js +146 -0
- package/test/internals/initialConfig.test.js +5 -3
- package/test/output-validation.test.js +6 -2
- package/test/plugin.test.js +177 -14
- package/test/route-prefix.test.js +250 -0
- package/test/router-options.test.js +61 -0
- package/test/schema-feature.test.js +24 -0
- package/test/schema-serialization.test.js +57 -0
- package/test/types/fastify.test-d.ts +1 -0
- package/test/types/instance.test-d.ts +1 -0
- package/test/types/logger.test-d.ts +13 -1
- package/test/types/route.test-d.ts +14 -0
- package/test/types/type-provider.test-d.ts +6 -0
- package/types/instance.d.ts +1 -0
- package/types/logger.d.ts +3 -1
- package/types/route.d.ts +1 -1
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +3 -4
package/test/plugin.test.js
CHANGED
|
@@ -184,11 +184,11 @@ test('fastify.register with fastify-plugin should provide access to external fas
|
|
|
184
184
|
})
|
|
185
185
|
})
|
|
186
186
|
|
|
187
|
-
test('fastify.register with fastify-plugin registers
|
|
187
|
+
test('fastify.register with fastify-plugin registers fastify level plugins', t => {
|
|
188
188
|
t.plan(15)
|
|
189
189
|
const fastify = Fastify()
|
|
190
190
|
|
|
191
|
-
function
|
|
191
|
+
function fastifyPlugin (instance, opts, done) {
|
|
192
192
|
instance.decorate('test', 'first')
|
|
193
193
|
t.ok(instance.test)
|
|
194
194
|
done()
|
|
@@ -199,7 +199,7 @@ test('fastify.register with fastify-plugin registers root level plugins', t => {
|
|
|
199
199
|
done()
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
fastify.register(fp(
|
|
202
|
+
fastify.register(fp(fastifyPlugin))
|
|
203
203
|
|
|
204
204
|
fastify.register((instance, opts, done) => {
|
|
205
205
|
t.ok(instance.test)
|
|
@@ -354,27 +354,29 @@ test('check dependencies - should throw', t => {
|
|
|
354
354
|
})
|
|
355
355
|
|
|
356
356
|
test('set the plugin name based on the plugin displayName symbol', t => {
|
|
357
|
-
t.plan(
|
|
357
|
+
t.plan(6)
|
|
358
358
|
const fastify = Fastify()
|
|
359
359
|
|
|
360
360
|
fastify.register(fp((fastify, opts, done) => {
|
|
361
|
-
t.equal(fastify.pluginName, 'plugin-A')
|
|
361
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A')
|
|
362
362
|
fastify.register(fp((fastify, opts, done) => {
|
|
363
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB')
|
|
363
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB')
|
|
364
364
|
done()
|
|
365
365
|
}, { name: 'plugin-AB' }))
|
|
366
366
|
fastify.register(fp((fastify, opts, done) => {
|
|
367
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
|
|
367
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB -> plugin-AC')
|
|
368
368
|
done()
|
|
369
369
|
}, { name: 'plugin-AC' }))
|
|
370
370
|
done()
|
|
371
371
|
}, { name: 'plugin-A' }))
|
|
372
372
|
|
|
373
373
|
fastify.register(fp((fastify, opts, done) => {
|
|
374
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC -> plugin-B')
|
|
374
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB -> plugin-AC -> plugin-B')
|
|
375
375
|
done()
|
|
376
376
|
}, { name: 'plugin-B' }))
|
|
377
377
|
|
|
378
|
+
t.equal(fastify.pluginName, 'fastify')
|
|
379
|
+
|
|
378
380
|
fastify.listen({ port: 0 }, err => {
|
|
379
381
|
t.error(err)
|
|
380
382
|
fastify.close()
|
|
@@ -382,29 +384,31 @@ test('set the plugin name based on the plugin displayName symbol', t => {
|
|
|
382
384
|
})
|
|
383
385
|
|
|
384
386
|
test('plugin name will change when using no encapsulation', t => {
|
|
385
|
-
t.plan(
|
|
387
|
+
t.plan(6)
|
|
386
388
|
const fastify = Fastify()
|
|
387
389
|
|
|
388
390
|
fastify.register(fp((fastify, opts, done) => {
|
|
389
391
|
// store it in a different variable will hold the correct name
|
|
390
392
|
const pluginName = fastify.pluginName
|
|
391
393
|
fastify.register(fp((fastify, opts, done) => {
|
|
392
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB')
|
|
394
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB')
|
|
393
395
|
done()
|
|
394
396
|
}, { name: 'plugin-AB' }))
|
|
395
397
|
fastify.register(fp((fastify, opts, done) => {
|
|
396
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
|
|
398
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB -> plugin-AC')
|
|
397
399
|
done()
|
|
398
400
|
}, { name: 'plugin-AC' }))
|
|
399
401
|
setImmediate(() => {
|
|
400
402
|
// normally we would expect the name plugin-A
|
|
401
403
|
// but we operate on the same instance in each plugin
|
|
402
|
-
t.equal(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
|
|
403
|
-
t.equal(pluginName, 'plugin-A')
|
|
404
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-A -> plugin-AB -> plugin-AC')
|
|
405
|
+
t.equal(pluginName, 'fastify -> plugin-A')
|
|
404
406
|
})
|
|
405
407
|
done()
|
|
406
408
|
}, { name: 'plugin-A' }))
|
|
407
409
|
|
|
410
|
+
t.equal(fastify.pluginName, 'fastify')
|
|
411
|
+
|
|
408
412
|
fastify.listen({ port: 0 }, err => {
|
|
409
413
|
t.error(err)
|
|
410
414
|
fastify.close()
|
|
@@ -415,7 +419,7 @@ test('plugin name is undefined when accessing in no plugin context', t => {
|
|
|
415
419
|
t.plan(2)
|
|
416
420
|
const fastify = Fastify()
|
|
417
421
|
|
|
418
|
-
t.equal(fastify.pluginName,
|
|
422
|
+
t.equal(fastify.pluginName, 'fastify')
|
|
419
423
|
|
|
420
424
|
fastify.listen({ port: 0 }, err => {
|
|
421
425
|
t.error(err)
|
|
@@ -1052,3 +1056,162 @@ test('plugin metadata - release candidate', t => {
|
|
|
1052
1056
|
done()
|
|
1053
1057
|
}
|
|
1054
1058
|
})
|
|
1059
|
+
|
|
1060
|
+
test('hasPlugin method exists as a function', t => {
|
|
1061
|
+
t.plan(1)
|
|
1062
|
+
|
|
1063
|
+
const fastify = Fastify()
|
|
1064
|
+
t.equal(typeof fastify.hasPlugin, 'function')
|
|
1065
|
+
})
|
|
1066
|
+
|
|
1067
|
+
test('hasPlugin returns true if the specified plugin has been registered', async t => {
|
|
1068
|
+
t.plan(4)
|
|
1069
|
+
|
|
1070
|
+
const fastify = Fastify()
|
|
1071
|
+
|
|
1072
|
+
function pluginA (fastify, opts, done) {
|
|
1073
|
+
t.ok(fastify.hasPlugin('plugin-A'))
|
|
1074
|
+
done()
|
|
1075
|
+
}
|
|
1076
|
+
pluginA[Symbol.for('fastify.display-name')] = 'plugin-A'
|
|
1077
|
+
fastify.register(pluginA)
|
|
1078
|
+
|
|
1079
|
+
fastify.register(function pluginB (fastify, opts, done) {
|
|
1080
|
+
t.ok(fastify.hasPlugin('pluginB'))
|
|
1081
|
+
done()
|
|
1082
|
+
})
|
|
1083
|
+
|
|
1084
|
+
fastify.register(function (fastify, opts, done) {
|
|
1085
|
+
// one line
|
|
1086
|
+
t.ok(fastify.hasPlugin('function (fastify, opts, done) { -- // one line'))
|
|
1087
|
+
done()
|
|
1088
|
+
})
|
|
1089
|
+
|
|
1090
|
+
await fastify.ready()
|
|
1091
|
+
|
|
1092
|
+
t.ok(fastify.hasPlugin('fastify'))
|
|
1093
|
+
})
|
|
1094
|
+
|
|
1095
|
+
test('hasPlugin returns false if the specified plugin has not been registered', t => {
|
|
1096
|
+
t.plan(1)
|
|
1097
|
+
|
|
1098
|
+
const fastify = Fastify()
|
|
1099
|
+
t.notOk(fastify.hasPlugin('pluginFoo'))
|
|
1100
|
+
})
|
|
1101
|
+
|
|
1102
|
+
test('hasPlugin returns false when using encapsulation', async t => {
|
|
1103
|
+
t.plan(25)
|
|
1104
|
+
|
|
1105
|
+
const fastify = Fastify()
|
|
1106
|
+
|
|
1107
|
+
fastify.register(function pluginA (fastify, opts, done) {
|
|
1108
|
+
t.ok(fastify.hasPlugin('pluginA'))
|
|
1109
|
+
t.notOk(fastify.hasPlugin('pluginAA'))
|
|
1110
|
+
t.notOk(fastify.hasPlugin('pluginAAA'))
|
|
1111
|
+
t.notOk(fastify.hasPlugin('pluginAB'))
|
|
1112
|
+
t.notOk(fastify.hasPlugin('pluginB'))
|
|
1113
|
+
|
|
1114
|
+
fastify.register(function pluginAA (fastify, opts, done) {
|
|
1115
|
+
t.notOk(fastify.hasPlugin('pluginA'))
|
|
1116
|
+
t.ok(fastify.hasPlugin('pluginAA'))
|
|
1117
|
+
t.notOk(fastify.hasPlugin('pluginAAA'))
|
|
1118
|
+
t.notOk(fastify.hasPlugin('pluginAB'))
|
|
1119
|
+
t.notOk(fastify.hasPlugin('pluginB'))
|
|
1120
|
+
|
|
1121
|
+
fastify.register(function pluginAAA (fastify, opts, done) {
|
|
1122
|
+
t.notOk(fastify.hasPlugin('pluginA'))
|
|
1123
|
+
t.notOk(fastify.hasPlugin('pluginAA'))
|
|
1124
|
+
t.ok(fastify.hasPlugin('pluginAAA'))
|
|
1125
|
+
t.notOk(fastify.hasPlugin('pluginAB'))
|
|
1126
|
+
t.notOk(fastify.hasPlugin('pluginB'))
|
|
1127
|
+
|
|
1128
|
+
done()
|
|
1129
|
+
})
|
|
1130
|
+
|
|
1131
|
+
done()
|
|
1132
|
+
})
|
|
1133
|
+
|
|
1134
|
+
fastify.register(function pluginAB (fastify, opts, done) {
|
|
1135
|
+
t.notOk(fastify.hasPlugin('pluginA'))
|
|
1136
|
+
t.notOk(fastify.hasPlugin('pluginAA'))
|
|
1137
|
+
t.notOk(fastify.hasPlugin('pluginAAA'))
|
|
1138
|
+
t.ok(fastify.hasPlugin('pluginAB'))
|
|
1139
|
+
t.notOk(fastify.hasPlugin('pluginB'))
|
|
1140
|
+
|
|
1141
|
+
done()
|
|
1142
|
+
})
|
|
1143
|
+
|
|
1144
|
+
done()
|
|
1145
|
+
})
|
|
1146
|
+
|
|
1147
|
+
fastify.register(function pluginB (fastify, opts, done) {
|
|
1148
|
+
t.notOk(fastify.hasPlugin('pluginA'))
|
|
1149
|
+
t.notOk(fastify.hasPlugin('pluginAA'))
|
|
1150
|
+
t.notOk(fastify.hasPlugin('pluginAAA'))
|
|
1151
|
+
t.notOk(fastify.hasPlugin('pluginAB'))
|
|
1152
|
+
t.ok(fastify.hasPlugin('pluginB'))
|
|
1153
|
+
|
|
1154
|
+
done()
|
|
1155
|
+
})
|
|
1156
|
+
|
|
1157
|
+
await fastify.ready()
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
test('hasPlugin returns true when using no encapsulation', async t => {
|
|
1161
|
+
t.plan(26)
|
|
1162
|
+
|
|
1163
|
+
const fastify = Fastify()
|
|
1164
|
+
|
|
1165
|
+
fastify.register(fp((fastify, opts, done) => {
|
|
1166
|
+
t.equal(fastify.pluginName, 'fastify -> plugin-AA')
|
|
1167
|
+
t.ok(fastify.hasPlugin('plugin-AA'))
|
|
1168
|
+
t.notOk(fastify.hasPlugin('plugin-A'))
|
|
1169
|
+
t.notOk(fastify.hasPlugin('plugin-AAA'))
|
|
1170
|
+
t.notOk(fastify.hasPlugin('plugin-AB'))
|
|
1171
|
+
t.notOk(fastify.hasPlugin('plugin-B'))
|
|
1172
|
+
|
|
1173
|
+
fastify.register(fp((fastify, opts, done) => {
|
|
1174
|
+
t.ok(fastify.hasPlugin('plugin-AA'))
|
|
1175
|
+
t.ok(fastify.hasPlugin('plugin-A'))
|
|
1176
|
+
t.notOk(fastify.hasPlugin('plugin-AAA'))
|
|
1177
|
+
t.notOk(fastify.hasPlugin('plugin-AB'))
|
|
1178
|
+
t.notOk(fastify.hasPlugin('plugin-B'))
|
|
1179
|
+
|
|
1180
|
+
fastify.register(fp((fastify, opts, done) => {
|
|
1181
|
+
t.ok(fastify.hasPlugin('plugin-AA'))
|
|
1182
|
+
t.ok(fastify.hasPlugin('plugin-A'))
|
|
1183
|
+
t.ok(fastify.hasPlugin('plugin-AAA'))
|
|
1184
|
+
t.notOk(fastify.hasPlugin('plugin-AB'))
|
|
1185
|
+
t.notOk(fastify.hasPlugin('plugin-B'))
|
|
1186
|
+
|
|
1187
|
+
done()
|
|
1188
|
+
}, { name: 'plugin-AAA' }))
|
|
1189
|
+
|
|
1190
|
+
done()
|
|
1191
|
+
}, { name: 'plugin-A' }))
|
|
1192
|
+
|
|
1193
|
+
fastify.register(fp((fastify, opts, done) => {
|
|
1194
|
+
t.ok(fastify.hasPlugin('plugin-AA'))
|
|
1195
|
+
t.ok(fastify.hasPlugin('plugin-A'))
|
|
1196
|
+
t.ok(fastify.hasPlugin('plugin-AAA'))
|
|
1197
|
+
t.ok(fastify.hasPlugin('plugin-AB'))
|
|
1198
|
+
t.notOk(fastify.hasPlugin('plugin-B'))
|
|
1199
|
+
|
|
1200
|
+
done()
|
|
1201
|
+
}, { name: 'plugin-AB' }))
|
|
1202
|
+
|
|
1203
|
+
done()
|
|
1204
|
+
}, { name: 'plugin-AA' }))
|
|
1205
|
+
|
|
1206
|
+
fastify.register(fp((fastify, opts, done) => {
|
|
1207
|
+
t.ok(fastify.hasPlugin('plugin-AA'))
|
|
1208
|
+
t.ok(fastify.hasPlugin('plugin-A'))
|
|
1209
|
+
t.ok(fastify.hasPlugin('plugin-AAA'))
|
|
1210
|
+
t.ok(fastify.hasPlugin('plugin-AB'))
|
|
1211
|
+
t.ok(fastify.hasPlugin('plugin-B'))
|
|
1212
|
+
|
|
1213
|
+
done()
|
|
1214
|
+
}, { name: 'plugin-B' }))
|
|
1215
|
+
|
|
1216
|
+
await fastify.ready()
|
|
1217
|
+
})
|
|
@@ -423,6 +423,37 @@ test('matches both /prefix and /prefix/ with a / route - ignoreTrailingSlash: tr
|
|
|
423
423
|
})
|
|
424
424
|
})
|
|
425
425
|
|
|
426
|
+
test('matches both /prefix and /prefix/ with a / route - ignoreDuplicateSlashes: true', t => {
|
|
427
|
+
t.plan(4)
|
|
428
|
+
const fastify = Fastify({
|
|
429
|
+
ignoreDuplicateSlashes: true
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
fastify.register(function (fastify, opts, done) {
|
|
433
|
+
fastify.get('/', (req, reply) => {
|
|
434
|
+
reply.send({ hello: 'world' })
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
done()
|
|
438
|
+
}, { prefix: '/prefix' })
|
|
439
|
+
|
|
440
|
+
fastify.inject({
|
|
441
|
+
method: 'GET',
|
|
442
|
+
url: '/prefix'
|
|
443
|
+
}, (err, res) => {
|
|
444
|
+
t.error(err)
|
|
445
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
fastify.inject({
|
|
449
|
+
method: 'GET',
|
|
450
|
+
url: '/prefix/'
|
|
451
|
+
}, (err, res) => {
|
|
452
|
+
t.error(err)
|
|
453
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
|
|
426
457
|
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreTrailingSlash: false', t => {
|
|
427
458
|
t.plan(4)
|
|
428
459
|
const fastify = Fastify({
|
|
@@ -459,6 +490,106 @@ test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "
|
|
|
459
490
|
})
|
|
460
491
|
})
|
|
461
492
|
|
|
493
|
+
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreDuplicateSlashes: false', t => {
|
|
494
|
+
t.plan(4)
|
|
495
|
+
const fastify = Fastify({
|
|
496
|
+
ignoreDuplicateSlashes: false
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
fastify.register(function (fastify, opts, done) {
|
|
500
|
+
fastify.route({
|
|
501
|
+
method: 'GET',
|
|
502
|
+
url: '/',
|
|
503
|
+
prefixTrailingSlash: 'both',
|
|
504
|
+
handler: (req, reply) => {
|
|
505
|
+
reply.send({ hello: 'world' })
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
done()
|
|
510
|
+
}, { prefix: '/prefix' })
|
|
511
|
+
|
|
512
|
+
fastify.inject({
|
|
513
|
+
method: 'GET',
|
|
514
|
+
url: '/prefix'
|
|
515
|
+
}, (err, res) => {
|
|
516
|
+
t.error(err)
|
|
517
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
fastify.inject({
|
|
521
|
+
method: 'GET',
|
|
522
|
+
url: '/prefix/'
|
|
523
|
+
}, (err, res) => {
|
|
524
|
+
t.error(err)
|
|
525
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
526
|
+
})
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
test('matches both /prefix and /prefix/ with a / route - ignoreTrailingSlash: true, ignoreDuplicateSlashes: true', t => {
|
|
530
|
+
t.plan(4)
|
|
531
|
+
const fastify = Fastify({
|
|
532
|
+
ignoreTrailingSlash: true,
|
|
533
|
+
ignoreDuplicateSlashes: true
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
fastify.register(function (fastify, opts, done) {
|
|
537
|
+
fastify.get('/', (req, reply) => {
|
|
538
|
+
reply.send({ hello: 'world' })
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
done()
|
|
542
|
+
}, { prefix: '/prefix' })
|
|
543
|
+
|
|
544
|
+
fastify.inject({
|
|
545
|
+
method: 'GET',
|
|
546
|
+
url: '/prefix'
|
|
547
|
+
}, (err, res) => {
|
|
548
|
+
t.error(err)
|
|
549
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
fastify.inject({
|
|
553
|
+
method: 'GET',
|
|
554
|
+
url: '/prefix/'
|
|
555
|
+
}, (err, res) => {
|
|
556
|
+
t.error(err)
|
|
557
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
558
|
+
})
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
test('matches both /prefix and /prefix/ with a / route - ignoreTrailingSlash: true, ignoreDuplicateSlashes: false', t => {
|
|
562
|
+
t.plan(4)
|
|
563
|
+
const fastify = Fastify({
|
|
564
|
+
ignoreTrailingSlash: true,
|
|
565
|
+
ignoreDuplicateSlashes: false
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
fastify.register(function (fastify, opts, done) {
|
|
569
|
+
fastify.get('/', (req, reply) => {
|
|
570
|
+
reply.send({ hello: 'world' })
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
done()
|
|
574
|
+
}, { prefix: '/prefix' })
|
|
575
|
+
|
|
576
|
+
fastify.inject({
|
|
577
|
+
method: 'GET',
|
|
578
|
+
url: '/prefix'
|
|
579
|
+
}, (err, res) => {
|
|
580
|
+
t.error(err)
|
|
581
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
fastify.inject({
|
|
585
|
+
method: 'GET',
|
|
586
|
+
url: '/prefix/'
|
|
587
|
+
}, (err, res) => {
|
|
588
|
+
t.error(err)
|
|
589
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
590
|
+
})
|
|
591
|
+
})
|
|
592
|
+
|
|
462
593
|
test('returns 404 status code with /prefix/ and / route - prefixTrailingSlash: "both" (default), ignoreTrailingSlash: true', t => {
|
|
463
594
|
t.plan(2)
|
|
464
595
|
const fastify = Fastify({
|
|
@@ -490,6 +621,89 @@ test('returns 404 status code with /prefix/ and / route - prefixTrailingSlash: "
|
|
|
490
621
|
})
|
|
491
622
|
})
|
|
492
623
|
|
|
624
|
+
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreDuplicateSlashes: true', t => {
|
|
625
|
+
t.plan(2)
|
|
626
|
+
const fastify = Fastify({
|
|
627
|
+
ignoreDuplicateSlashes: true
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
fastify.register(function (fastify, opts, done) {
|
|
631
|
+
fastify.route({
|
|
632
|
+
method: 'GET',
|
|
633
|
+
url: '/',
|
|
634
|
+
handler: (req, reply) => {
|
|
635
|
+
reply.send({ hello: 'world' })
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
done()
|
|
640
|
+
}, { prefix: '/prefix/' })
|
|
641
|
+
|
|
642
|
+
fastify.inject({
|
|
643
|
+
method: 'GET',
|
|
644
|
+
url: '/prefix//'
|
|
645
|
+
}, (err, res) => {
|
|
646
|
+
t.error(err)
|
|
647
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
648
|
+
})
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreTrailingSlash: true, ignoreDuplicateSlashes: true', t => {
|
|
652
|
+
t.plan(2)
|
|
653
|
+
const fastify = Fastify({
|
|
654
|
+
ignoreTrailingSlash: true,
|
|
655
|
+
ignoreDuplicateSlashes: true
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
fastify.register(function (fastify, opts, done) {
|
|
659
|
+
fastify.route({
|
|
660
|
+
method: 'GET',
|
|
661
|
+
url: '/',
|
|
662
|
+
handler: (req, reply) => {
|
|
663
|
+
reply.send({ hello: 'world' })
|
|
664
|
+
}
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
done()
|
|
668
|
+
}, { prefix: '/prefix/' })
|
|
669
|
+
|
|
670
|
+
fastify.inject({
|
|
671
|
+
method: 'GET',
|
|
672
|
+
url: '/prefix//'
|
|
673
|
+
}, (err, res) => {
|
|
674
|
+
t.error(err)
|
|
675
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
676
|
+
})
|
|
677
|
+
})
|
|
678
|
+
|
|
679
|
+
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreDuplicateSlashes: true', t => {
|
|
680
|
+
t.plan(2)
|
|
681
|
+
const fastify = Fastify({
|
|
682
|
+
ignoreTrailingSlash: true,
|
|
683
|
+
ignoreDuplicateSlashes: true
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
fastify.register(function (fastify, opts, done) {
|
|
687
|
+
fastify.route({
|
|
688
|
+
method: 'GET',
|
|
689
|
+
url: '/',
|
|
690
|
+
handler: (req, reply) => {
|
|
691
|
+
reply.send({ hello: 'world' })
|
|
692
|
+
}
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
done()
|
|
696
|
+
}, { prefix: '/prefix/' })
|
|
697
|
+
|
|
698
|
+
fastify.inject({
|
|
699
|
+
method: 'GET',
|
|
700
|
+
url: '/prefix//'
|
|
701
|
+
}, (err, res) => {
|
|
702
|
+
t.error(err)
|
|
703
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
704
|
+
})
|
|
705
|
+
})
|
|
706
|
+
|
|
493
707
|
test('matches only /prefix with a / route - prefixTrailingSlash: "no-slash", ignoreTrailingSlash: false', t => {
|
|
494
708
|
t.plan(4)
|
|
495
709
|
const fastify = Fastify({
|
|
@@ -526,6 +740,42 @@ test('matches only /prefix with a / route - prefixTrailingSlash: "no-slash", ig
|
|
|
526
740
|
})
|
|
527
741
|
})
|
|
528
742
|
|
|
743
|
+
test('matches only /prefix with a / route - prefixTrailingSlash: "no-slash", ignoreDuplicateSlashes: false', t => {
|
|
744
|
+
t.plan(4)
|
|
745
|
+
const fastify = Fastify({
|
|
746
|
+
ignoreDuplicateSlashes: false
|
|
747
|
+
})
|
|
748
|
+
|
|
749
|
+
fastify.register(function (fastify, opts, done) {
|
|
750
|
+
fastify.route({
|
|
751
|
+
method: 'GET',
|
|
752
|
+
url: '/',
|
|
753
|
+
prefixTrailingSlash: 'no-slash',
|
|
754
|
+
handler: (req, reply) => {
|
|
755
|
+
reply.send({ hello: 'world' })
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
done()
|
|
760
|
+
}, { prefix: '/prefix' })
|
|
761
|
+
|
|
762
|
+
fastify.inject({
|
|
763
|
+
method: 'GET',
|
|
764
|
+
url: '/prefix'
|
|
765
|
+
}, (err, res) => {
|
|
766
|
+
t.error(err)
|
|
767
|
+
t.same(JSON.parse(res.payload), { hello: 'world' })
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
fastify.inject({
|
|
771
|
+
method: 'GET',
|
|
772
|
+
url: '/prefix/'
|
|
773
|
+
}, (err, res) => {
|
|
774
|
+
t.error(err)
|
|
775
|
+
t.equal(JSON.parse(res.payload).statusCode, 404)
|
|
776
|
+
})
|
|
777
|
+
})
|
|
778
|
+
|
|
529
779
|
test('matches only /prefix/ with a / route - prefixTrailingSlash: "slash", ignoreTrailingSlash: false', t => {
|
|
530
780
|
t.plan(4)
|
|
531
781
|
const fastify = Fastify({
|
|
@@ -44,6 +44,67 @@ test('Should honor ignoreTrailingSlash option', t => {
|
|
|
44
44
|
})
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
+
test('Should honor ignoreDuplicateSlashes option', t => {
|
|
48
|
+
t.plan(4)
|
|
49
|
+
const fastify = Fastify({
|
|
50
|
+
ignoreDuplicateSlashes: true
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
fastify.get('/test//test///test', (req, res) => {
|
|
54
|
+
res.send('test')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
fastify.listen({ port: 0 }, (err) => {
|
|
58
|
+
t.teardown(() => { fastify.close() })
|
|
59
|
+
if (err) t.threw(err)
|
|
60
|
+
|
|
61
|
+
const baseUrl = getUrl(fastify)
|
|
62
|
+
|
|
63
|
+
sget.concat(baseUrl + '/test/test/test', (err, res, data) => {
|
|
64
|
+
if (err) t.threw(err)
|
|
65
|
+
t.equal(res.statusCode, 200)
|
|
66
|
+
t.equal(data.toString(), 'test')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
sget.concat(baseUrl + '/test//test///test', (err, res, data) => {
|
|
70
|
+
if (err) t.threw(err)
|
|
71
|
+
t.equal(res.statusCode, 200)
|
|
72
|
+
t.equal(data.toString(), 'test')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('Should honor ignoreTrailingSlash and ignoreDuplicateSlashes options', t => {
|
|
78
|
+
t.plan(4)
|
|
79
|
+
const fastify = Fastify({
|
|
80
|
+
ignoreTrailingSlash: true,
|
|
81
|
+
ignoreDuplicateSlashes: true
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
fastify.get('/test//test///test', (req, res) => {
|
|
85
|
+
res.send('test')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
fastify.listen({ port: 0 }, (err) => {
|
|
89
|
+
t.teardown(() => { fastify.close() })
|
|
90
|
+
if (err) t.threw(err)
|
|
91
|
+
|
|
92
|
+
const baseUrl = getUrl(fastify)
|
|
93
|
+
|
|
94
|
+
sget.concat(baseUrl + '/test/test/test/', (err, res, data) => {
|
|
95
|
+
if (err) t.threw(err)
|
|
96
|
+
t.equal(res.statusCode, 200)
|
|
97
|
+
t.equal(data.toString(), 'test')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
sget.concat(baseUrl + '/test//test///test//', (err, res, data) => {
|
|
101
|
+
if (err) t.threw(err)
|
|
102
|
+
t.equal(res.statusCode, 200)
|
|
103
|
+
t.equal(data.toString(), 'test')
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
47
108
|
test('Should honor maxParamLength option', t => {
|
|
48
109
|
t.plan(4)
|
|
49
110
|
const fastify = Fastify({ maxParamLength: 10 })
|
|
@@ -1755,3 +1755,27 @@ test('Should coerce the array if the default validator is used', async t => {
|
|
|
1755
1755
|
t.error(err)
|
|
1756
1756
|
}
|
|
1757
1757
|
})
|
|
1758
|
+
|
|
1759
|
+
test('Should return a human-friendly error if response status codes are not specified', t => {
|
|
1760
|
+
t.plan(2)
|
|
1761
|
+
const fastify = Fastify()
|
|
1762
|
+
|
|
1763
|
+
fastify.route({
|
|
1764
|
+
url: '/',
|
|
1765
|
+
method: 'GET',
|
|
1766
|
+
schema: {
|
|
1767
|
+
response: {
|
|
1768
|
+
// This should be nested under a status code key, e.g { 200: { type: 'array' } }
|
|
1769
|
+
type: 'array'
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
handler: (req, reply) => {
|
|
1773
|
+
reply.send([])
|
|
1774
|
+
}
|
|
1775
|
+
})
|
|
1776
|
+
|
|
1777
|
+
fastify.ready(err => {
|
|
1778
|
+
t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
|
|
1779
|
+
t.match(err.message, 'Failed building the serialization schema for GET: /, due to error response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }')
|
|
1780
|
+
})
|
|
1781
|
+
})
|
|
@@ -711,3 +711,60 @@ test('Errors in searilizer sended to errorHandler', async t => {
|
|
|
711
711
|
t.ok(savedError.serialization, 'Serialization sign presents')
|
|
712
712
|
t.end()
|
|
713
713
|
})
|
|
714
|
+
|
|
715
|
+
test('capital X', t => {
|
|
716
|
+
t.plan(3)
|
|
717
|
+
|
|
718
|
+
const fastify = Fastify()
|
|
719
|
+
fastify.get('/', {
|
|
720
|
+
schema: {
|
|
721
|
+
response: {
|
|
722
|
+
'2XX': {
|
|
723
|
+
type: 'object',
|
|
724
|
+
properties: {
|
|
725
|
+
name: { type: 'string' },
|
|
726
|
+
work: { type: 'string' }
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}, function (req, reply) {
|
|
732
|
+
reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
fastify.inject('/', (err, res) => {
|
|
736
|
+
t.error(err)
|
|
737
|
+
t.same(res.json(), { name: 'Foo', work: 'Bar' })
|
|
738
|
+
t.equal(res.statusCode, 200)
|
|
739
|
+
})
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
test('allow default as status code and used as last fallback', t => {
|
|
743
|
+
t.plan(3)
|
|
744
|
+
const fastify = Fastify()
|
|
745
|
+
|
|
746
|
+
fastify.route({
|
|
747
|
+
url: '/',
|
|
748
|
+
method: 'GET',
|
|
749
|
+
schema: {
|
|
750
|
+
response: {
|
|
751
|
+
default: {
|
|
752
|
+
type: 'object',
|
|
753
|
+
properties: {
|
|
754
|
+
name: { type: 'string' },
|
|
755
|
+
work: { type: 'string' }
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
handler: (req, reply) => {
|
|
761
|
+
reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
fastify.inject('/', (err, res) => {
|
|
766
|
+
t.error(err)
|
|
767
|
+
t.same(res.json(), { name: 'Foo', work: 'Bar' })
|
|
768
|
+
t.equal(res.statusCode, 200)
|
|
769
|
+
})
|
|
770
|
+
})
|
|
@@ -58,6 +58,7 @@ fastify({ http2: true, https: {} }).inject({}, lightMyRequestCallback)
|
|
|
58
58
|
// server options
|
|
59
59
|
expectAssignable<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true }))
|
|
60
60
|
expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
|
|
61
|
+
expectAssignable<FastifyInstance>(fastify({ ignoreDuplicateSlashes: true }))
|
|
61
62
|
expectAssignable<FastifyInstance>(fastify({ connectionTimeout: 1000 }))
|
|
62
63
|
expectAssignable<FastifyInstance>(fastify({ forceCloseConnections: true }))
|
|
63
64
|
expectAssignable<FastifyInstance>(fastify({ keepAliveTimeout: 1000 }))
|