fastify 3.26.0 → 4.0.0-alpha.1
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 +5 -4
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +49 -35
- package/docs/Guides/Ecosystem.md +2 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +8 -1
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Reply.md +23 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +17 -10
- package/docs/Reference/Server.md +98 -63
- package/docs/Reference/TypeScript.md +11 -13
- package/docs/Reference/Validation-and-Serialization.md +32 -54
- package/docs/Type-Providers.md +257 -0
- package/examples/hooks.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/fastify.d.ts +36 -22
- package/fastify.js +72 -53
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +5 -3
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +49 -10
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/noop-set.js +10 -0
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +44 -163
- package/lib/request.js +13 -10
- package/lib/route.js +158 -139
- package/lib/schema-controller.js +3 -3
- package/lib/schemas.js +27 -1
- package/lib/server.js +219 -116
- package/lib/symbols.js +6 -4
- package/lib/validation.js +2 -1
- package/lib/warnings.js +2 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +40 -45
- package/test/404s.test.js +265 -108
- package/test/500s.test.js +2 -2
- package/test/async-await.test.js +15 -71
- package/test/close.test.js +39 -1
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +56 -4
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +0 -65
- package/test/custom-parser.test.js +54 -121
- package/test/decorator.test.js +1 -3
- package/test/delete.test.js +5 -5
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/get.test.js +3 -3
- package/test/helper.js +18 -3
- package/test/hooks-async.test.js +14 -47
- package/test/hooks.on-ready.test.js +9 -4
- package/test/hooks.test.js +58 -99
- package/test/http2/closing.test.js +5 -11
- package/test/http2/unknown-http-method.test.js +3 -9
- package/test/https/custom-https-server.test.js +12 -6
- package/test/inject.test.js +1 -1
- package/test/input-validation.js +2 -2
- package/test/internals/all.test.js +2 -2
- package/test/internals/contentTypeParser.test.js +4 -4
- package/test/internals/handleRequest.test.js +9 -46
- package/test/internals/initialConfig.test.js +33 -12
- package/test/internals/logger.test.js +1 -1
- package/test/internals/reply.test.js +245 -3
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.test.js +84 -1
- package/test/logger.test.js +98 -58
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +51 -14
- package/test/plugin.test.js +31 -5
- package/test/pretty-print.test.js +22 -10
- package/test/reply-error.test.js +123 -12
- package/test/request-error.test.js +2 -5
- package/test/route-hooks.test.js +17 -17
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +216 -20
- package/test/router-options.test.js +1 -1
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +24 -19
- package/test/schema-serialization.test.js +50 -9
- package/test/schema-special-usage.test.js +14 -81
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +8 -8
- package/test/stream.test.js +23 -12
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +1 -1
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +12 -18
- package/test/types/hooks.test-d.ts +7 -3
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +61 -15
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +417 -0
- package/test/validation-error-handling.test.js +9 -9
- package/test/versioned-routes.test.js +29 -17
- package/test/wrapThenable.test.js +7 -6
- package/types/.eslintrc.json +1 -1
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +107 -60
- package/types/instance.d.ts +137 -105
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/register.d.ts +1 -1
- package/types/reply.d.ts +16 -11
- package/types/request.d.ts +10 -5
- package/types/route.d.ts +42 -31
- package/types/schema.d.ts +15 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
package/test/route.test.js
CHANGED
|
@@ -5,9 +5,19 @@ 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('
|
|
8
|
+
const joi = require('joi')
|
|
9
9
|
const Fastify = require('..')
|
|
10
10
|
const proxyquire = require('proxyquire')
|
|
11
|
+
const { FST_ERR_INVALID_URL } = require('../lib/errors')
|
|
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
|
+
}
|
|
11
21
|
|
|
12
22
|
test('route', t => {
|
|
13
23
|
t.plan(9)
|
|
@@ -202,7 +212,7 @@ test('same route definition object on multiple prefixes', async t => {
|
|
|
202
212
|
url: '/simple'
|
|
203
213
|
}
|
|
204
214
|
|
|
205
|
-
const fastify = Fastify()
|
|
215
|
+
const fastify = Fastify({ exposeHeadRoutes: false })
|
|
206
216
|
|
|
207
217
|
fastify.register(async function (f) {
|
|
208
218
|
f.addHook('onRoute', (routeOptions) => {
|
|
@@ -535,15 +545,15 @@ test('throws when route with empty url', async t => {
|
|
|
535
545
|
|
|
536
546
|
const fastify = Fastify()
|
|
537
547
|
try {
|
|
538
|
-
|
|
548
|
+
fastify.route({
|
|
539
549
|
method: 'GET',
|
|
540
550
|
url: '',
|
|
541
551
|
handler: (req, res) => {
|
|
542
552
|
res.send('hi!')
|
|
543
553
|
}
|
|
544
|
-
})
|
|
554
|
+
})
|
|
545
555
|
} catch (err) {
|
|
546
|
-
t.equal(err.message, 'The
|
|
556
|
+
t.equal(err.message, 'The path could not be empty')
|
|
547
557
|
}
|
|
548
558
|
})
|
|
549
559
|
|
|
@@ -552,10 +562,10 @@ test('throws when route with empty url in shorthand declaration', async t => {
|
|
|
552
562
|
|
|
553
563
|
const fastify = Fastify()
|
|
554
564
|
try {
|
|
555
|
-
|
|
565
|
+
fastify.get(
|
|
556
566
|
'',
|
|
557
567
|
async function handler () { return {} }
|
|
558
|
-
)
|
|
568
|
+
)
|
|
559
569
|
} catch (err) {
|
|
560
570
|
t.equal(err.message, 'The path could not be empty')
|
|
561
571
|
}
|
|
@@ -580,6 +590,86 @@ test('throws when route-level error handler is not a function', t => {
|
|
|
580
590
|
}
|
|
581
591
|
})
|
|
582
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
|
+
|
|
583
673
|
test('Creates a HEAD route for each GET one', t => {
|
|
584
674
|
t.plan(8)
|
|
585
675
|
|
|
@@ -810,7 +900,7 @@ test('HEAD route should handle properly each response type', t => {
|
|
|
810
900
|
}, (error, res) => {
|
|
811
901
|
t.error(error)
|
|
812
902
|
t.equal(res.statusCode, 200)
|
|
813
|
-
t.equal(res.headers['content-type'],
|
|
903
|
+
t.equal(res.headers['content-type'], undefined)
|
|
814
904
|
t.equal(res.headers['content-length'], undefined)
|
|
815
905
|
t.equal(res.body, '')
|
|
816
906
|
})
|
|
@@ -958,11 +1048,11 @@ test("HEAD route should handle stream.on('error')", t => {
|
|
|
958
1048
|
}, (error, res) => {
|
|
959
1049
|
t.error(error)
|
|
960
1050
|
t.equal(res.statusCode, 200)
|
|
961
|
-
t.equal(res.headers['content-type'],
|
|
1051
|
+
t.equal(res.headers['content-type'], undefined)
|
|
962
1052
|
})
|
|
963
1053
|
})
|
|
964
1054
|
|
|
965
|
-
test('HEAD route should
|
|
1055
|
+
test('HEAD route should be exposed by default', t => {
|
|
966
1056
|
t.plan(7)
|
|
967
1057
|
|
|
968
1058
|
const resStream = stream.Readable.from('Hello with error!')
|
|
@@ -991,7 +1081,7 @@ test('HEAD route should not be exposed by default', t => {
|
|
|
991
1081
|
url: '/without-flag'
|
|
992
1082
|
}, (error, res) => {
|
|
993
1083
|
t.error(error)
|
|
994
|
-
t.equal(res.statusCode,
|
|
1084
|
+
t.equal(res.statusCode, 200)
|
|
995
1085
|
})
|
|
996
1086
|
|
|
997
1087
|
fastify.inject({
|
|
@@ -1247,24 +1337,130 @@ test('Will not try to re-createprefixed HEAD route if it already exists and expo
|
|
|
1247
1337
|
t.ok(true)
|
|
1248
1338
|
})
|
|
1249
1339
|
|
|
1250
|
-
test('
|
|
1251
|
-
t.plan(
|
|
1340
|
+
test('GET route with body schema should throw', t => {
|
|
1341
|
+
t.plan(1)
|
|
1252
1342
|
|
|
1253
1343
|
const fastify = Fastify()
|
|
1254
1344
|
|
|
1255
1345
|
t.throws(() => {
|
|
1256
1346
|
fastify.route({
|
|
1257
1347
|
method: 'GET',
|
|
1258
|
-
path: '/
|
|
1348
|
+
path: '/get',
|
|
1349
|
+
schema: {
|
|
1350
|
+
body: {}
|
|
1351
|
+
},
|
|
1352
|
+
handler: function (req, reply) {
|
|
1353
|
+
reply.send({ hello: 'world' })
|
|
1354
|
+
}
|
|
1355
|
+
})
|
|
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()
|
|
1363
|
+
|
|
1364
|
+
t.throws(() => {
|
|
1365
|
+
fastify.route({
|
|
1366
|
+
method: 'HEAD',
|
|
1367
|
+
path: '/shouldThrow',
|
|
1368
|
+
schema: {
|
|
1369
|
+
body: {}
|
|
1370
|
+
},
|
|
1371
|
+
handler: function (req, reply) {
|
|
1372
|
+
reply.send({ hello: 'world' })
|
|
1373
|
+
}
|
|
1259
1374
|
})
|
|
1260
|
-
}, new Error('
|
|
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()
|
|
1261
1382
|
|
|
1262
1383
|
t.throws(() => {
|
|
1263
1384
|
fastify.route({
|
|
1264
|
-
method: '
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1385
|
+
method: ['HEAD', 'GET'],
|
|
1386
|
+
path: '/shouldThrowHead',
|
|
1387
|
+
schema: {
|
|
1388
|
+
body: {}
|
|
1389
|
+
},
|
|
1390
|
+
handler: function (req, reply) {
|
|
1391
|
+
reply.send({ hello: 'world' })
|
|
1392
|
+
}
|
|
1268
1393
|
})
|
|
1269
|
-
}, new Error('
|
|
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(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öö')
|
|
1453
|
+
})
|
|
1454
|
+
})
|
|
1455
|
+
})
|
|
1456
|
+
|
|
1457
|
+
test('invalid url attribute - non string URL', t => {
|
|
1458
|
+
t.plan(1)
|
|
1459
|
+
const fastify = Fastify()
|
|
1460
|
+
|
|
1461
|
+
try {
|
|
1462
|
+
fastify.get(/^\/(donations|skills|blogs)/, () => {})
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
t.equal(error.code, FST_ERR_INVALID_URL().code)
|
|
1465
|
+
}
|
|
1270
1466
|
})
|
|
@@ -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, '\'
|
|
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('
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -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.
|
|
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: '
|
|
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.
|
|
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({
|
|
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: {
|
|
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: {
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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:
|
|
1748
|
+
msg: 'string'
|
|
1744
1749
|
}
|
|
1745
1750
|
})
|
|
1746
1751
|
|
|
1747
|
-
t.equal(res.
|
|
1748
|
-
t.
|
|
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
|
}
|
|
@@ -163,7 +163,7 @@ test('Use shared schema and $ref with $id in response ($ref to $id)', t => {
|
|
|
163
163
|
t.equal(res.statusCode, 400)
|
|
164
164
|
t.same(res.json(), {
|
|
165
165
|
error: 'Bad Request',
|
|
166
|
-
message: "body
|
|
166
|
+
message: "body must have required property 'address'",
|
|
167
167
|
statusCode: 400
|
|
168
168
|
})
|
|
169
169
|
})
|
|
@@ -236,8 +236,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
|
|
|
236
236
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
237
237
|
title: 'List of Asset locations',
|
|
238
238
|
type: 'array',
|
|
239
|
-
items: { $ref: 'http://example.com/asset.json#' }
|
|
240
|
-
default: []
|
|
239
|
+
items: { $ref: 'http://example.com/asset.json#' }
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
fastify.post('/', {
|
|
@@ -273,7 +272,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
|
|
|
273
272
|
t.equal(res.statusCode, 400)
|
|
274
273
|
t.same(res.json(), {
|
|
275
274
|
error: 'Bad Request',
|
|
276
|
-
message: 'body
|
|
275
|
+
message: 'body/0/location/email must match format "email"',
|
|
277
276
|
statusCode: 400
|
|
278
277
|
})
|
|
279
278
|
})
|
|
@@ -282,7 +281,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
|
|
|
282
281
|
|
|
283
282
|
test('Custom setSerializerCompiler', t => {
|
|
284
283
|
t.plan(7)
|
|
285
|
-
const fastify = Fastify()
|
|
284
|
+
const fastify = Fastify({ exposeHeadRoutes: false })
|
|
286
285
|
|
|
287
286
|
const outSchema = {
|
|
288
287
|
$id: 'test',
|
|
@@ -355,7 +354,6 @@ test('Custom setSerializerCompiler returns bad serialized output', t => {
|
|
|
355
354
|
t.error(err)
|
|
356
355
|
t.equal(res.statusCode, 500)
|
|
357
356
|
t.strictSame(res.json(), {
|
|
358
|
-
error: 'Internal Server Error',
|
|
359
357
|
code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
|
|
360
358
|
message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',
|
|
361
359
|
statusCode: 500
|
|
@@ -410,11 +408,11 @@ test('Custom serializer per route', async t => {
|
|
|
410
408
|
res = await fastify.inject('/route')
|
|
411
409
|
t.equal(res.json().mean, 'route')
|
|
412
410
|
|
|
413
|
-
t.equal(hit,
|
|
411
|
+
t.equal(hit, 4, 'the custom and route serializer has been called')
|
|
414
412
|
})
|
|
415
413
|
|
|
416
414
|
test('Reply serializer win over serializer ', t => {
|
|
417
|
-
t.plan(
|
|
415
|
+
t.plan(6)
|
|
418
416
|
|
|
419
417
|
const fastify = Fastify()
|
|
420
418
|
fastify.setReplySerializer(function (payload, statusCode) {
|
|
@@ -453,7 +451,7 @@ test('Reply serializer win over serializer ', t => {
|
|
|
453
451
|
})
|
|
454
452
|
|
|
455
453
|
test('Reply serializer win over serializer ', t => {
|
|
456
|
-
t.plan(
|
|
454
|
+
t.plan(6)
|
|
457
455
|
|
|
458
456
|
const fastify = Fastify()
|
|
459
457
|
fastify.setReplySerializer(function (payload, statusCode) {
|
|
@@ -570,11 +568,13 @@ test('do not crash if status code serializer errors', async t => {
|
|
|
570
568
|
const fastify = Fastify()
|
|
571
569
|
|
|
572
570
|
const requiresFoo = {
|
|
571
|
+
type: 'object',
|
|
573
572
|
properties: { foo: { type: 'string' } },
|
|
574
573
|
required: ['foo']
|
|
575
574
|
}
|
|
576
575
|
|
|
577
576
|
const someUserErrorType2 = {
|
|
577
|
+
type: 'object',
|
|
578
578
|
properties: {
|
|
579
579
|
code: { type: 'number' }
|
|
580
580
|
},
|
|
@@ -670,3 +670,44 @@ test('error in custom schema serialize compiler, throw FST_ERR_SCH_SERIALIZATION
|
|
|
670
670
|
t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
|
|
671
671
|
})
|
|
672
672
|
})
|
|
673
|
+
|
|
674
|
+
test('Errors in searilizer sended to errorHandler', async t => {
|
|
675
|
+
let savedError
|
|
676
|
+
|
|
677
|
+
const fastify = Fastify()
|
|
678
|
+
fastify.get('/', {
|
|
679
|
+
schema: {
|
|
680
|
+
response: {
|
|
681
|
+
200: {
|
|
682
|
+
type: 'object',
|
|
683
|
+
properties: {
|
|
684
|
+
name: { type: 'string' },
|
|
685
|
+
power: { type: 'string' }
|
|
686
|
+
},
|
|
687
|
+
required: ['name']
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
}, function (req, reply) {
|
|
693
|
+
reply.code(200).send({ no: 'thing' })
|
|
694
|
+
})
|
|
695
|
+
fastify.setErrorHandler((error, request, reply) => {
|
|
696
|
+
savedError = error
|
|
697
|
+
reply.code(500).send(error)
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
const res = await fastify.inject('/')
|
|
701
|
+
|
|
702
|
+
t.equal(res.statusCode, 500)
|
|
703
|
+
|
|
704
|
+
// t.same(savedError, new Error('"name" is required!'));
|
|
705
|
+
t.same(res.json(), {
|
|
706
|
+
statusCode: 500,
|
|
707
|
+
error: 'Internal Server Error',
|
|
708
|
+
message: '"name" is required!'
|
|
709
|
+
})
|
|
710
|
+
t.ok(savedError, 'error presents')
|
|
711
|
+
t.ok(savedError.serialization, 'Serialization sign presents')
|
|
712
|
+
t.end()
|
|
713
|
+
})
|