fastify 4.25.2 → 4.26.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/.vscode/settings.json +22 -0
- package/EXPENSE_POLICY.md +105 -0
- package/GOVERNANCE.md +2 -103
- package/LICENSE +1 -1
- package/README.md +13 -9
- package/SECURITY.md +2 -157
- package/SPONSORS.md +20 -0
- package/build/build-validation.js +3 -1
- package/docs/Guides/Ecosystem.md +27 -9
- package/docs/Guides/Getting-Started.md +16 -3
- package/docs/Guides/Style-Guide.md +7 -7
- package/docs/Reference/Decorators.md +1 -1
- package/docs/Reference/Errors.md +63 -1
- package/docs/Reference/Hooks.md +1 -1
- package/docs/Reference/Logging.md +3 -3
- package/docs/Reference/Reply.md +70 -1
- package/docs/Reference/Server.md +90 -0
- package/docs/Reference/Warnings.md +2 -0
- package/fastify.d.ts +3 -2
- package/fastify.js +25 -7
- package/lib/configValidator.js +62 -33
- package/lib/contentTypeParser.js +9 -2
- package/lib/error-handler.js +1 -1
- package/lib/error-serializer.js +2 -0
- package/lib/errors.js +4 -0
- package/lib/fourOhFour.js +4 -3
- package/lib/hooks.js +1 -5
- package/lib/reply.js +68 -10
- package/lib/reqIdGenFactory.js +5 -0
- package/lib/route.js +22 -6
- package/lib/schema-controller.js +37 -4
- package/lib/symbols.js +1 -0
- package/lib/warnings.js +6 -0
- package/package.json +17 -5
- package/test/async_hooks.test.js +69 -0
- package/test/findRoute.test.js +135 -0
- package/test/genReqId.test.js +392 -0
- package/test/hooks.on-listen.test.js +66 -14
- package/test/internals/errors.test.js +17 -7
- package/test/internals/initialConfig.test.js +7 -3
- package/test/internals/reply.test.js +80 -5
- package/test/schema-serialization.test.js +41 -0
- package/test/schema-validation.test.js +115 -6
- package/test/serialize-response.test.js +187 -0
- package/test/types/instance.test-d.ts +14 -1
- package/test/types/reply.test-d.ts +4 -2
- package/test/types/route.test-d.ts +15 -1
- package/test/useSemicolonDelimiter.test.js +113 -0
- package/test/web-api.test.js +208 -0
- package/types/instance.d.ts +23 -10
- package/types/reply.d.ts +4 -0
- package/test/types/import.js +0 -2
|
@@ -5,9 +5,7 @@ const Fastify = require('../fastify')
|
|
|
5
5
|
const fp = require('fastify-plugin')
|
|
6
6
|
const split = require('split2')
|
|
7
7
|
const helper = require('./helper')
|
|
8
|
-
|
|
9
|
-
// fix citgm @aix72-ppc64
|
|
10
|
-
const LISTEN_READYNESS = process.env.CITGM ? 250 : 50
|
|
8
|
+
const { kState } = require('../lib/symbols')
|
|
11
9
|
|
|
12
10
|
let localhost
|
|
13
11
|
before(async function () {
|
|
@@ -274,8 +272,8 @@ test('localhost Register onListen hook after a plugin inside a plugin should log
|
|
|
274
272
|
})
|
|
275
273
|
})
|
|
276
274
|
|
|
277
|
-
test('localhost onListen encapsulation should be called in order', t => {
|
|
278
|
-
t.plan(
|
|
275
|
+
test('localhost onListen encapsulation should be called in order', async t => {
|
|
276
|
+
t.plan(8)
|
|
279
277
|
const fastify = Fastify()
|
|
280
278
|
t.teardown(fastify.close.bind(fastify))
|
|
281
279
|
|
|
@@ -287,20 +285,75 @@ test('localhost onListen encapsulation should be called in order', t => {
|
|
|
287
285
|
done()
|
|
288
286
|
})
|
|
289
287
|
|
|
290
|
-
fastify.register(async (childOne, o) => {
|
|
288
|
+
await fastify.register(async (childOne, o) => {
|
|
291
289
|
childOne.addHook('onListen', function (done) {
|
|
292
290
|
t.equal(++order, 2, 'called in childOne')
|
|
293
291
|
t.equal(this.pluginName, childOne.pluginName, 'the this binding is the right instance')
|
|
294
292
|
done()
|
|
295
293
|
})
|
|
296
|
-
|
|
294
|
+
|
|
295
|
+
await childOne.register(async (childTwo, o) => {
|
|
297
296
|
childTwo.addHook('onListen', async function () {
|
|
298
297
|
t.equal(++order, 3, 'called in childTwo')
|
|
299
298
|
t.equal(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')
|
|
300
299
|
})
|
|
301
300
|
})
|
|
301
|
+
|
|
302
|
+
await childOne.register(async (childTwoPeer, o) => {
|
|
303
|
+
childTwoPeer.addHook('onListen', async function () {
|
|
304
|
+
t.equal(++order, 4, 'called second in childTwo')
|
|
305
|
+
t.equal(this.pluginName, childTwoPeer.pluginName, 'the this binding is the right instance')
|
|
306
|
+
})
|
|
307
|
+
})
|
|
302
308
|
})
|
|
303
|
-
fastify.listen({
|
|
309
|
+
await fastify.listen({
|
|
310
|
+
host: 'localhost',
|
|
311
|
+
port: 0
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
test('localhost onListen encapsulation with only nested hook', async t => {
|
|
316
|
+
t.plan(1)
|
|
317
|
+
const fastify = Fastify()
|
|
318
|
+
t.teardown(fastify.close.bind(fastify))
|
|
319
|
+
|
|
320
|
+
await fastify.register(async (child) => {
|
|
321
|
+
await child.register(async (child2) => {
|
|
322
|
+
child2.addHook('onListen', function (done) {
|
|
323
|
+
t.pass()
|
|
324
|
+
done()
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
await fastify.listen({
|
|
330
|
+
host: 'localhost',
|
|
331
|
+
port: 0
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
test('localhost onListen peer encapsulations with only nested hooks', async t => {
|
|
336
|
+
t.plan(2)
|
|
337
|
+
const fastify = Fastify()
|
|
338
|
+
t.teardown(fastify.close.bind(fastify))
|
|
339
|
+
|
|
340
|
+
await fastify.register(async (child) => {
|
|
341
|
+
await child.register(async (child2) => {
|
|
342
|
+
child2.addHook('onListen', function (done) {
|
|
343
|
+
t.pass()
|
|
344
|
+
done()
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
await child.register(async (child2) => {
|
|
349
|
+
child2.addHook('onListen', function (done) {
|
|
350
|
+
t.pass()
|
|
351
|
+
done()
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
await fastify.listen({
|
|
304
357
|
host: 'localhost',
|
|
305
358
|
port: 0
|
|
306
359
|
})
|
|
@@ -1056,36 +1109,35 @@ test('async onListen does not need to be awaited', t => {
|
|
|
1056
1109
|
|
|
1057
1110
|
test('onListen hooks do not block /1', t => {
|
|
1058
1111
|
t.plan(2)
|
|
1112
|
+
|
|
1059
1113
|
const fastify = Fastify()
|
|
1060
1114
|
t.teardown(fastify.close.bind(fastify))
|
|
1061
1115
|
|
|
1062
1116
|
fastify.addHook('onListen', function (done) {
|
|
1063
|
-
|
|
1117
|
+
t.equal(fastify[kState].listening, true)
|
|
1118
|
+
done()
|
|
1064
1119
|
})
|
|
1065
1120
|
|
|
1066
|
-
const startDate = new Date()
|
|
1067
1121
|
fastify.listen({
|
|
1068
1122
|
host: 'localhost',
|
|
1069
1123
|
port: 0
|
|
1070
1124
|
}, err => {
|
|
1071
1125
|
t.error(err)
|
|
1072
|
-
t.ok(new Date() - startDate < LISTEN_READYNESS)
|
|
1073
1126
|
})
|
|
1074
1127
|
})
|
|
1075
1128
|
|
|
1076
1129
|
test('onListen hooks do not block /2', async t => {
|
|
1077
1130
|
t.plan(1)
|
|
1131
|
+
|
|
1078
1132
|
const fastify = Fastify()
|
|
1079
1133
|
t.teardown(fastify.close.bind(fastify))
|
|
1080
1134
|
|
|
1081
1135
|
fastify.addHook('onListen', async function () {
|
|
1082
|
-
|
|
1136
|
+
t.equal(fastify[kState].listening, true)
|
|
1083
1137
|
})
|
|
1084
1138
|
|
|
1085
|
-
const startDate = new Date()
|
|
1086
1139
|
await fastify.listen({
|
|
1087
1140
|
host: 'localhost',
|
|
1088
1141
|
port: 0
|
|
1089
1142
|
})
|
|
1090
|
-
t.ok(new Date() - startDate < LISTEN_READYNESS)
|
|
1091
1143
|
})
|
|
@@ -5,7 +5,7 @@ const errors = require('../../lib/errors')
|
|
|
5
5
|
const { readFileSync } = require('node:fs')
|
|
6
6
|
const { resolve } = require('node:path')
|
|
7
7
|
|
|
8
|
-
test('should expose
|
|
8
|
+
test('should expose 79 errors', t => {
|
|
9
9
|
t.plan(1)
|
|
10
10
|
const exportedKeys = Object.keys(errors)
|
|
11
11
|
let counter = 0
|
|
@@ -14,11 +14,11 @@ test('should expose 78 errors', t => {
|
|
|
14
14
|
counter++
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
t.equal(counter,
|
|
17
|
+
t.equal(counter, 79)
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
test('ensure name and codes of Errors are identical', t => {
|
|
21
|
-
t.plan(
|
|
21
|
+
t.plan(79)
|
|
22
22
|
const exportedKeys = Object.keys(errors)
|
|
23
23
|
for (const key of exportedKeys) {
|
|
24
24
|
if (errors[key].name === 'FastifyError') {
|
|
@@ -337,6 +337,16 @@ test('FST_ERR_REP_INVALID_PAYLOAD_TYPE', t => {
|
|
|
337
337
|
t.ok(error instanceof TypeError)
|
|
338
338
|
})
|
|
339
339
|
|
|
340
|
+
test('FST_ERR_REP_RESPONSE_BODY_CONSUMED', t => {
|
|
341
|
+
t.plan(5)
|
|
342
|
+
const error = new errors.FST_ERR_REP_RESPONSE_BODY_CONSUMED()
|
|
343
|
+
t.equal(error.name, 'FastifyError')
|
|
344
|
+
t.equal(error.code, 'FST_ERR_REP_RESPONSE_BODY_CONSUMED')
|
|
345
|
+
t.equal(error.message, 'Response.body is already consumed.')
|
|
346
|
+
t.equal(error.statusCode, 500)
|
|
347
|
+
t.ok(error instanceof Error)
|
|
348
|
+
})
|
|
349
|
+
|
|
340
350
|
test('FST_ERR_REP_ALREADY_SENT', t => {
|
|
341
351
|
t.plan(5)
|
|
342
352
|
const error = new errors.FST_ERR_REP_ALREADY_SENT('/hello', 'GET')
|
|
@@ -818,7 +828,7 @@ test('FST_ERR_LISTEN_OPTIONS_INVALID', t => {
|
|
|
818
828
|
})
|
|
819
829
|
|
|
820
830
|
test('Ensure that all errors are in Errors.md TOC', t => {
|
|
821
|
-
t.plan(
|
|
831
|
+
t.plan(79)
|
|
822
832
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
823
833
|
|
|
824
834
|
const exportedKeys = Object.keys(errors)
|
|
@@ -830,7 +840,7 @@ test('Ensure that all errors are in Errors.md TOC', t => {
|
|
|
830
840
|
})
|
|
831
841
|
|
|
832
842
|
test('Ensure that non-existing errors are not in Errors.md TOC', t => {
|
|
833
|
-
t.plan(
|
|
843
|
+
t.plan(79)
|
|
834
844
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
835
845
|
|
|
836
846
|
const matchRE = / {4}- \[([A-Z0-9_]+)\]\(#[a-z0-9_]+\)/g
|
|
@@ -843,7 +853,7 @@ test('Ensure that non-existing errors are not in Errors.md TOC', t => {
|
|
|
843
853
|
})
|
|
844
854
|
|
|
845
855
|
test('Ensure that all errors are in Errors.md documented', t => {
|
|
846
|
-
t.plan(
|
|
856
|
+
t.plan(79)
|
|
847
857
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
848
858
|
|
|
849
859
|
const exportedKeys = Object.keys(errors)
|
|
@@ -855,7 +865,7 @@ test('Ensure that all errors are in Errors.md documented', t => {
|
|
|
855
865
|
})
|
|
856
866
|
|
|
857
867
|
test('Ensure that non-existing errors are not in Errors.md documented', t => {
|
|
858
|
-
t.plan(
|
|
868
|
+
t.plan(79)
|
|
859
869
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
860
870
|
|
|
861
871
|
const matchRE = /<a id="[0-9a-zA-Z_]+">([0-9a-zA-Z_]+)<\/a>/g
|
|
@@ -48,14 +48,15 @@ test('without options passed to Fastify, initialConfig should expose default val
|
|
|
48
48
|
requestIdHeader: 'request-id',
|
|
49
49
|
requestIdLogLabel: 'reqId',
|
|
50
50
|
http2SessionTimeout: 72000,
|
|
51
|
-
exposeHeadRoutes: true
|
|
51
|
+
exposeHeadRoutes: true,
|
|
52
|
+
useSemicolonDelimiter: true
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
t.same(Fastify().initialConfig, fastifyDefaultOptions)
|
|
55
56
|
})
|
|
56
57
|
|
|
57
58
|
test('Fastify.initialConfig should expose all options', t => {
|
|
58
|
-
t.plan(
|
|
59
|
+
t.plan(22)
|
|
59
60
|
|
|
60
61
|
const serverFactory = (handler, opts) => {
|
|
61
62
|
const server = http.createServer((req, res) => {
|
|
@@ -99,6 +100,7 @@ test('Fastify.initialConfig should expose all options', t => {
|
|
|
99
100
|
allowUnsafeRegex: false,
|
|
100
101
|
requestIdHeader: 'request-id-alt',
|
|
101
102
|
pluginTimeout: 20000,
|
|
103
|
+
useSemicolonDelimiter: false,
|
|
102
104
|
querystringParser: str => str,
|
|
103
105
|
genReqId: function (req) {
|
|
104
106
|
return reqId++
|
|
@@ -123,6 +125,7 @@ test('Fastify.initialConfig should expose all options', t => {
|
|
|
123
125
|
t.equal(fastify.initialConfig.bodyLimit, 1049600)
|
|
124
126
|
t.equal(fastify.initialConfig.onProtoPoisoning, 'remove')
|
|
125
127
|
t.equal(fastify.initialConfig.caseSensitive, true)
|
|
128
|
+
t.equal(fastify.initialConfig.useSemicolonDelimiter, false)
|
|
126
129
|
t.equal(fastify.initialConfig.allowUnsafeRegex, false)
|
|
127
130
|
t.equal(fastify.initialConfig.requestIdHeader, 'request-id-alt')
|
|
128
131
|
t.equal(fastify.initialConfig.pluginTimeout, 20000)
|
|
@@ -285,7 +288,8 @@ test('Should not have issues when passing stream options to Pino.js', t => {
|
|
|
285
288
|
requestIdHeader: 'request-id',
|
|
286
289
|
requestIdLogLabel: 'reqId',
|
|
287
290
|
http2SessionTimeout: 72000,
|
|
288
|
-
exposeHeadRoutes: true
|
|
291
|
+
exposeHeadRoutes: true,
|
|
292
|
+
useSemicolonDelimiter: true
|
|
289
293
|
})
|
|
290
294
|
} catch (error) {
|
|
291
295
|
t.fail()
|
|
@@ -19,7 +19,7 @@ const {
|
|
|
19
19
|
} = require('../../lib/symbols')
|
|
20
20
|
const fs = require('node:fs')
|
|
21
21
|
const path = require('node:path')
|
|
22
|
-
const { FSTDEP019,
|
|
22
|
+
const { FSTDEP010, FSTDEP019, FSTDEP020 } = require('../../lib/warnings')
|
|
23
23
|
|
|
24
24
|
const agent = new http.Agent({ keepAlive: false })
|
|
25
25
|
|
|
@@ -1598,7 +1598,40 @@ test('reply.getResponseTime() should return a number greater than 0 after the ti
|
|
|
1598
1598
|
fastify.inject({ method: 'GET', url: '/' })
|
|
1599
1599
|
})
|
|
1600
1600
|
|
|
1601
|
-
test('reply.getResponseTime() should return the time since a request started while inflight', t => {
|
|
1601
|
+
test('should emit deprecation warning when trying to use reply.getResponseTime() and should return the time since a request started while inflight', t => {
|
|
1602
|
+
t.plan(5)
|
|
1603
|
+
const fastify = Fastify()
|
|
1604
|
+
fastify.route({
|
|
1605
|
+
method: 'GET',
|
|
1606
|
+
url: '/',
|
|
1607
|
+
handler: (req, reply) => {
|
|
1608
|
+
reply.send('hello world')
|
|
1609
|
+
}
|
|
1610
|
+
})
|
|
1611
|
+
|
|
1612
|
+
process.removeAllListeners('warning')
|
|
1613
|
+
process.on('warning', onWarning)
|
|
1614
|
+
function onWarning (warning) {
|
|
1615
|
+
t.equal(warning.name, 'DeprecationWarning')
|
|
1616
|
+
t.equal(warning.code, FSTDEP020.code)
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
fastify.addHook('preValidation', (req, reply, done) => {
|
|
1620
|
+
t.equal(reply.getResponseTime(), reply.getResponseTime())
|
|
1621
|
+
done()
|
|
1622
|
+
})
|
|
1623
|
+
|
|
1624
|
+
fastify.inject({ method: 'GET', url: '/' }, (err, res) => {
|
|
1625
|
+
t.error(err)
|
|
1626
|
+
t.pass()
|
|
1627
|
+
|
|
1628
|
+
process.removeListener('warning', onWarning)
|
|
1629
|
+
})
|
|
1630
|
+
|
|
1631
|
+
FSTDEP020.emitted = false
|
|
1632
|
+
})
|
|
1633
|
+
|
|
1634
|
+
test('reply.getResponseTime() should return the same value after a request is finished', t => {
|
|
1602
1635
|
t.plan(1)
|
|
1603
1636
|
const fastify = Fastify()
|
|
1604
1637
|
fastify.route({
|
|
@@ -1609,19 +1642,61 @@ test('reply.getResponseTime() should return the time since a request started whi
|
|
|
1609
1642
|
}
|
|
1610
1643
|
})
|
|
1611
1644
|
|
|
1645
|
+
fastify.addHook('onResponse', (req, reply) => {
|
|
1646
|
+
t.equal(reply.getResponseTime(), reply.getResponseTime())
|
|
1647
|
+
t.end()
|
|
1648
|
+
})
|
|
1649
|
+
|
|
1650
|
+
fastify.inject({ method: 'GET', url: '/' })
|
|
1651
|
+
})
|
|
1652
|
+
|
|
1653
|
+
test('reply.elapsedTime should return a number greater than 0 after the timer is initialised on the reply by setting up response listeners', t => {
|
|
1654
|
+
t.plan(1)
|
|
1655
|
+
const fastify = Fastify()
|
|
1656
|
+
fastify.route({
|
|
1657
|
+
method: 'GET',
|
|
1658
|
+
url: '/',
|
|
1659
|
+
handler: (req, reply) => {
|
|
1660
|
+
reply.send('hello world')
|
|
1661
|
+
}
|
|
1662
|
+
})
|
|
1663
|
+
|
|
1664
|
+
fastify.addHook('onResponse', (req, reply) => {
|
|
1665
|
+
t.ok(reply.elapsedTime > 0)
|
|
1666
|
+
t.end()
|
|
1667
|
+
})
|
|
1668
|
+
|
|
1669
|
+
fastify.inject({ method: 'GET', url: '/' })
|
|
1670
|
+
})
|
|
1671
|
+
|
|
1672
|
+
test('reply.elapsedTime should return the time since a request started while inflight', t => {
|
|
1673
|
+
t.plan(1)
|
|
1674
|
+
const fastify = Fastify()
|
|
1675
|
+
fastify.route({
|
|
1676
|
+
method: 'GET',
|
|
1677
|
+
url: '/',
|
|
1678
|
+
handler: (req, reply) => {
|
|
1679
|
+
reply.send('hello world')
|
|
1680
|
+
}
|
|
1681
|
+
})
|
|
1682
|
+
|
|
1683
|
+
let preValidationElapsedTime
|
|
1684
|
+
|
|
1612
1685
|
fastify.addHook('preValidation', (req, reply, done) => {
|
|
1613
|
-
|
|
1686
|
+
preValidationElapsedTime = reply.elapsedTime
|
|
1687
|
+
|
|
1614
1688
|
done()
|
|
1615
1689
|
})
|
|
1616
1690
|
|
|
1617
1691
|
fastify.addHook('onResponse', (req, reply) => {
|
|
1692
|
+
t.ok(reply.elapsedTime > preValidationElapsedTime)
|
|
1618
1693
|
t.end()
|
|
1619
1694
|
})
|
|
1620
1695
|
|
|
1621
1696
|
fastify.inject({ method: 'GET', url: '/' })
|
|
1622
1697
|
})
|
|
1623
1698
|
|
|
1624
|
-
test('reply.
|
|
1699
|
+
test('reply.elapsedTime should return the same value after a request is finished', t => {
|
|
1625
1700
|
t.plan(1)
|
|
1626
1701
|
const fastify = Fastify()
|
|
1627
1702
|
fastify.route({
|
|
@@ -1633,7 +1708,7 @@ test('reply.getResponseTime() should return the same value after a request is fi
|
|
|
1633
1708
|
})
|
|
1634
1709
|
|
|
1635
1710
|
fastify.addHook('onResponse', (req, reply) => {
|
|
1636
|
-
t.equal(reply.
|
|
1711
|
+
t.equal(reply.elapsedTime, reply.elapsedTime)
|
|
1637
1712
|
t.end()
|
|
1638
1713
|
})
|
|
1639
1714
|
|
|
@@ -593,6 +593,47 @@ test('Custom setSerializerCompiler returns bad serialized output', t => {
|
|
|
593
593
|
})
|
|
594
594
|
})
|
|
595
595
|
|
|
596
|
+
test('Custom setSerializerCompiler with addSchema', t => {
|
|
597
|
+
t.plan(6)
|
|
598
|
+
const fastify = Fastify({ exposeHeadRoutes: false })
|
|
599
|
+
|
|
600
|
+
const outSchema = {
|
|
601
|
+
$id: 'test',
|
|
602
|
+
type: 'object',
|
|
603
|
+
whatever: 'need to be parsed by the custom serializer'
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {
|
|
607
|
+
t.equal(method, 'GET')
|
|
608
|
+
t.equal(url, '/foo/:id')
|
|
609
|
+
t.equal(httpStatus, '200')
|
|
610
|
+
t.same(schema, outSchema)
|
|
611
|
+
return _data => JSON.stringify({ id: 2 })
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
// provoke re-creation of serialization compiler in setupSerializer
|
|
615
|
+
fastify.addSchema({ $id: 'dummy', type: 'object' })
|
|
616
|
+
|
|
617
|
+
fastify.get('/foo/:id', {
|
|
618
|
+
handler (_req, reply) {
|
|
619
|
+
reply.send({ id: 1 })
|
|
620
|
+
},
|
|
621
|
+
schema: {
|
|
622
|
+
response: {
|
|
623
|
+
200: outSchema
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
fastify.inject({
|
|
629
|
+
method: 'GET',
|
|
630
|
+
url: '/foo/123'
|
|
631
|
+
}, (err, res) => {
|
|
632
|
+
t.error(err)
|
|
633
|
+
t.equal(res.payload, JSON.stringify({ id: 2 }))
|
|
634
|
+
})
|
|
635
|
+
})
|
|
636
|
+
|
|
596
637
|
test('Custom serializer per route', async t => {
|
|
597
638
|
const fastify = Fastify()
|
|
598
639
|
|
|
@@ -106,7 +106,7 @@ test('Basic validation test', t => {
|
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
test('External AJV instance', t => {
|
|
109
|
-
t.plan(
|
|
109
|
+
t.plan(5)
|
|
110
110
|
|
|
111
111
|
const fastify = Fastify()
|
|
112
112
|
const ajv = new AJV()
|
|
@@ -118,6 +118,7 @@ test('External AJV instance', t => {
|
|
|
118
118
|
fastify.addSchema(schemaBRefToA)
|
|
119
119
|
|
|
120
120
|
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
|
|
121
|
+
t.pass('custom validator compiler called')
|
|
121
122
|
return ajv.compile(schema)
|
|
122
123
|
})
|
|
123
124
|
|
|
@@ -151,7 +152,7 @@ test('External AJV instance', t => {
|
|
|
151
152
|
})
|
|
152
153
|
|
|
153
154
|
test('Encapsulation', t => {
|
|
154
|
-
t.plan(
|
|
155
|
+
t.plan(21)
|
|
155
156
|
|
|
156
157
|
const fastify = Fastify()
|
|
157
158
|
const ajv = new AJV()
|
|
@@ -164,6 +165,7 @@ test('Encapsulation', t => {
|
|
|
164
165
|
|
|
165
166
|
fastify.register((instance, opts, done) => {
|
|
166
167
|
const validator = ({ schema, method, url, httpPart }) => {
|
|
168
|
+
t.pass('custom validator compiler called')
|
|
167
169
|
return ajv.compile(schema)
|
|
168
170
|
}
|
|
169
171
|
instance.setValidatorCompiler(validator)
|
|
@@ -271,7 +273,7 @@ test('Encapsulation', t => {
|
|
|
271
273
|
})
|
|
272
274
|
|
|
273
275
|
test('Triple $ref with a simple $id', t => {
|
|
274
|
-
t.plan(
|
|
276
|
+
t.plan(7)
|
|
275
277
|
|
|
276
278
|
const fastify = Fastify()
|
|
277
279
|
const ajv = new AJV()
|
|
@@ -285,6 +287,7 @@ test('Triple $ref with a simple $id', t => {
|
|
|
285
287
|
fastify.addSchema(schemaCRefToB)
|
|
286
288
|
|
|
287
289
|
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
|
|
290
|
+
t.pass('custom validator compiler called')
|
|
288
291
|
return ajv.compile(schema)
|
|
289
292
|
})
|
|
290
293
|
|
|
@@ -1024,19 +1027,125 @@ test("The same $id in route's schema must not overwrite others", t => {
|
|
|
1024
1027
|
|
|
1025
1028
|
test('Custom validator compiler should not mutate schema', async t => {
|
|
1026
1029
|
t.plan(2)
|
|
1027
|
-
class Headers {}
|
|
1030
|
+
class Headers { }
|
|
1028
1031
|
const fastify = Fastify()
|
|
1029
1032
|
|
|
1030
1033
|
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
|
|
1031
1034
|
t.type(schema, Headers)
|
|
1032
|
-
return () => {}
|
|
1035
|
+
return () => { }
|
|
1033
1036
|
})
|
|
1034
1037
|
|
|
1035
1038
|
fastify.get('/', {
|
|
1036
1039
|
schema: {
|
|
1037
1040
|
headers: new Headers()
|
|
1038
1041
|
}
|
|
1039
|
-
}, () => {})
|
|
1042
|
+
}, () => { })
|
|
1043
|
+
|
|
1044
|
+
await fastify.ready()
|
|
1045
|
+
})
|
|
1046
|
+
|
|
1047
|
+
test('Custom validator builder override by custom validator compiler', async t => {
|
|
1048
|
+
t.plan(3)
|
|
1049
|
+
const ajvDefaults = {
|
|
1050
|
+
removeAdditional: true,
|
|
1051
|
+
coerceTypes: true,
|
|
1052
|
+
allErrors: true
|
|
1053
|
+
}
|
|
1054
|
+
const ajv1 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_one', type: 'object', validator: () => true })
|
|
1055
|
+
const ajv2 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_two', type: 'object', validator: () => true })
|
|
1056
|
+
const fastify = Fastify({ schemaController: { compilersFactory: { buildValidator: () => (routeSchemaDef) => ajv1.compile(routeSchemaDef.schema) } } })
|
|
1057
|
+
|
|
1058
|
+
fastify.setValidatorCompiler((routeSchemaDef) => ajv2.compile(routeSchemaDef.schema))
|
|
1059
|
+
|
|
1060
|
+
fastify.post('/two/:id', {
|
|
1061
|
+
schema: {
|
|
1062
|
+
params: {
|
|
1063
|
+
type: 'object',
|
|
1064
|
+
extended_two: true,
|
|
1065
|
+
properties: {
|
|
1066
|
+
id: { type: 'number' }
|
|
1067
|
+
},
|
|
1068
|
+
required: ['id']
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
handler: (req, _reply) => {
|
|
1072
|
+
t.same(typeof req.params.id, 'number')
|
|
1073
|
+
t.same(req.params.id, 43)
|
|
1074
|
+
return 'ok'
|
|
1075
|
+
}
|
|
1076
|
+
})
|
|
1040
1077
|
|
|
1041
1078
|
await fastify.ready()
|
|
1079
|
+
|
|
1080
|
+
const two = await fastify.inject({
|
|
1081
|
+
method: 'POST',
|
|
1082
|
+
url: '/two/43'
|
|
1083
|
+
})
|
|
1084
|
+
t.equal(two.statusCode, 200)
|
|
1085
|
+
})
|
|
1086
|
+
|
|
1087
|
+
test('Custom validator builder override by custom validator compiler in child instance', async t => {
|
|
1088
|
+
t.plan(6)
|
|
1089
|
+
const ajvDefaults = {
|
|
1090
|
+
removeAdditional: true,
|
|
1091
|
+
coerceTypes: true,
|
|
1092
|
+
allErrors: true
|
|
1093
|
+
}
|
|
1094
|
+
const ajv1 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_one', type: 'object', validator: () => true })
|
|
1095
|
+
const ajv2 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_two', type: 'object', validator: () => true })
|
|
1096
|
+
const fastify = Fastify({ schemaController: { compilersFactory: { buildValidator: () => (routeSchemaDef) => ajv1.compile(routeSchemaDef.schema) } } })
|
|
1097
|
+
|
|
1098
|
+
fastify.register((embedded, _opts, done) => {
|
|
1099
|
+
embedded.setValidatorCompiler((routeSchemaDef) => ajv2.compile(routeSchemaDef.schema))
|
|
1100
|
+
embedded.post('/two/:id', {
|
|
1101
|
+
schema: {
|
|
1102
|
+
params: {
|
|
1103
|
+
type: 'object',
|
|
1104
|
+
extended_two: true,
|
|
1105
|
+
properties: {
|
|
1106
|
+
id: { type: 'number' }
|
|
1107
|
+
},
|
|
1108
|
+
required: ['id']
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
handler: (req, _reply) => {
|
|
1112
|
+
t.same(typeof req.params.id, 'number')
|
|
1113
|
+
t.same(req.params.id, 43)
|
|
1114
|
+
return 'ok'
|
|
1115
|
+
}
|
|
1116
|
+
})
|
|
1117
|
+
done()
|
|
1118
|
+
})
|
|
1119
|
+
|
|
1120
|
+
fastify.post('/one/:id', {
|
|
1121
|
+
schema: {
|
|
1122
|
+
params: {
|
|
1123
|
+
type: 'object',
|
|
1124
|
+
extended_one: true,
|
|
1125
|
+
properties: {
|
|
1126
|
+
id: { type: 'number' }
|
|
1127
|
+
},
|
|
1128
|
+
required: ['id']
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
handler: (req, _reply) => {
|
|
1132
|
+
t.same(typeof req.params.id, 'number')
|
|
1133
|
+
t.same(req.params.id, 42)
|
|
1134
|
+
return 'ok'
|
|
1135
|
+
}
|
|
1136
|
+
})
|
|
1137
|
+
|
|
1138
|
+
await fastify.ready()
|
|
1139
|
+
|
|
1140
|
+
const one = await fastify.inject({
|
|
1141
|
+
method: 'POST',
|
|
1142
|
+
url: '/one/42'
|
|
1143
|
+
})
|
|
1144
|
+
t.equal(one.statusCode, 200)
|
|
1145
|
+
|
|
1146
|
+
const two = await fastify.inject({
|
|
1147
|
+
method: 'POST',
|
|
1148
|
+
url: '/two/43'
|
|
1149
|
+
})
|
|
1150
|
+
t.equal(two.statusCode, 200)
|
|
1042
1151
|
})
|