fastify 5.9.0 → 5.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/PROJECT_CHARTER.md +1 -1
- package/README.md +7 -10
- package/SPONSORS.md +1 -0
- package/build/build-validation.js +2 -2
- package/docs/Guides/Delay-Accepting-Requests.md +1 -1
- package/docs/Guides/Ecosystem.md +12 -9
- package/docs/Guides/Getting-Started.md +10 -10
- package/docs/Guides/Migration-Guide-V4.md +2 -2
- package/docs/Guides/Migration-Guide-V5.md +1 -1
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Guides/Serverless.md +6 -13
- package/docs/Guides/Style-Guide.md +1 -1
- package/docs/Reference/Errors.md +6 -0
- package/docs/Reference/Hooks.md +1 -1
- package/docs/Reference/Logging.md +3 -0
- package/docs/Reference/Reply.md +2 -2
- package/docs/Reference/Request.md +13 -13
- package/docs/Reference/Server.md +117 -17
- package/docs/Reference/Type-Providers.md +24 -4
- package/docs/Reference/TypeScript.md +8 -10
- package/docs/Reference/Warnings.md +5 -0
- package/eslint.config.js +1 -1
- package/fastify.d.ts +60 -13
- package/fastify.js +28 -23
- package/lib/content-type-parser.js +1 -11
- package/lib/content-type.js +70 -19
- package/lib/context.js +0 -3
- package/lib/error-handler.js +7 -26
- package/lib/errors.js +17 -0
- package/lib/four-oh-four.js +8 -10
- package/lib/handle-request.js +37 -8
- package/lib/hooks.js +5 -1
- package/lib/log-controller.js +169 -0
- package/lib/logger-factory.js +25 -4
- package/lib/reply.js +35 -44
- package/lib/req-id-gen-factory.js +4 -1
- package/lib/request.js +3 -3
- package/lib/route.js +28 -36
- package/lib/schemas.js +3 -3
- package/lib/symbols.js +1 -1
- package/lib/validation.js +10 -1
- package/lib/warnings.js +19 -1
- package/package.json +4 -4
- package/test/content-type.test.js +51 -4
- package/test/find-route.test.js +35 -0
- package/test/fix-6411.test.js +65 -0
- package/test/genReqId.test.js +24 -0
- package/test/hooks.test.js +71 -0
- package/test/internals/all.test.js +2 -1
- package/test/internals/errors.test.js +21 -1
- package/test/internals/logger.test.js +322 -0
- package/test/internals/reply.test.js +57 -4
- package/test/internals/request.test.js +10 -7
- package/test/logger/logging.test.js +40 -1
- package/test/reply-error.test.js +35 -0
- package/test/rfc-10008.test.js +147 -0
- package/test/route-shorthand.test.js +14 -4
- package/test/schema-serialization.test.js +33 -0
- package/test/stream.4.test.js +2 -2
- package/test/trust-proxy.test.js +49 -30
- package/test/types/errors.tst.ts +2 -0
- package/test/types/route.tst.ts +3 -3
- package/types/content-type-parser.d.ts +26 -6
- package/types/errors.d.ts +3 -0
- package/types/hooks.d.ts +137 -76
- package/types/instance.d.ts +152 -47
- package/types/logger.d.ts +57 -2
- package/types/plugin.d.ts +7 -3
- package/types/register.d.ts +53 -10
- package/types/reply.d.ts +62 -14
- package/types/route.d.ts +68 -34
- package/types/type-provider.d.ts +29 -8
- package/types/utils.d.ts +2 -2
package/lib/warnings.js
CHANGED
|
@@ -7,6 +7,8 @@ const { createWarning } = require('process-warning')
|
|
|
7
7
|
* - FSTWRN001
|
|
8
8
|
* - FSTSEC001
|
|
9
9
|
* - FSTDEP022
|
|
10
|
+
* - FSTDEP023
|
|
11
|
+
* - FSTDEP024
|
|
10
12
|
*
|
|
11
13
|
* Deprecation Codes FSTDEP001 - FSTDEP021 were used by v4 and MUST NOT be reused.
|
|
12
14
|
* - FSTDEP022 is used by v5 and MUST NOT be reused.
|
|
@@ -48,10 +50,26 @@ const FSTDEP022 = createWarning({
|
|
|
48
50
|
unlimited: true
|
|
49
51
|
})
|
|
50
52
|
|
|
53
|
+
const FSTDEP023 = createWarning({
|
|
54
|
+
name: 'FastifyDeprecation',
|
|
55
|
+
code: 'FSTDEP023',
|
|
56
|
+
message: 'disableRequestLogging option is deprecated. Use the logController option with disableRequestLogging or isLogDisabled override instead. The disableRequestLogging top-level option will be removed in `fastify@6`.',
|
|
57
|
+
unlimited: true
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const FSTDEP024 = createWarning({
|
|
61
|
+
name: 'FastifyDeprecation',
|
|
62
|
+
code: 'FSTDEP024',
|
|
63
|
+
message: 'requestIdLogLabel option is deprecated. Use the logController option with requestIdLogLabel instead. The requestIdLogLabel top-level option will be removed in `fastify@6`.',
|
|
64
|
+
unlimited: true
|
|
65
|
+
})
|
|
66
|
+
|
|
51
67
|
module.exports = {
|
|
52
68
|
FSTWRN001,
|
|
53
69
|
FSTWRN003,
|
|
54
70
|
FSTWRN004,
|
|
55
71
|
FSTSEC001,
|
|
56
|
-
FSTDEP022
|
|
72
|
+
FSTDEP022,
|
|
73
|
+
FSTDEP023,
|
|
74
|
+
FSTDEP024
|
|
57
75
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"@sinonjs/fake-timers": "^11.2.2",
|
|
171
171
|
"@stylistic/eslint-plugin": "^5.1.0",
|
|
172
172
|
"@stylistic/eslint-plugin-js": "^4.1.0",
|
|
173
|
-
"@types/node": "^
|
|
173
|
+
"@types/node": "^26.0.1",
|
|
174
174
|
"ajv": "^8.12.0",
|
|
175
175
|
"ajv-errors": "^3.0.0",
|
|
176
176
|
"ajv-formats": "^3.0.1",
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
"joi": "^18.0.1",
|
|
192
192
|
"json-schema-to-ts": "^3.0.1",
|
|
193
193
|
"JSONStream": "^1.3.5",
|
|
194
|
-
"markdownlint-cli2": "^0.
|
|
194
|
+
"markdownlint-cli2": "^0.23.0",
|
|
195
195
|
"neostandard": "^0.12.0",
|
|
196
196
|
"node-forge": "^1.3.1",
|
|
197
197
|
"proxyquire": "^2.1.3",
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
"@fastify/proxy-addr": "^5.0.0",
|
|
211
211
|
"abstract-logging": "^2.0.1",
|
|
212
212
|
"avvio": "^9.0.0",
|
|
213
|
-
"fast-json-stringify": "^
|
|
213
|
+
"fast-json-stringify": "^7.0.0",
|
|
214
214
|
"find-my-way": "^9.6.0",
|
|
215
215
|
"light-my-request": "^6.0.0",
|
|
216
216
|
"pino": "^9.14.0 || ^10.1.0",
|
|
@@ -161,12 +161,11 @@ describe('ContentType class', () => {
|
|
|
161
161
|
t.assert.equal(found.mediaType, 'application/json')
|
|
162
162
|
t.assert.equal(found.type, 'application')
|
|
163
163
|
t.assert.equal(found.subtype, 'json')
|
|
164
|
-
t.assert.equal(found.parameters.size,
|
|
164
|
+
t.assert.equal(found.parameters.size, 2)
|
|
165
165
|
|
|
166
166
|
const expected = [
|
|
167
167
|
['charset', 'utf-8'],
|
|
168
|
-
['foo', 'BaR']
|
|
169
|
-
['baz', 'invalid quoted string']
|
|
168
|
+
['foo', 'BaR']
|
|
170
169
|
]
|
|
171
170
|
t.assert.deepStrictEqual(
|
|
172
171
|
Array.from(found.parameters.entries()),
|
|
@@ -175,7 +174,55 @@ describe('ContentType class', () => {
|
|
|
175
174
|
|
|
176
175
|
t.assert.equal(
|
|
177
176
|
found.toString(),
|
|
178
|
-
'application/json; charset="utf-8"; foo="BaR"
|
|
177
|
+
'application/json; charset="utf-8"; foo="BaR"'
|
|
179
178
|
)
|
|
180
179
|
})
|
|
180
|
+
|
|
181
|
+
test('preserves a semicolon inside a quoted parameter value', (t) => {
|
|
182
|
+
// RFC 9110 §5.6.6: ';' is a literal qdtext octet inside a quoted-string
|
|
183
|
+
// and does not terminate the parameter value.
|
|
184
|
+
const found = new ContentType('application/json; name="foo;bar"; charset=utf-8')
|
|
185
|
+
t.assert.equal(found.isValid, true)
|
|
186
|
+
t.assert.equal(found.mediaType, 'application/json')
|
|
187
|
+
t.assert.equal(found.parameters.get('name'), 'foo;bar')
|
|
188
|
+
t.assert.equal(found.parameters.get('charset'), 'utf-8')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('does not leak a fake parameter out of a quoted value', (t) => {
|
|
192
|
+
// A `key=value;` sequence inside a quoted-string is opaque content of the
|
|
193
|
+
// enclosing value, not a subsequent parameter.
|
|
194
|
+
const found = new ContentType('application/json; name="a=b;charset=fake"; boundary=xyz')
|
|
195
|
+
t.assert.equal(found.isValid, true)
|
|
196
|
+
t.assert.equal(found.parameters.get('name'), 'a=b;charset=fake')
|
|
197
|
+
t.assert.equal(found.parameters.get('boundary'), 'xyz')
|
|
198
|
+
t.assert.equal(found.parameters.has('charset'), false)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('unescapes a quoted-pair inside a quoted-string', (t) => {
|
|
202
|
+
// RFC 9110 §5.6.4: a quoted-pair MUST be handled as if replaced by
|
|
203
|
+
// the octet following the backslash.
|
|
204
|
+
const found = new ContentType('application/json; name="he said \\"hi\\""')
|
|
205
|
+
t.assert.equal(found.isValid, true)
|
|
206
|
+
t.assert.equal(found.parameters.get('name'), 'he said "hi"')
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('ContentType class cache', () => {
|
|
211
|
+
test('allow access cache', (t) => {
|
|
212
|
+
const contentType1 = ContentType.from('application/json')
|
|
213
|
+
const contentType2 = ContentType.cache.get('application/json')
|
|
214
|
+
t.assert.equal(contentType1, contentType2)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test('returns same instance for the same content type string', (t) => {
|
|
218
|
+
const contentType1 = ContentType.from('application/json')
|
|
219
|
+
const contentType2 = ContentType.from('application/json')
|
|
220
|
+
t.assert.equal(contentType1, contentType2)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test('returns different instances for different content type strings', (t) => {
|
|
224
|
+
const contentType1 = ContentType.from('application/json')
|
|
225
|
+
const contentType2 = ContentType.from('text/plain')
|
|
226
|
+
t.assert.notEqual(contentType1, contentType2)
|
|
227
|
+
})
|
|
181
228
|
})
|
package/test/find-route.test.js
CHANGED
|
@@ -66,6 +66,22 @@ test('findRoute should return null when when url is not passed', t => {
|
|
|
66
66
|
}), null)
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
+
test('findRoute should return null when method is not passed', t => {
|
|
70
|
+
t.plan(1)
|
|
71
|
+
const fastify = Fastify()
|
|
72
|
+
|
|
73
|
+
fastify.get('/artists/:artistId', {
|
|
74
|
+
schema: {
|
|
75
|
+
params: { artistId: { type: 'integer' } }
|
|
76
|
+
},
|
|
77
|
+
handler: (req, reply) => reply.send(typeof req.params.artistId)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
t.assert.strictEqual(fastify.findRoute({
|
|
81
|
+
url: '/artists/:artistId'
|
|
82
|
+
}), null)
|
|
83
|
+
})
|
|
84
|
+
|
|
69
85
|
test('findRoute should return null when route cannot be found due to a different path', t => {
|
|
70
86
|
t.plan(1)
|
|
71
87
|
const fastify = Fastify()
|
|
@@ -103,6 +119,25 @@ test('findRoute should return the route when found', t => {
|
|
|
103
119
|
t.assert.strictEqual(route.params.artistId, ':artistId')
|
|
104
120
|
})
|
|
105
121
|
|
|
122
|
+
test('findRoute should find a route even if method is not uppercased', t => {
|
|
123
|
+
t.plan(2)
|
|
124
|
+
const fastify = Fastify()
|
|
125
|
+
|
|
126
|
+
fastify.get('/artists/:artistId', {
|
|
127
|
+
schema: {
|
|
128
|
+
params: { artistId: { type: 'integer' } }
|
|
129
|
+
},
|
|
130
|
+
handler: (req, reply) => reply.send(typeof req.params.artistId)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const route = fastify.findRoute({
|
|
134
|
+
method: 'get',
|
|
135
|
+
url: '/artists/:artistId'
|
|
136
|
+
})
|
|
137
|
+
t.assert.notStrictEqual(route, null)
|
|
138
|
+
t.assert.strictEqual(route.params.artistId, ':artistId')
|
|
139
|
+
})
|
|
140
|
+
|
|
106
141
|
test('findRoute should work correctly when used within plugins', (t, done) => {
|
|
107
142
|
t.plan(1)
|
|
108
143
|
const fastify = Fastify()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
|
|
6
|
+
test('custom validatorCompiler returning falsy values should update request param', async (t) => {
|
|
7
|
+
const fastify = Fastify()
|
|
8
|
+
|
|
9
|
+
fastify.setValidatorCompiler(() => {
|
|
10
|
+
return (data) => {
|
|
11
|
+
if (typeof data === 'object' && data !== null && 'coerceTo' in data) {
|
|
12
|
+
return { value: data.coerceTo }
|
|
13
|
+
}
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
fastify.post('/test', { schema: { body: { type: 'object' } } }, (request, reply) => {
|
|
19
|
+
reply.send({ body: request.body })
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// value: 0 (falsy)
|
|
23
|
+
{
|
|
24
|
+
const res = await fastify.inject({
|
|
25
|
+
method: 'POST',
|
|
26
|
+
url: '/test',
|
|
27
|
+
payload: { coerceTo: 0 }
|
|
28
|
+
})
|
|
29
|
+
t.assert.strictEqual(res.statusCode, 200)
|
|
30
|
+
t.assert.strictEqual(JSON.parse(res.payload).body, 0, 'should update body to 0')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// value: "" (falsy)
|
|
34
|
+
{
|
|
35
|
+
const res = await fastify.inject({
|
|
36
|
+
method: 'POST',
|
|
37
|
+
url: '/test',
|
|
38
|
+
payload: { coerceTo: '' }
|
|
39
|
+
})
|
|
40
|
+
t.assert.strictEqual(res.statusCode, 200)
|
|
41
|
+
t.assert.strictEqual(JSON.parse(res.payload).body, '', 'should update body to empty string')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// value: false (falsy)
|
|
45
|
+
{
|
|
46
|
+
const res = await fastify.inject({
|
|
47
|
+
method: 'POST',
|
|
48
|
+
url: '/test',
|
|
49
|
+
payload: { coerceTo: false }
|
|
50
|
+
})
|
|
51
|
+
t.assert.strictEqual(res.statusCode, 200)
|
|
52
|
+
t.assert.strictEqual(JSON.parse(res.payload).body, false, 'should update body to false')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// value: null (falsy)
|
|
56
|
+
{
|
|
57
|
+
const res = await fastify.inject({
|
|
58
|
+
method: 'POST',
|
|
59
|
+
url: '/test',
|
|
60
|
+
payload: { coerceTo: null }
|
|
61
|
+
})
|
|
62
|
+
t.assert.strictEqual(res.statusCode, 200)
|
|
63
|
+
t.assert.strictEqual(JSON.parse(res.payload).body, null, 'should update body to null')
|
|
64
|
+
}
|
|
65
|
+
})
|
package/test/genReqId.test.js
CHANGED
|
@@ -85,6 +85,30 @@ test('Should handle properly requestIdHeader option', t => {
|
|
|
85
85
|
t.assert.strictEqual(Fastify({ requestIdHeader: 'x-request-id' }).initialConfig.requestIdHeader, 'x-request-id')
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
+
test('Should expose the genReqId function via the getter', (t, done) => {
|
|
89
|
+
t.plan(5)
|
|
90
|
+
|
|
91
|
+
const fastify = Fastify()
|
|
92
|
+
t.assert.strictEqual(typeof fastify.genReqId, 'function')
|
|
93
|
+
t.assert.strictEqual(typeof fastify.genReqId({ headers: {} }), 'string')
|
|
94
|
+
|
|
95
|
+
const custom = function (req) {
|
|
96
|
+
return 'custom'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fastify.register(function (instance, opts, next) {
|
|
100
|
+
instance.setGenReqId(custom)
|
|
101
|
+
t.assert.strictEqual(instance.genReqId(), 'custom')
|
|
102
|
+
next()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
fastify.ready(err => {
|
|
106
|
+
t.assert.ifError(err)
|
|
107
|
+
t.assert.notStrictEqual(fastify.genReqId, custom)
|
|
108
|
+
done()
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
88
112
|
test('Should accept option to set genReqId with setGenReqId option', (t, done) => {
|
|
89
113
|
t.plan(9)
|
|
90
114
|
|
package/test/hooks.test.js
CHANGED
|
@@ -3599,3 +3599,74 @@ test('onRequestAbort should handle async errors / 2', (t, testDone) => {
|
|
|
3599
3599
|
sleep(500).then(() => socket.destroy())
|
|
3600
3600
|
})
|
|
3601
3601
|
})
|
|
3602
|
+
|
|
3603
|
+
test('socket timeout listener is removed when socket._meta is cleared after response', async t => {
|
|
3604
|
+
t.plan(4)
|
|
3605
|
+
|
|
3606
|
+
const fastify = Fastify({ connectionTimeout: 200 })
|
|
3607
|
+
t.after(() => fastify.close())
|
|
3608
|
+
|
|
3609
|
+
fastify.addHook('onTimeout', function (req, reply, done) { done() })
|
|
3610
|
+
|
|
3611
|
+
fastify.addHook('onResponse', function (req, reply, done) {
|
|
3612
|
+
const socket = req.raw.socket
|
|
3613
|
+
t.assert.strictEqual(socket._meta, null, 'socket._meta must be null after response')
|
|
3614
|
+
t.assert.strictEqual(socket.listeners('timeout').some(listener => listener.name === 'handleTimeout'), false, 'Fastify timeout listener must be removed after response')
|
|
3615
|
+
done()
|
|
3616
|
+
})
|
|
3617
|
+
|
|
3618
|
+
fastify.get('/', async () => ({ ok: true }))
|
|
3619
|
+
|
|
3620
|
+
const address = await fastify.listen({ port: 0 })
|
|
3621
|
+
const result = await fetch(address)
|
|
3622
|
+
t.assert.ok(result.ok)
|
|
3623
|
+
t.assert.strictEqual(result.status, 200)
|
|
3624
|
+
})
|
|
3625
|
+
|
|
3626
|
+
test('socket._meta and timeout listener are cleared on reply.hijack() when onTimeout is registered', async t => {
|
|
3627
|
+
// reply.hijack() opts out of Fastify's response lifecycle so onResFinished
|
|
3628
|
+
// never fires. Before this fix, socket._meta was left pointing at the
|
|
3629
|
+
// request/reply objects for the full keep-alive socket lifetime when
|
|
3630
|
+
// an onTimeout hook was registered.
|
|
3631
|
+
t.plan(3)
|
|
3632
|
+
|
|
3633
|
+
const net = require('node:net')
|
|
3634
|
+
const fastify = Fastify({ connectionTimeout: 300 })
|
|
3635
|
+
t.after(() => fastify.close())
|
|
3636
|
+
|
|
3637
|
+
fastify.addHook('onTimeout', function (req, reply, done) { done() })
|
|
3638
|
+
|
|
3639
|
+
let metaAfterHijack = 'not-set'
|
|
3640
|
+
let hasFastifyTimeoutListenerAfterHijack = true
|
|
3641
|
+
fastify.get('/', async (req, reply) => {
|
|
3642
|
+
reply.hijack()
|
|
3643
|
+
// _meta must be cleared synchronously inside hijack()
|
|
3644
|
+
metaAfterHijack = req.raw.socket._meta
|
|
3645
|
+
hasFastifyTimeoutListenerAfterHijack = req.raw.socket.listeners('timeout').some(listener => listener.name === 'handleTimeout')
|
|
3646
|
+
// Write a minimal valid HTTP/1.1 response and close
|
|
3647
|
+
reply.raw.write('HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok')
|
|
3648
|
+
reply.raw.end()
|
|
3649
|
+
return reply
|
|
3650
|
+
})
|
|
3651
|
+
|
|
3652
|
+
await fastify.listen({ port: 0 })
|
|
3653
|
+
|
|
3654
|
+
// Use raw TCP so we are not affected by fetch() rejecting a closed socket.
|
|
3655
|
+
// Extract host/port from the server address object to handle IPv6 (::1) on Windows.
|
|
3656
|
+
const { port, address: host } = fastify.server.address()
|
|
3657
|
+
await new Promise((resolve, reject) => {
|
|
3658
|
+
const socket = net.connect(port, host, () => {
|
|
3659
|
+
socket.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
|
|
3660
|
+
})
|
|
3661
|
+
socket.on('data', () => {})
|
|
3662
|
+
socket.on('close', resolve)
|
|
3663
|
+
socket.on('error', reject)
|
|
3664
|
+
})
|
|
3665
|
+
|
|
3666
|
+
t.assert.ok(true, 'no crash during hijacked request with onTimeout registered')
|
|
3667
|
+
t.assert.ok(
|
|
3668
|
+
metaAfterHijack == null,
|
|
3669
|
+
`socket._meta must be null or undefined after reply.hijack() — got: ${JSON.stringify(metaAfterHijack)}`
|
|
3670
|
+
)
|
|
3671
|
+
t.assert.strictEqual(hasFastifyTimeoutListenerAfterHijack, false, 'Fastify timeout listener must be removed after reply.hijack()')
|
|
3672
|
+
})
|
|
@@ -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
|
-
const expectedErrors =
|
|
8
|
+
const expectedErrors = 94
|
|
9
9
|
|
|
10
10
|
test(`should expose ${expectedErrors} errors`, t => {
|
|
11
11
|
t.plan(1)
|
|
@@ -770,6 +770,26 @@ test('FST_ERR_ROUTE_REWRITE_NOT_STR', t => {
|
|
|
770
770
|
t.assert.ok(error instanceof TypeError)
|
|
771
771
|
})
|
|
772
772
|
|
|
773
|
+
test('FST_ERR_ROUTE_MISSING_CONTENT_TYPE', t => {
|
|
774
|
+
t.plan(5)
|
|
775
|
+
const error = new errors.FST_ERR_ROUTE_MISSING_CONTENT_TYPE()
|
|
776
|
+
t.assert.strictEqual(error.name, 'FastifyError')
|
|
777
|
+
t.assert.strictEqual(error.code, 'FST_ERR_ROUTE_MISSING_CONTENT_TYPE')
|
|
778
|
+
t.assert.strictEqual(error.message, "Method '%s' must provide a 'Content-Type' header.")
|
|
779
|
+
t.assert.strictEqual(error.statusCode, 400)
|
|
780
|
+
t.assert.ok(error instanceof Error)
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
test('FST_ERR_ROUTE_MISSING_CONTENT', t => {
|
|
784
|
+
t.plan(5)
|
|
785
|
+
const error = new errors.FST_ERR_ROUTE_MISSING_CONTENT()
|
|
786
|
+
t.assert.strictEqual(error.name, 'FastifyError')
|
|
787
|
+
t.assert.strictEqual(error.code, 'FST_ERR_ROUTE_MISSING_CONTENT')
|
|
788
|
+
t.assert.strictEqual(error.message, "Method '%s' must provide a request body.")
|
|
789
|
+
t.assert.strictEqual(error.statusCode, 400)
|
|
790
|
+
t.assert.ok(error instanceof Error)
|
|
791
|
+
})
|
|
792
|
+
|
|
773
793
|
test('FST_ERR_REOPENED_CLOSE_SERVER', t => {
|
|
774
794
|
t.plan(5)
|
|
775
795
|
const error = new errors.FST_ERR_REOPENED_CLOSE_SERVER()
|