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.
- package/README.md +15 -4
- package/build/build-validation.js +8 -0
- package/docs/Benchmarking.md +2 -2
- package/docs/ContentTypeParser.md +12 -10
- package/docs/Decorators.md +14 -14
- package/docs/Ecosystem.md +7 -1
- package/docs/Errors.md +13 -8
- package/docs/Fluent-Schema.md +9 -12
- package/docs/Getting-Started.md +29 -25
- package/docs/HTTP2.md +1 -1
- package/docs/Hooks.md +201 -186
- package/docs/LTS.md +6 -7
- package/docs/Logging.md +10 -10
- package/docs/Middleware.md +59 -0
- package/docs/Plugins-Guide.md +52 -52
- package/docs/Plugins.md +3 -0
- package/docs/Reply.md +47 -3
- package/docs/Routes.md +120 -8
- package/docs/Server.md +69 -3
- package/docs/Serverless.md +76 -4
- package/docs/TypeScript.md +33 -10
- package/docs/Validation-and-Serialization.md +137 -1
- package/examples/typescript-server.ts +1 -1
- package/fastify.d.ts +52 -13
- package/fastify.js +68 -7
- package/lib/configValidator.js +99 -52
- package/lib/contentTypeParser.js +4 -4
- package/lib/context.js +2 -1
- package/lib/errors.js +21 -18
- package/lib/fourOhFour.js +10 -10
- package/lib/handleRequest.js +1 -2
- package/lib/logger.js +2 -2
- package/lib/pluginUtils.js +32 -0
- package/lib/reply.js +41 -6
- package/lib/route.js +37 -9
- package/lib/schemas.js +23 -12
- package/lib/symbols.js +4 -1
- package/lib/validation.js +15 -9
- package/lib/wrapThenable.js +1 -1
- package/package.json +34 -26
- package/test/404s.test.js +41 -1
- package/test/async-await.js +66 -0
- package/test/custom-parser.test.js +1 -1
- package/test/custom-querystring-parser.test.js +1 -1
- package/test/decorator.test.js +48 -0
- package/test/emit-warning.test.js +3 -3
- package/test/fastify-instance.test.js +29 -0
- package/test/helper.js +7 -7
- package/test/hooks-async.js +4 -3
- package/test/hooks.test.js +27 -8
- package/test/input-validation.test.js +126 -0
- package/test/internals/errors.test.js +9 -1
- package/test/internals/initialConfig.test.js +4 -2
- package/test/internals/plugin.test.js +4 -4
- package/test/internals/reply.test.js +78 -6
- package/test/internals/schemas.test.js +30 -0
- package/test/internals/validation.test.js +18 -0
- package/test/listen.test.js +1 -1
- package/test/logger.test.js +314 -1
- package/test/plugin.test.js +171 -0
- package/test/promises.test.js +55 -0
- package/test/proto-poisoning.test.js +76 -0
- package/test/route-hooks.test.js +109 -91
- package/test/route-prefix.test.js +1 -1
- package/test/schemas.test.js +450 -0
- package/test/shared-schemas.test.js +2 -2
- package/test/stream.test.js +10 -6
- package/test/throw.test.js +48 -2
- package/test/types/index.ts +86 -1
- package/test/validation-error-handling.test.js +3 -3
- package/test/versioned-routes.test.js +1 -1
- package/docs/Middlewares.md +0 -59
|
@@ -3,8 +3,37 @@
|
|
|
3
3
|
const t = require('tap')
|
|
4
4
|
const test = t.test
|
|
5
5
|
const Fastify = require('..')
|
|
6
|
+
const {
|
|
7
|
+
kOptions
|
|
8
|
+
} = require('../lib/symbols')
|
|
6
9
|
|
|
7
10
|
test('root fastify instance is an object', t => {
|
|
8
11
|
t.plan(1)
|
|
9
12
|
t.type(Fastify(), 'object')
|
|
10
13
|
})
|
|
14
|
+
|
|
15
|
+
test('fastify instance should contains ajv options', t => {
|
|
16
|
+
t.plan(1)
|
|
17
|
+
const fastify = Fastify({
|
|
18
|
+
ajv: {
|
|
19
|
+
customOptions: {
|
|
20
|
+
nullable: false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
t.same(fastify[kOptions].ajv, {
|
|
25
|
+
customOptions: {
|
|
26
|
+
nullable: false
|
|
27
|
+
},
|
|
28
|
+
plugins: []
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('fastify instance get invalid ajv options', t => {
|
|
33
|
+
t.plan(1)
|
|
34
|
+
t.throw(() => Fastify({
|
|
35
|
+
ajv: {
|
|
36
|
+
customOptions: 8
|
|
37
|
+
}
|
|
38
|
+
}))
|
|
39
|
+
})
|
package/test/helper.js
CHANGED
|
@@ -221,7 +221,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
221
221
|
})
|
|
222
222
|
|
|
223
223
|
if (loMethod === 'options') {
|
|
224
|
-
test(
|
|
224
|
+
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', t => {
|
|
225
225
|
t.plan(2)
|
|
226
226
|
sget({
|
|
227
227
|
method: upMethod,
|
|
@@ -328,7 +328,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
328
328
|
t.strictDeepEqual(JSON.parse(res.payload), {
|
|
329
329
|
error: 'Bad Request',
|
|
330
330
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
331
|
-
message:
|
|
331
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
332
332
|
statusCode: 400
|
|
333
333
|
})
|
|
334
334
|
})
|
|
@@ -344,7 +344,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
344
344
|
t.strictDeepEqual(JSON.parse(body.toString()), {
|
|
345
345
|
error: 'Bad Request',
|
|
346
346
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
347
|
-
message:
|
|
347
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
348
348
|
statusCode: 400
|
|
349
349
|
})
|
|
350
350
|
})
|
|
@@ -361,7 +361,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
361
361
|
t.strictDeepEqual(JSON.parse(res.payload), {
|
|
362
362
|
error: 'Bad Request',
|
|
363
363
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
364
|
-
message:
|
|
364
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
365
365
|
statusCode: 400
|
|
366
366
|
})
|
|
367
367
|
})
|
|
@@ -378,7 +378,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
378
378
|
t.strictDeepEqual(JSON.parse(body.toString()), {
|
|
379
379
|
error: 'Bad Request',
|
|
380
380
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
381
|
-
message:
|
|
381
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
382
382
|
statusCode: 400
|
|
383
383
|
})
|
|
384
384
|
})
|
|
@@ -395,7 +395,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
395
395
|
t.strictDeepEqual(JSON.parse(res.payload), {
|
|
396
396
|
error: 'Bad Request',
|
|
397
397
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
398
|
-
message:
|
|
398
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
399
399
|
statusCode: 400
|
|
400
400
|
})
|
|
401
401
|
})
|
|
@@ -412,7 +412,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
|
|
|
412
412
|
t.strictDeepEqual(JSON.parse(body.toString()), {
|
|
413
413
|
error: 'Bad Request',
|
|
414
414
|
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
|
|
415
|
-
message:
|
|
415
|
+
message: "FST_ERR_CTP_EMPTY_JSON_BODY: Body cannot be empty when content-type is set to 'application/json'",
|
|
416
416
|
statusCode: 400
|
|
417
417
|
})
|
|
418
418
|
})
|
package/test/hooks-async.js
CHANGED
|
@@ -520,7 +520,7 @@ function asyncHookTest (t) {
|
|
|
520
520
|
|
|
521
521
|
stream.on('data', line => {
|
|
522
522
|
t.strictEqual(line.level, 40)
|
|
523
|
-
t.true(line.msg.startsWith(
|
|
523
|
+
t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
|
524
524
|
t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
|
525
525
|
})
|
|
526
526
|
|
|
@@ -528,7 +528,7 @@ function asyncHookTest (t) {
|
|
|
528
528
|
})
|
|
529
529
|
|
|
530
530
|
t.test('4 arguments', t => {
|
|
531
|
-
t.plan(
|
|
531
|
+
t.plan(9)
|
|
532
532
|
const stream = split(JSON.parse)
|
|
533
533
|
const fastify = Fastify({
|
|
534
534
|
logger: { stream }
|
|
@@ -536,12 +536,13 @@ function asyncHookTest (t) {
|
|
|
536
536
|
|
|
537
537
|
stream.on('data', line => {
|
|
538
538
|
t.strictEqual(line.level, 40)
|
|
539
|
-
t.true(line.msg.startsWith(
|
|
539
|
+
t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
|
540
540
|
t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
|
541
541
|
})
|
|
542
542
|
|
|
543
543
|
fastify.addHook('onSend', async (req, reply, payload, next) => {})
|
|
544
544
|
fastify.addHook('preSerialization', async (req, reply, payload, next) => {})
|
|
545
|
+
fastify.addHook('onError', async (req, reply, error, next) => {})
|
|
545
546
|
})
|
|
546
547
|
|
|
547
548
|
t.end()
|
package/test/hooks.test.js
CHANGED
|
@@ -14,7 +14,7 @@ const symbols = require('../lib/symbols.js')
|
|
|
14
14
|
const payload = { hello: 'world' }
|
|
15
15
|
|
|
16
16
|
test('hooks', t => {
|
|
17
|
-
t.plan(
|
|
17
|
+
t.plan(38)
|
|
18
18
|
const fastify = Fastify()
|
|
19
19
|
|
|
20
20
|
try {
|
|
@@ -99,6 +99,9 @@ test('hooks', t => {
|
|
|
99
99
|
t.is(reply.test, 'the reply has come')
|
|
100
100
|
reply.code(200).send(payload)
|
|
101
101
|
},
|
|
102
|
+
onResponse: function (req, reply, done) {
|
|
103
|
+
t.ok('onResponse inside hook')
|
|
104
|
+
},
|
|
102
105
|
response: {
|
|
103
106
|
200: {
|
|
104
107
|
type: 'object'
|
|
@@ -485,7 +488,7 @@ test('onRoute hook should pass correct route with custom prefix', t => {
|
|
|
485
488
|
})
|
|
486
489
|
|
|
487
490
|
test('onRoute hook should pass correct route with custom options', t => {
|
|
488
|
-
t.plan(
|
|
491
|
+
t.plan(6)
|
|
489
492
|
const fastify = Fastify()
|
|
490
493
|
fastify.register((instance, opts, next) => {
|
|
491
494
|
instance.addHook('onRoute', function (route) {
|
|
@@ -493,8 +496,15 @@ test('onRoute hook should pass correct route with custom options', t => {
|
|
|
493
496
|
t.strictEqual(route.url, '/foo')
|
|
494
497
|
t.strictEqual(route.logLevel, 'info')
|
|
495
498
|
t.strictEqual(route.bodyLimit, 100)
|
|
499
|
+
t.type(route.logSerializers.test, 'function')
|
|
496
500
|
})
|
|
497
|
-
instance.get('/foo', {
|
|
501
|
+
instance.get('/foo', {
|
|
502
|
+
logLevel: 'info',
|
|
503
|
+
bodyLimit: 100,
|
|
504
|
+
logSerializers: {
|
|
505
|
+
test: value => value
|
|
506
|
+
}
|
|
507
|
+
}, function (req, reply) {
|
|
498
508
|
reply.send()
|
|
499
509
|
})
|
|
500
510
|
next()
|
|
@@ -2581,16 +2591,24 @@ test('preSerialization hooks should support encapsulation', t => {
|
|
|
2581
2591
|
})
|
|
2582
2592
|
|
|
2583
2593
|
test('onRegister hook should be called / 1', t => {
|
|
2584
|
-
t.plan(
|
|
2594
|
+
t.plan(4)
|
|
2585
2595
|
const fastify = Fastify()
|
|
2586
2596
|
|
|
2597
|
+
const pluginOpts = { prefix: 'hello', custom: 'world' }
|
|
2587
2598
|
fastify.register((instance, opts, next) => {
|
|
2588
2599
|
next()
|
|
2589
|
-
})
|
|
2600
|
+
}, pluginOpts)
|
|
2590
2601
|
|
|
2591
|
-
|
|
2602
|
+
let first = true
|
|
2603
|
+
fastify.addHook('onRegister', (instance, opts) => {
|
|
2592
2604
|
// duck typing for the win!
|
|
2593
2605
|
t.ok(instance.addHook)
|
|
2606
|
+
if (first) {
|
|
2607
|
+
// the first call of the onRegister is the main fastify instance, not the registered ones
|
|
2608
|
+
first = false
|
|
2609
|
+
} else {
|
|
2610
|
+
t.deepEquals(opts, pluginOpts)
|
|
2611
|
+
}
|
|
2594
2612
|
})
|
|
2595
2613
|
|
|
2596
2614
|
fastify.ready(err => {
|
|
@@ -2655,7 +2673,7 @@ test('onRegister hook should be called / 3', t => {
|
|
|
2655
2673
|
})
|
|
2656
2674
|
|
|
2657
2675
|
test('onRegister hook should be called / 4', t => {
|
|
2658
|
-
t.plan(
|
|
2676
|
+
t.plan(3)
|
|
2659
2677
|
const fastify = Fastify()
|
|
2660
2678
|
|
|
2661
2679
|
function plugin (instance, opts, next) {
|
|
@@ -2665,9 +2683,10 @@ test('onRegister hook should be called / 4', t => {
|
|
|
2665
2683
|
|
|
2666
2684
|
fastify.register(plugin)
|
|
2667
2685
|
|
|
2668
|
-
fastify.addHook('onRegister', instance => {
|
|
2686
|
+
fastify.addHook('onRegister', (instance, opts) => {
|
|
2669
2687
|
// duck typing for the win!
|
|
2670
2688
|
t.ok(instance.addHook)
|
|
2689
|
+
t.deepEquals(opts, {})
|
|
2671
2690
|
})
|
|
2672
2691
|
|
|
2673
2692
|
fastify.ready(err => {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const t = require('tap')
|
|
4
4
|
const test = t.test
|
|
5
5
|
const Fastify = require('..')
|
|
6
|
+
const ajvMergePatch = require('ajv-merge-patch')
|
|
6
7
|
|
|
7
8
|
test('case insensitive header validation', t => {
|
|
8
9
|
t.plan(2)
|
|
@@ -67,3 +68,128 @@ test('not evaluate json-schema $schema keyword', t => {
|
|
|
67
68
|
t.equal(res.payload, 'world')
|
|
68
69
|
})
|
|
69
70
|
})
|
|
71
|
+
|
|
72
|
+
test('Should handle $merge keywords in body', t => {
|
|
73
|
+
t.plan(5)
|
|
74
|
+
const fastify = Fastify({
|
|
75
|
+
ajv: {
|
|
76
|
+
plugins: [
|
|
77
|
+
ajvMergePatch
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
fastify.route({
|
|
83
|
+
method: 'POST',
|
|
84
|
+
url: '/',
|
|
85
|
+
schema: {
|
|
86
|
+
body: {
|
|
87
|
+
$merge: {
|
|
88
|
+
source: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
q: {
|
|
92
|
+
type: 'string'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
with: {
|
|
97
|
+
required: ['q']
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
handler (req, reply) {
|
|
103
|
+
reply.send({ ok: 1 })
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
fastify.ready(err => {
|
|
108
|
+
t.error(err)
|
|
109
|
+
|
|
110
|
+
fastify.inject({
|
|
111
|
+
method: 'POST',
|
|
112
|
+
url: '/'
|
|
113
|
+
}, (err, res) => {
|
|
114
|
+
t.error(err)
|
|
115
|
+
t.equals(res.statusCode, 400)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
fastify.inject({
|
|
119
|
+
method: 'POST',
|
|
120
|
+
url: '/',
|
|
121
|
+
body: {
|
|
122
|
+
q: 'foo'
|
|
123
|
+
}
|
|
124
|
+
}, (err, res) => {
|
|
125
|
+
t.error(err)
|
|
126
|
+
t.equals(res.statusCode, 200)
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('Should handle $patch keywords in body', t => {
|
|
132
|
+
t.plan(5)
|
|
133
|
+
const fastify = Fastify({
|
|
134
|
+
ajv: {
|
|
135
|
+
plugins: [
|
|
136
|
+
ajvMergePatch
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
fastify.route({
|
|
142
|
+
method: 'POST',
|
|
143
|
+
url: '/',
|
|
144
|
+
schema: {
|
|
145
|
+
body: {
|
|
146
|
+
$patch: {
|
|
147
|
+
source: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
q: {
|
|
151
|
+
type: 'string'
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
with: [
|
|
156
|
+
{
|
|
157
|
+
op: 'add',
|
|
158
|
+
path: '/properties/q',
|
|
159
|
+
value: { type: 'number' }
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
handler (req, reply) {
|
|
166
|
+
reply.send({ ok: 1 })
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
fastify.ready(err => {
|
|
171
|
+
t.error(err)
|
|
172
|
+
|
|
173
|
+
fastify.inject({
|
|
174
|
+
method: 'POST',
|
|
175
|
+
url: '/',
|
|
176
|
+
body: {
|
|
177
|
+
q: 'foo'
|
|
178
|
+
}
|
|
179
|
+
}, (err, res) => {
|
|
180
|
+
t.error(err)
|
|
181
|
+
t.equals(res.statusCode, 400)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
fastify.inject({
|
|
185
|
+
method: 'POST',
|
|
186
|
+
url: '/',
|
|
187
|
+
body: {
|
|
188
|
+
q: 10
|
|
189
|
+
}
|
|
190
|
+
}, (err, res) => {
|
|
191
|
+
t.error(err)
|
|
192
|
+
t.equals(res.statusCode, 200)
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
})
|
|
@@ -78,7 +78,7 @@ test('Should throw when error code has no message', t => {
|
|
|
78
78
|
try {
|
|
79
79
|
createError('code')
|
|
80
80
|
} catch (err) {
|
|
81
|
-
t.equal(err.message,
|
|
81
|
+
t.equal(err.message, 'Fastify error message must not be empty')
|
|
82
82
|
}
|
|
83
83
|
})
|
|
84
84
|
|
|
@@ -94,3 +94,11 @@ test('Create error with different base', t => {
|
|
|
94
94
|
t.equal(err.statusCode, 500)
|
|
95
95
|
t.ok(err.stack)
|
|
96
96
|
})
|
|
97
|
+
|
|
98
|
+
test('Error has appropriate string tag', t => {
|
|
99
|
+
t.plan(1)
|
|
100
|
+
const NewError = createError('CODE', 'foo')
|
|
101
|
+
const err = new NewError()
|
|
102
|
+
const str = Object.prototype.toString.call(err)
|
|
103
|
+
t.equal(str, '[object Error]')
|
|
104
|
+
})
|
|
@@ -25,6 +25,7 @@ test('without options passed to Fastify, initialConfig should expose default val
|
|
|
25
25
|
ignoreTrailingSlash: false,
|
|
26
26
|
maxParamLength: 100,
|
|
27
27
|
onProtoPoisoning: 'error',
|
|
28
|
+
onConstructorPoisoning: 'ignore',
|
|
28
29
|
pluginTimeout: 10000,
|
|
29
30
|
requestIdHeader: 'request-id',
|
|
30
31
|
requestIdLogLabel: 'reqId'
|
|
@@ -55,7 +56,7 @@ test('Fastify.initialConfig should expose all options', t => {
|
|
|
55
56
|
}
|
|
56
57
|
},
|
|
57
58
|
deriveVersion: (req, ctx) => {
|
|
58
|
-
return req.headers
|
|
59
|
+
return req.headers.accept
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -153,7 +154,7 @@ test('Return an error if options do not match the validation schema', t => {
|
|
|
153
154
|
} catch (error) {
|
|
154
155
|
t.type(error, Error)
|
|
155
156
|
t.equal(error.name, 'FastifyError [FST_ERR_INIT_OPTS_INVALID]')
|
|
156
|
-
t.equal(error.message,
|
|
157
|
+
t.equal(error.message, 'FST_ERR_INIT_OPTS_INVALID: Invalid initialization options: \'["should be boolean"]\'')
|
|
157
158
|
t.equal(error.code, 'FST_ERR_INIT_OPTS_INVALID')
|
|
158
159
|
t.ok(error.stack)
|
|
159
160
|
t.pass()
|
|
@@ -228,6 +229,7 @@ test('Should not have issues when passing stream options to Pino.js', t => {
|
|
|
228
229
|
ignoreTrailingSlash: true,
|
|
229
230
|
maxParamLength: 100,
|
|
230
231
|
onProtoPoisoning: 'error',
|
|
232
|
+
onConstructorPoisoning: 'ignore',
|
|
231
233
|
pluginTimeout: 10000,
|
|
232
234
|
requestIdHeader: 'request-id',
|
|
233
235
|
requestIdLogLabel: 'reqId'
|
|
@@ -7,7 +7,7 @@ const pluginUtilsPublic = require('../../lib/pluginUtils.js')
|
|
|
7
7
|
const pluginUtils = require('../../lib/pluginUtils')[Symbol.for('internals')]
|
|
8
8
|
const symbols = require('../../lib/symbols.js')
|
|
9
9
|
|
|
10
|
-
test(
|
|
10
|
+
test("shouldSkipOverride should check the 'skip-override' symbol", t => {
|
|
11
11
|
t.plan(2)
|
|
12
12
|
|
|
13
13
|
yes[Symbol.for('skip-override')] = true
|
|
@@ -19,7 +19,7 @@ test(`shouldSkipOverride should check the 'skip-override' symbol`, t => {
|
|
|
19
19
|
function no () {}
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
test(
|
|
22
|
+
test("getMeta should return the object stored with the 'plugin-meta' symbol", t => {
|
|
23
23
|
t.plan(1)
|
|
24
24
|
|
|
25
25
|
const meta = { hello: 'world' }
|
|
@@ -76,7 +76,7 @@ test('checkDecorators should check if the given decorator is present in the inst
|
|
|
76
76
|
pluginUtils.checkDecorators.call(context, fn)
|
|
77
77
|
t.fail('should throw')
|
|
78
78
|
} catch (err) {
|
|
79
|
-
t.is(err.message,
|
|
79
|
+
t.is(err.message, "The decorator 'plugin' is not present in Request")
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function fn () {}
|
|
@@ -117,7 +117,7 @@ test('checkDependencies should check if the given dependency is present in the i
|
|
|
117
117
|
pluginUtils.checkDependencies.call(context, fn)
|
|
118
118
|
t.fail('should throw')
|
|
119
119
|
} catch (err) {
|
|
120
|
-
t.is(err.message,
|
|
120
|
+
t.is(err.message, "The dependency 'plugin' of plugin 'test-plugin' is not registered")
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
function fn () {}
|
|
@@ -6,6 +6,7 @@ const sget = require('simple-get').concat
|
|
|
6
6
|
const http = require('http')
|
|
7
7
|
const NotFound = require('http-errors').NotFound
|
|
8
8
|
const Reply = require('../../lib/reply')
|
|
9
|
+
const { Writable } = require('readable-stream')
|
|
9
10
|
const {
|
|
10
11
|
kReplyErrorHandlerCalled,
|
|
11
12
|
kReplyHeaders,
|
|
@@ -36,14 +37,32 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
36
37
|
|
|
37
38
|
test('reply.send throw with circular JSON', t => {
|
|
38
39
|
t.plan(1)
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const response = {
|
|
41
|
+
setHeader: () => {},
|
|
42
|
+
hasHeader: () => false,
|
|
43
|
+
getHeader: () => undefined,
|
|
44
|
+
writeHead: () => {},
|
|
45
|
+
end: () => {}
|
|
46
|
+
}
|
|
47
|
+
const reply = new Reply(response, { onSend: [] }, null)
|
|
42
48
|
t.throws(() => {
|
|
43
49
|
var obj = {}
|
|
44
50
|
obj.obj = obj
|
|
45
51
|
reply.send(JSON.stringify(obj))
|
|
46
|
-
})
|
|
52
|
+
}, 'Converting circular structure to JSON')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('reply.send returns itself', t => {
|
|
56
|
+
t.plan(1)
|
|
57
|
+
const response = {
|
|
58
|
+
setHeader: () => {},
|
|
59
|
+
hasHeader: () => false,
|
|
60
|
+
getHeader: () => undefined,
|
|
61
|
+
writeHead: () => {},
|
|
62
|
+
end: () => {}
|
|
63
|
+
}
|
|
64
|
+
const reply = new Reply(response, { onSend: [] }, null)
|
|
65
|
+
t.equal(reply.send('hello'), reply)
|
|
47
66
|
})
|
|
48
67
|
|
|
49
68
|
test('reply.serializer should set a custom serializer', t => {
|
|
@@ -262,7 +281,7 @@ test('within an instance', t => {
|
|
|
262
281
|
http.get(url, (response) => {
|
|
263
282
|
t.strictEqual(response.headers['x-onsend'], 'yes')
|
|
264
283
|
t.strictEqual(response.headers['content-length'], '0')
|
|
265
|
-
t.strictEqual(response.headers
|
|
284
|
+
t.strictEqual(response.headers.location, '/')
|
|
266
285
|
})
|
|
267
286
|
})
|
|
268
287
|
|
|
@@ -419,7 +438,7 @@ test('stream using reply.res.writeHead should return customize headers', t => {
|
|
|
419
438
|
url: 'http://localhost:' + fastify.server.address().port
|
|
420
439
|
}, (err, response, body) => {
|
|
421
440
|
t.error(err)
|
|
422
|
-
t.strictEqual(response.headers
|
|
441
|
+
t.strictEqual(response.headers.location, '/')
|
|
423
442
|
t.strictEqual(response.headers['Content-Type'], undefined)
|
|
424
443
|
t.deepEqual(body, buf)
|
|
425
444
|
})
|
|
@@ -905,6 +924,23 @@ test('.status() is an alias for .code()', t => {
|
|
|
905
924
|
})
|
|
906
925
|
})
|
|
907
926
|
|
|
927
|
+
test('.statusCode is getter and setter', t => {
|
|
928
|
+
t.plan(4)
|
|
929
|
+
const fastify = require('../..')()
|
|
930
|
+
|
|
931
|
+
fastify.get('/', function (req, reply) {
|
|
932
|
+
t.ok(reply.statusCode, 200, 'default status value')
|
|
933
|
+
reply.statusCode = 418
|
|
934
|
+
t.ok(reply.statusCode, 418)
|
|
935
|
+
reply.send()
|
|
936
|
+
})
|
|
937
|
+
|
|
938
|
+
fastify.inject('/', (err, res) => {
|
|
939
|
+
t.error(err)
|
|
940
|
+
t.is(res.statusCode, 418)
|
|
941
|
+
})
|
|
942
|
+
})
|
|
943
|
+
|
|
908
944
|
test('reply.header setting multiple cookies as multiple Set-Cookie headers', t => {
|
|
909
945
|
t.plan(7)
|
|
910
946
|
|
|
@@ -1252,3 +1288,39 @@ test('reply should not call the custom serializer for errors and not found', t =
|
|
|
1252
1288
|
t.strictEqual(res.statusCode, 404)
|
|
1253
1289
|
})
|
|
1254
1290
|
})
|
|
1291
|
+
|
|
1292
|
+
test('reply.then', t => {
|
|
1293
|
+
t.plan(2)
|
|
1294
|
+
|
|
1295
|
+
function context () {}
|
|
1296
|
+
function request () {}
|
|
1297
|
+
|
|
1298
|
+
t.test('without an error', t => {
|
|
1299
|
+
t.plan(1)
|
|
1300
|
+
|
|
1301
|
+
const response = new Writable()
|
|
1302
|
+
const reply = new Reply(response, context, request)
|
|
1303
|
+
|
|
1304
|
+
reply.then(function () {
|
|
1305
|
+
t.pass('fullfilled called')
|
|
1306
|
+
})
|
|
1307
|
+
|
|
1308
|
+
response.destroy()
|
|
1309
|
+
})
|
|
1310
|
+
|
|
1311
|
+
t.test('with an error', t => {
|
|
1312
|
+
t.plan(1)
|
|
1313
|
+
|
|
1314
|
+
const response = new Writable()
|
|
1315
|
+
const reply = new Reply(response, context, request)
|
|
1316
|
+
const _err = new Error('kaboom')
|
|
1317
|
+
|
|
1318
|
+
reply.then(function () {
|
|
1319
|
+
t.fail('fullfilled called')
|
|
1320
|
+
}, function (err) {
|
|
1321
|
+
t.equal(err, _err)
|
|
1322
|
+
})
|
|
1323
|
+
|
|
1324
|
+
response.destroy(_err)
|
|
1325
|
+
})
|
|
1326
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const test = require('tap').test
|
|
4
|
+
const { Schemas } = require('../../lib/schemas')
|
|
5
|
+
|
|
6
|
+
test('Should not change resolved schema', t => {
|
|
7
|
+
t.plan(4)
|
|
8
|
+
|
|
9
|
+
const schemas = new Schemas()
|
|
10
|
+
schemas.add({
|
|
11
|
+
$id: 'A',
|
|
12
|
+
field: 'value'
|
|
13
|
+
})
|
|
14
|
+
const schema = {
|
|
15
|
+
a: 'A#'
|
|
16
|
+
}
|
|
17
|
+
const resolvedSchema = schemas.resolveRefs(schema)
|
|
18
|
+
|
|
19
|
+
t.same(resolvedSchema.a, {
|
|
20
|
+
field: 'value'
|
|
21
|
+
})
|
|
22
|
+
t.same(resolvedSchema.$id, undefined)
|
|
23
|
+
|
|
24
|
+
schemas.getJsonSchemas()
|
|
25
|
+
|
|
26
|
+
t.same(resolvedSchema.a, {
|
|
27
|
+
field: 'value'
|
|
28
|
+
})
|
|
29
|
+
t.same(resolvedSchema.$id, undefined)
|
|
30
|
+
})
|
|
@@ -226,3 +226,21 @@ test('build schema - headers are not lowercased in case of custom object', t =>
|
|
|
226
226
|
return () => {}
|
|
227
227
|
}, new Schemas())
|
|
228
228
|
})
|
|
229
|
+
|
|
230
|
+
test('build schema - uppercased headers are not included', t => {
|
|
231
|
+
t.plan(1)
|
|
232
|
+
const opts = {
|
|
233
|
+
schema: {
|
|
234
|
+
headers: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
'Content-Type': { type: 'string' }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
validation.build(opts, schema => {
|
|
243
|
+
t.notOk('Content-Type' in schema.properties, 'uppercase does not exist')
|
|
244
|
+
return () => {}
|
|
245
|
+
}, new Schemas())
|
|
246
|
+
})
|
package/test/listen.test.js
CHANGED
|
@@ -190,7 +190,7 @@ if (os.platform() !== 'win32') {
|
|
|
190
190
|
const fastify = Fastify()
|
|
191
191
|
t.tearDown(fastify.close.bind(fastify))
|
|
192
192
|
|
|
193
|
-
const sockFile = path.join(os.tmpdir(), 'server.sock
|
|
193
|
+
const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').substr(2, 8)}-server.sock`)
|
|
194
194
|
try {
|
|
195
195
|
fs.unlinkSync(sockFile)
|
|
196
196
|
} catch (e) { }
|