fastify 5.2.2 → 5.3.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/SPONSORS.md +0 -1
- package/docs/Guides/Ecosystem.md +2 -0
- package/docs/Reference/Decorators.md +199 -0
- package/docs/Reference/Errors.md +2 -0
- package/fastify.js +3 -2
- package/lib/decorate.js +18 -3
- package/lib/errors.js +4 -0
- package/lib/reply.js +17 -2
- package/lib/request.js +28 -2
- package/lib/validation.js +11 -1
- package/package.json +3 -3
- package/test/build/error-serializer.test.js +1 -2
- package/test/custom-parser.0.test.js +160 -129
- package/test/custom-parser.1.test.js +77 -63
- package/test/custom-parser.4.test.js +55 -38
- package/test/custom-querystring-parser.test.js +46 -28
- package/test/decorator.test.js +174 -4
- package/test/fastify-instance.test.js +12 -2
- package/test/hooks.on-listen.test.js +17 -14
- package/test/internals/errors.test.js +14 -1
- package/test/listen.2.test.js +4 -1
- package/test/logger/instantiation.test.js +89 -96
- package/test/logger/logging.test.js +116 -120
- package/test/logger/options.test.js +97 -99
- package/test/logger/request.test.js +66 -66
- package/test/schema-validation.test.js +120 -0
- package/test/server.test.js +175 -0
- package/test/stream.4.test.js +38 -33
- package/test/toolkit.js +31 -0
- package/test/types/instance.test-d.ts +3 -0
- package/test/types/reply.test-d.ts +1 -0
- package/test/types/request.test-d.ts +4 -0
- package/test/types/type-provider.test-d.ts +40 -0
- package/test/upgrade.test.js +3 -6
- package/types/instance.d.ts +2 -0
- package/types/reply.d.ts +1 -0
- package/types/request.d.ts +2 -0
- package/types/type-provider.d.ts +12 -3
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const stream = require('node:stream')
|
|
4
4
|
|
|
5
|
-
const t = require('
|
|
5
|
+
const t = require('node:test')
|
|
6
6
|
const split = require('split2')
|
|
7
7
|
|
|
8
8
|
const Fastify = require('../../fastify')
|
|
9
9
|
const helper = require('../helper')
|
|
10
10
|
const { on } = stream
|
|
11
11
|
const { request } = require('./logger-test-utils')
|
|
12
|
+
const { partialDeepStrictEqual } = require('../toolkit')
|
|
12
13
|
|
|
13
|
-
t.test('request', (t) => {
|
|
14
|
-
t.setTimeout(60000)
|
|
15
|
-
|
|
14
|
+
t.test('request', { timeout: 60000 }, async (t) => {
|
|
16
15
|
let localhost
|
|
17
16
|
|
|
18
17
|
t.plan(7)
|
|
@@ -20,7 +19,7 @@ t.test('request', (t) => {
|
|
|
20
19
|
[localhost] = await helper.getLoopbackHost()
|
|
21
20
|
})
|
|
22
21
|
|
|
23
|
-
t.test('The request id header key can be customized', async (t) => {
|
|
22
|
+
await t.test('The request id header key can be customized', async (t) => {
|
|
24
23
|
const lines = ['incoming request', 'some log message', 'request completed']
|
|
25
24
|
t.plan(lines.length * 2 + 2)
|
|
26
25
|
const REQUEST_ID = '42'
|
|
@@ -30,26 +29,26 @@ t.test('request', (t) => {
|
|
|
30
29
|
logger: { stream, level: 'info' },
|
|
31
30
|
requestIdHeader: 'my-custom-request-id'
|
|
32
31
|
})
|
|
33
|
-
t.
|
|
32
|
+
t.after(() => fastify.close())
|
|
34
33
|
|
|
35
34
|
fastify.get('/', (req, reply) => {
|
|
36
|
-
t.
|
|
35
|
+
t.assert.strictEqual(req.id, REQUEST_ID)
|
|
37
36
|
req.log.info('some log message')
|
|
38
37
|
reply.send({ id: req.id })
|
|
39
38
|
})
|
|
40
39
|
|
|
41
40
|
const response = await fastify.inject({ method: 'GET', url: '/', headers: { 'my-custom-request-id': REQUEST_ID } })
|
|
42
41
|
const body = await response.json()
|
|
43
|
-
t.
|
|
42
|
+
t.assert.strictEqual(body.id, REQUEST_ID)
|
|
44
43
|
|
|
45
44
|
for await (const [line] of on(stream, 'data')) {
|
|
46
|
-
t.
|
|
47
|
-
t.
|
|
45
|
+
t.assert.strictEqual(line.reqId, REQUEST_ID)
|
|
46
|
+
t.assert.strictEqual(line.msg, lines.shift(), 'message is set')
|
|
48
47
|
if (lines.length === 0) break
|
|
49
48
|
}
|
|
50
49
|
})
|
|
51
50
|
|
|
52
|
-
t.test('The request id header key can be ignored', async (t) => {
|
|
51
|
+
await t.test('The request id header key can be ignored', async (t) => {
|
|
53
52
|
const lines = ['incoming request', 'some log message', 'request completed']
|
|
54
53
|
t.plan(lines.length * 2 + 2)
|
|
55
54
|
const REQUEST_ID = 'ignore-me'
|
|
@@ -59,33 +58,33 @@ t.test('request', (t) => {
|
|
|
59
58
|
logger: { stream, level: 'info' },
|
|
60
59
|
requestIdHeader: false
|
|
61
60
|
})
|
|
62
|
-
t.
|
|
61
|
+
t.after(() => fastify.close())
|
|
63
62
|
|
|
64
63
|
fastify.get('/', (req, reply) => {
|
|
65
|
-
t.
|
|
64
|
+
t.assert.strictEqual(req.id, 'req-1')
|
|
66
65
|
req.log.info('some log message')
|
|
67
66
|
reply.send({ id: req.id })
|
|
68
67
|
})
|
|
69
68
|
const response = await fastify.inject({ method: 'GET', url: '/', headers: { 'request-id': REQUEST_ID } })
|
|
70
69
|
const body = await response.json()
|
|
71
|
-
t.
|
|
70
|
+
t.assert.strictEqual(body.id, 'req-1')
|
|
72
71
|
|
|
73
72
|
for await (const [line] of on(stream, 'data')) {
|
|
74
|
-
t.
|
|
75
|
-
t.
|
|
73
|
+
t.assert.strictEqual(line.reqId, 'req-1')
|
|
74
|
+
t.assert.strictEqual(line.msg, lines.shift(), 'message is set')
|
|
76
75
|
if (lines.length === 0) break
|
|
77
76
|
}
|
|
78
77
|
})
|
|
79
78
|
|
|
80
|
-
t.test('The request id header key can be customized along with a custom id generator', async (t) => {
|
|
79
|
+
await t.test('The request id header key can be customized along with a custom id generator', async (t) => {
|
|
81
80
|
const REQUEST_ID = '42'
|
|
82
81
|
const matches = [
|
|
83
|
-
{ reqId: REQUEST_ID, msg:
|
|
84
|
-
{ reqId: REQUEST_ID, msg:
|
|
85
|
-
{ reqId: REQUEST_ID, msg:
|
|
86
|
-
{ reqId: 'foo', msg:
|
|
87
|
-
{ reqId: 'foo', msg:
|
|
88
|
-
{ reqId: 'foo', msg:
|
|
82
|
+
{ reqId: REQUEST_ID, msg: 'incoming request' },
|
|
83
|
+
{ reqId: REQUEST_ID, msg: 'some log message' },
|
|
84
|
+
{ reqId: REQUEST_ID, msg: 'request completed' },
|
|
85
|
+
{ reqId: 'foo', msg: 'incoming request' },
|
|
86
|
+
{ reqId: 'foo', msg: 'some log message 2' },
|
|
87
|
+
{ reqId: 'foo', msg: 'request completed' }
|
|
89
88
|
]
|
|
90
89
|
t.plan(matches.length + 4)
|
|
91
90
|
|
|
@@ -97,16 +96,16 @@ t.test('request', (t) => {
|
|
|
97
96
|
return 'foo'
|
|
98
97
|
}
|
|
99
98
|
})
|
|
100
|
-
t.
|
|
99
|
+
t.after(() => fastify.close())
|
|
101
100
|
|
|
102
101
|
fastify.get('/one', (req, reply) => {
|
|
103
|
-
t.
|
|
102
|
+
t.assert.strictEqual(req.id, REQUEST_ID)
|
|
104
103
|
req.log.info('some log message')
|
|
105
104
|
reply.send({ id: req.id })
|
|
106
105
|
})
|
|
107
106
|
|
|
108
107
|
fastify.get('/two', (req, reply) => {
|
|
109
|
-
t.
|
|
108
|
+
t.assert.strictEqual(req.id, 'foo')
|
|
110
109
|
req.log.info('some log message 2')
|
|
111
110
|
reply.send({ id: req.id })
|
|
112
111
|
})
|
|
@@ -114,30 +113,30 @@ t.test('request', (t) => {
|
|
|
114
113
|
{
|
|
115
114
|
const response = await fastify.inject({ method: 'GET', url: '/one', headers: { 'my-custom-request-id': REQUEST_ID } })
|
|
116
115
|
const body = await response.json()
|
|
117
|
-
t.
|
|
116
|
+
t.assert.strictEqual(body.id, REQUEST_ID)
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
{
|
|
121
120
|
const response = await fastify.inject({ method: 'GET', url: '/two' })
|
|
122
121
|
const body = await response.json()
|
|
123
|
-
t.
|
|
122
|
+
t.assert.strictEqual(body.id, 'foo')
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
for await (const [line] of on(stream, 'data')) {
|
|
127
|
-
t.
|
|
126
|
+
t.assert.ok(partialDeepStrictEqual(line, matches.shift()))
|
|
128
127
|
if (matches.length === 0) break
|
|
129
128
|
}
|
|
130
129
|
})
|
|
131
130
|
|
|
132
|
-
t.test('The request id header key can be ignored along with a custom id generator', async (t) => {
|
|
131
|
+
await t.test('The request id header key can be ignored along with a custom id generator', async (t) => {
|
|
133
132
|
const REQUEST_ID = 'ignore-me'
|
|
134
133
|
const matches = [
|
|
135
|
-
{ reqId: 'foo', msg:
|
|
136
|
-
{ reqId: 'foo', msg:
|
|
137
|
-
{ reqId: 'foo', msg:
|
|
138
|
-
{ reqId: 'foo', msg:
|
|
139
|
-
{ reqId: 'foo', msg:
|
|
140
|
-
{ reqId: 'foo', msg:
|
|
134
|
+
{ reqId: 'foo', msg: 'incoming request' },
|
|
135
|
+
{ reqId: 'foo', msg: 'some log message' },
|
|
136
|
+
{ reqId: 'foo', msg: 'request completed' },
|
|
137
|
+
{ reqId: 'foo', msg: 'incoming request' },
|
|
138
|
+
{ reqId: 'foo', msg: 'some log message 2' },
|
|
139
|
+
{ reqId: 'foo', msg: 'request completed' }
|
|
141
140
|
]
|
|
142
141
|
t.plan(matches.length + 4)
|
|
143
142
|
|
|
@@ -149,16 +148,16 @@ t.test('request', (t) => {
|
|
|
149
148
|
return 'foo'
|
|
150
149
|
}
|
|
151
150
|
})
|
|
152
|
-
t.
|
|
151
|
+
t.after(() => fastify.close())
|
|
153
152
|
|
|
154
153
|
fastify.get('/one', (req, reply) => {
|
|
155
|
-
t.
|
|
154
|
+
t.assert.strictEqual(req.id, 'foo')
|
|
156
155
|
req.log.info('some log message')
|
|
157
156
|
reply.send({ id: req.id })
|
|
158
157
|
})
|
|
159
158
|
|
|
160
159
|
fastify.get('/two', (req, reply) => {
|
|
161
|
-
t.
|
|
160
|
+
t.assert.strictEqual(req.id, 'foo')
|
|
162
161
|
req.log.info('some log message 2')
|
|
163
162
|
reply.send({ id: req.id })
|
|
164
163
|
})
|
|
@@ -166,27 +165,27 @@ t.test('request', (t) => {
|
|
|
166
165
|
{
|
|
167
166
|
const response = await fastify.inject({ method: 'GET', url: '/one', headers: { 'request-id': REQUEST_ID } })
|
|
168
167
|
const body = await response.json()
|
|
169
|
-
t.
|
|
168
|
+
t.assert.strictEqual(body.id, 'foo')
|
|
170
169
|
}
|
|
171
170
|
|
|
172
171
|
{
|
|
173
172
|
const response = await fastify.inject({ method: 'GET', url: '/two' })
|
|
174
173
|
const body = await response.json()
|
|
175
|
-
t.
|
|
174
|
+
t.assert.strictEqual(body.id, 'foo')
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
for await (const [line] of on(stream, 'data')) {
|
|
179
|
-
t.
|
|
178
|
+
t.assert.ok(partialDeepStrictEqual(line, matches.shift()))
|
|
180
179
|
if (matches.length === 0) break
|
|
181
180
|
}
|
|
182
181
|
})
|
|
183
182
|
|
|
184
|
-
t.test('The request id log label can be changed', async (t) => {
|
|
183
|
+
await t.test('The request id log label can be changed', async (t) => {
|
|
185
184
|
const REQUEST_ID = '42'
|
|
186
185
|
const matches = [
|
|
187
|
-
{ traceId: REQUEST_ID, msg:
|
|
188
|
-
{ traceId: REQUEST_ID, msg:
|
|
189
|
-
{ traceId: REQUEST_ID, msg:
|
|
186
|
+
{ traceId: REQUEST_ID, msg: 'incoming request' },
|
|
187
|
+
{ traceId: REQUEST_ID, msg: 'some log message' },
|
|
188
|
+
{ traceId: REQUEST_ID, msg: 'request completed' }
|
|
190
189
|
]
|
|
191
190
|
t.plan(matches.length + 2)
|
|
192
191
|
|
|
@@ -196,10 +195,10 @@ t.test('request', (t) => {
|
|
|
196
195
|
requestIdHeader: 'my-custom-request-id',
|
|
197
196
|
requestIdLogLabel: 'traceId'
|
|
198
197
|
})
|
|
199
|
-
t.
|
|
198
|
+
t.after(() => fastify.close())
|
|
200
199
|
|
|
201
200
|
fastify.get('/one', (req, reply) => {
|
|
202
|
-
t.
|
|
201
|
+
t.assert.strictEqual(req.id, REQUEST_ID)
|
|
203
202
|
req.log.info('some log message')
|
|
204
203
|
reply.send({ id: req.id })
|
|
205
204
|
})
|
|
@@ -207,22 +206,16 @@ t.test('request', (t) => {
|
|
|
207
206
|
{
|
|
208
207
|
const response = await fastify.inject({ method: 'GET', url: '/one', headers: { 'my-custom-request-id': REQUEST_ID } })
|
|
209
208
|
const body = await response.json()
|
|
210
|
-
t.
|
|
209
|
+
t.assert.strictEqual(body.id, REQUEST_ID)
|
|
211
210
|
}
|
|
212
211
|
|
|
213
212
|
for await (const [line] of on(stream, 'data')) {
|
|
214
|
-
t.
|
|
213
|
+
t.assert.ok(partialDeepStrictEqual(line, matches.shift()))
|
|
215
214
|
if (matches.length === 0) break
|
|
216
215
|
}
|
|
217
216
|
})
|
|
218
217
|
|
|
219
|
-
t.test('should redact the authorization header if so specified', async (t) => {
|
|
220
|
-
const lines = [
|
|
221
|
-
{ msg: /Server listening at/ },
|
|
222
|
-
{ req: { headers: { authorization: '[Redacted]' } }, msg: 'incoming request' },
|
|
223
|
-
{ res: { statusCode: 200 }, msg: 'request completed' }
|
|
224
|
-
]
|
|
225
|
-
t.plan(lines.length + 3)
|
|
218
|
+
await t.test('should redact the authorization header if so specified', async (t) => {
|
|
226
219
|
const stream = split(JSON.parse)
|
|
227
220
|
const fastify = Fastify({
|
|
228
221
|
logger: {
|
|
@@ -243,15 +236,22 @@ t.test('request', (t) => {
|
|
|
243
236
|
}
|
|
244
237
|
}
|
|
245
238
|
})
|
|
246
|
-
t.
|
|
239
|
+
t.after(() => fastify.close())
|
|
247
240
|
|
|
248
241
|
fastify.get('/', function (req, reply) {
|
|
249
|
-
t.
|
|
242
|
+
t.assert.deepStrictEqual(req.headers.authorization, 'Bearer abcde')
|
|
250
243
|
reply.send({ hello: 'world' })
|
|
251
244
|
})
|
|
252
245
|
|
|
253
246
|
await fastify.ready()
|
|
254
|
-
await fastify.listen({ port: 0, host: localhost })
|
|
247
|
+
const server = await fastify.listen({ port: 0, host: localhost })
|
|
248
|
+
|
|
249
|
+
const lines = [
|
|
250
|
+
{ msg: `Server listening at ${server}` },
|
|
251
|
+
{ req: { headers: { authorization: '[Redacted]' } }, msg: 'incoming request' },
|
|
252
|
+
{ res: { statusCode: 200 }, msg: 'request completed' }
|
|
253
|
+
]
|
|
254
|
+
t.plan(lines.length + 3)
|
|
255
255
|
|
|
256
256
|
await request({
|
|
257
257
|
method: 'GET',
|
|
@@ -262,17 +262,17 @@ t.test('request', (t) => {
|
|
|
262
262
|
authorization: 'Bearer abcde'
|
|
263
263
|
}
|
|
264
264
|
}, function (response, body) {
|
|
265
|
-
t.
|
|
266
|
-
t.
|
|
265
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
266
|
+
t.assert.deepStrictEqual(body, JSON.stringify({ hello: 'world' }))
|
|
267
267
|
})
|
|
268
268
|
|
|
269
269
|
for await (const [line] of on(stream, 'data')) {
|
|
270
|
-
t.
|
|
270
|
+
t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
|
|
271
271
|
if (lines.length === 0) break
|
|
272
272
|
}
|
|
273
273
|
})
|
|
274
274
|
|
|
275
|
-
t.test('should not throw error when serializing custom req', (t) => {
|
|
275
|
+
await t.test('should not throw error when serializing custom req', (t) => {
|
|
276
276
|
t.plan(1)
|
|
277
277
|
|
|
278
278
|
const lines = []
|
|
@@ -283,10 +283,10 @@ t.test('request', (t) => {
|
|
|
283
283
|
}
|
|
284
284
|
})
|
|
285
285
|
const fastify = Fastify({ logger: { level: 'info', stream: dest } })
|
|
286
|
-
t.
|
|
286
|
+
t.after(() => fastify.close())
|
|
287
287
|
|
|
288
288
|
fastify.log.info({ req: {} })
|
|
289
289
|
|
|
290
|
-
t.
|
|
290
|
+
t.assert.deepStrictEqual(lines[0].req, {})
|
|
291
291
|
})
|
|
292
292
|
})
|
|
@@ -1300,3 +1300,123 @@ test('Custom validator builder override by custom validator compiler in child in
|
|
|
1300
1300
|
})
|
|
1301
1301
|
t.equal(two.statusCode, 200)
|
|
1302
1302
|
})
|
|
1303
|
+
|
|
1304
|
+
test('Schema validation when no content type is provided', async t => {
|
|
1305
|
+
// this case should not be happened in normal use-case,
|
|
1306
|
+
// it is added for the completeness of code branch
|
|
1307
|
+
const fastify = Fastify()
|
|
1308
|
+
|
|
1309
|
+
fastify.post('/', {
|
|
1310
|
+
schema: {
|
|
1311
|
+
body: {
|
|
1312
|
+
content: {
|
|
1313
|
+
'application/json': {
|
|
1314
|
+
schema: {
|
|
1315
|
+
type: 'object',
|
|
1316
|
+
properties: {
|
|
1317
|
+
foo: { type: 'string' }
|
|
1318
|
+
},
|
|
1319
|
+
required: ['foo'],
|
|
1320
|
+
additionalProperties: false
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
preValidation: async (request) => {
|
|
1327
|
+
request.headers['content-type'] = undefined
|
|
1328
|
+
}
|
|
1329
|
+
}, async () => 'ok')
|
|
1330
|
+
|
|
1331
|
+
await fastify.ready()
|
|
1332
|
+
|
|
1333
|
+
const invalid = await fastify.inject({
|
|
1334
|
+
method: 'POST',
|
|
1335
|
+
url: '/',
|
|
1336
|
+
headers: {
|
|
1337
|
+
'content-type': 'application/json'
|
|
1338
|
+
},
|
|
1339
|
+
body: { invalid: 'string' }
|
|
1340
|
+
})
|
|
1341
|
+
t.equal(invalid.statusCode, 200)
|
|
1342
|
+
})
|
|
1343
|
+
|
|
1344
|
+
test('Schema validation will not be bypass by different content type', async t => {
|
|
1345
|
+
t.plan(8)
|
|
1346
|
+
|
|
1347
|
+
const fastify = Fastify()
|
|
1348
|
+
|
|
1349
|
+
fastify.post('/', {
|
|
1350
|
+
schema: {
|
|
1351
|
+
body: {
|
|
1352
|
+
content: {
|
|
1353
|
+
'application/json': {
|
|
1354
|
+
schema: {
|
|
1355
|
+
type: 'object',
|
|
1356
|
+
properties: {
|
|
1357
|
+
foo: { type: 'string' }
|
|
1358
|
+
},
|
|
1359
|
+
required: ['foo'],
|
|
1360
|
+
additionalProperties: false
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}, async () => 'ok')
|
|
1367
|
+
|
|
1368
|
+
await fastify.ready()
|
|
1369
|
+
|
|
1370
|
+
const correct1 = await fastify.inject({
|
|
1371
|
+
method: 'POST',
|
|
1372
|
+
url: '/',
|
|
1373
|
+
headers: {
|
|
1374
|
+
'content-type': 'application/json'
|
|
1375
|
+
},
|
|
1376
|
+
body: { foo: 'string' }
|
|
1377
|
+
})
|
|
1378
|
+
t.equal(correct1.statusCode, 200)
|
|
1379
|
+
|
|
1380
|
+
const correct2 = await fastify.inject({
|
|
1381
|
+
method: 'POST',
|
|
1382
|
+
url: '/',
|
|
1383
|
+
headers: {
|
|
1384
|
+
'content-type': 'application/json; charset=utf-8'
|
|
1385
|
+
},
|
|
1386
|
+
body: { foo: 'string' }
|
|
1387
|
+
})
|
|
1388
|
+
t.equal(correct2.statusCode, 200)
|
|
1389
|
+
|
|
1390
|
+
const invalid1 = await fastify.inject({
|
|
1391
|
+
method: 'POST',
|
|
1392
|
+
url: '/',
|
|
1393
|
+
headers: {
|
|
1394
|
+
'content-type': 'application/json ;'
|
|
1395
|
+
},
|
|
1396
|
+
body: { invalid: 'string' }
|
|
1397
|
+
})
|
|
1398
|
+
t.equal(invalid1.statusCode, 400)
|
|
1399
|
+
t.equal(invalid1.json().code, 'FST_ERR_VALIDATION')
|
|
1400
|
+
|
|
1401
|
+
const invalid2 = await fastify.inject({
|
|
1402
|
+
method: 'POST',
|
|
1403
|
+
url: '/',
|
|
1404
|
+
headers: {
|
|
1405
|
+
'content-type': 'ApPlIcAtIoN/JsOn;'
|
|
1406
|
+
},
|
|
1407
|
+
body: { invalid: 'string' }
|
|
1408
|
+
})
|
|
1409
|
+
t.equal(invalid2.statusCode, 400)
|
|
1410
|
+
t.equal(invalid2.json().code, 'FST_ERR_VALIDATION')
|
|
1411
|
+
|
|
1412
|
+
const invalid3 = await fastify.inject({
|
|
1413
|
+
method: 'POST',
|
|
1414
|
+
url: '/',
|
|
1415
|
+
headers: {
|
|
1416
|
+
'content-type': 'ApPlIcAtIoN/JsOn ;'
|
|
1417
|
+
},
|
|
1418
|
+
body: { invalid: 'string' }
|
|
1419
|
+
})
|
|
1420
|
+
t.equal(invalid3.statusCode, 400)
|
|
1421
|
+
t.equal(invalid3.json().code, 'FST_ERR_VALIDATION')
|
|
1422
|
+
})
|
package/test/server.test.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const undici = require('undici')
|
|
5
7
|
|
|
6
8
|
test('listen should accept null port', async t => {
|
|
7
9
|
const fastify = Fastify()
|
|
@@ -11,3 +13,176 @@ test('listen should accept null port', async t => {
|
|
|
11
13
|
fastify.listen({ port: null })
|
|
12
14
|
)
|
|
13
15
|
})
|
|
16
|
+
|
|
17
|
+
test('listen should accept undefined port', async t => {
|
|
18
|
+
const fastify = Fastify()
|
|
19
|
+
t.after(() => fastify.close())
|
|
20
|
+
|
|
21
|
+
await t.assert.doesNotReject(
|
|
22
|
+
fastify.listen({ port: undefined })
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('listen should accept stringified number port', async t => {
|
|
27
|
+
const fastify = Fastify()
|
|
28
|
+
t.after(() => fastify.close())
|
|
29
|
+
|
|
30
|
+
await t.assert.doesNotReject(
|
|
31
|
+
fastify.listen({ port: '1234' })
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('listen should accept log text resolution function', async t => {
|
|
36
|
+
const fastify = Fastify()
|
|
37
|
+
t.after(() => fastify.close())
|
|
38
|
+
|
|
39
|
+
await t.assert.doesNotReject(
|
|
40
|
+
fastify.listen({
|
|
41
|
+
host: '127.0.0.1',
|
|
42
|
+
port: '1234',
|
|
43
|
+
listenTextResolver: (address) => {
|
|
44
|
+
t.assert.strictEqual(address, 'http://127.0.0.1:1234')
|
|
45
|
+
return 'hardcoded text'
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('listen should reject string port', async (t) => {
|
|
52
|
+
const fastify = Fastify()
|
|
53
|
+
t.after(() => fastify.close())
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
await fastify.listen({ port: 'hello-world' })
|
|
57
|
+
} catch (error) {
|
|
58
|
+
t.assert.strictEqual(error.code, 'ERR_SOCKET_BAD_PORT')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await fastify.listen({ port: '1234hello' })
|
|
63
|
+
} catch (error) {
|
|
64
|
+
t.assert.strictEqual(error.code, 'ERR_SOCKET_BAD_PORT')
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('Test for hostname and port', (t, end) => {
|
|
69
|
+
const app = Fastify()
|
|
70
|
+
t.after(() => app.close())
|
|
71
|
+
app.get('/host', (req, res) => {
|
|
72
|
+
const host = 'localhost:8000'
|
|
73
|
+
t.assert.strictEqual(req.host, host)
|
|
74
|
+
t.assert.strictEqual(req.hostname, req.host.split(':')[0])
|
|
75
|
+
t.assert.strictEqual(req.port, Number(req.host.split(':')[1]))
|
|
76
|
+
res.send('ok')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
app.listen({ port: 8000 }, () => {
|
|
80
|
+
sget('http://localhost:8000/host', () => { end() })
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('abort signal', async t => {
|
|
85
|
+
await t.test('listen should not start server', (t, end) => {
|
|
86
|
+
t.plan(2)
|
|
87
|
+
function onClose (instance, done) {
|
|
88
|
+
t.assert.strictEqual(instance, fastify)
|
|
89
|
+
done()
|
|
90
|
+
end()
|
|
91
|
+
}
|
|
92
|
+
const controller = new AbortController()
|
|
93
|
+
|
|
94
|
+
const fastify = Fastify()
|
|
95
|
+
fastify.addHook('onClose', onClose)
|
|
96
|
+
fastify.listen({ port: 1234, signal: controller.signal }, (err) => {
|
|
97
|
+
t.assert.ifError(err)
|
|
98
|
+
})
|
|
99
|
+
controller.abort()
|
|
100
|
+
t.assert.strictEqual(fastify.server.listening, false)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
await t.test('listen should not start server if already aborted', (t, end) => {
|
|
104
|
+
t.plan(2)
|
|
105
|
+
function onClose (instance, done) {
|
|
106
|
+
t.assert.strictEqual(instance, fastify)
|
|
107
|
+
done()
|
|
108
|
+
end()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const controller = new AbortController()
|
|
112
|
+
controller.abort()
|
|
113
|
+
const fastify = Fastify()
|
|
114
|
+
fastify.addHook('onClose', onClose)
|
|
115
|
+
fastify.listen({ port: 1234, signal: controller.signal }, (err) => {
|
|
116
|
+
t.assert.ifError(err)
|
|
117
|
+
})
|
|
118
|
+
t.assert.strictEqual(fastify.server.listening, false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
await t.test('listen should throw if received invalid signal', t => {
|
|
122
|
+
t.plan(2)
|
|
123
|
+
const fastify = Fastify()
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
fastify.listen({ port: 1234, signal: {} }, (err) => {
|
|
127
|
+
t.assert.ifError(err)
|
|
128
|
+
})
|
|
129
|
+
t.assert.fail('should throw')
|
|
130
|
+
} catch (e) {
|
|
131
|
+
t.assert.strictEqual(e.code, 'FST_ERR_LISTEN_OPTIONS_INVALID')
|
|
132
|
+
t.assert.strictEqual(e.message, 'Invalid listen options: \'Invalid options.signal\'')
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('#5180 - preClose should be called before closing secondary server', async (t) => {
|
|
138
|
+
t.plan(2)
|
|
139
|
+
const fastify = Fastify({ forceCloseConnections: true })
|
|
140
|
+
let flag = false
|
|
141
|
+
t.after(() => fastify.close())
|
|
142
|
+
|
|
143
|
+
fastify.addHook('preClose', () => {
|
|
144
|
+
flag = true
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
fastify.get('/', async (req, reply) => {
|
|
148
|
+
// request will be pending for 1 second to simulate a slow request
|
|
149
|
+
await new Promise((resolve) => { setTimeout(resolve, 1000) })
|
|
150
|
+
return { hello: 'world' }
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
fastify.listen({ port: 0 }, (err) => {
|
|
154
|
+
t.assert.ifError(err)
|
|
155
|
+
const addresses = fastify.addresses()
|
|
156
|
+
const mainServerAddress = fastify.server.address()
|
|
157
|
+
let secondaryAddress
|
|
158
|
+
for (const addr of addresses) {
|
|
159
|
+
if (addr.family !== mainServerAddress.family) {
|
|
160
|
+
secondaryAddress = addr
|
|
161
|
+
secondaryAddress.address = secondaryAddress.family === 'IPv6'
|
|
162
|
+
? `[${secondaryAddress.address}]`
|
|
163
|
+
: secondaryAddress.address
|
|
164
|
+
break
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!secondaryAddress) {
|
|
169
|
+
t.assert.ok(true, 'Secondary address not found')
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
undici.request(`http://${secondaryAddress.address}:${secondaryAddress.port}/`)
|
|
174
|
+
.then(
|
|
175
|
+
() => { t.assert.fail('Request should not succeed') },
|
|
176
|
+
() => {
|
|
177
|
+
t.assert.ok(flag)
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Close the server while the slow request is pending
|
|
182
|
+
setTimeout(fastify.close, 250)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// Wait 1000ms to ensure that the test is finished and async operations are
|
|
186
|
+
// completed
|
|
187
|
+
await new Promise((resolve) => { setTimeout(resolve, 1000) })
|
|
188
|
+
})
|