fastify 5.2.2 → 5.3.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/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/package.json +3 -3
- package/test/build/error-serializer.test.js +1 -2
- package/test/custom-parser.4.test.js +55 -38
- 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/server.test.js +175 -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
- package/.vscode/settings.json +0 -22
package/test/decorator.test.js
CHANGED
|
@@ -925,9 +925,9 @@ test('Request/reply decorators should be able to access the server instance', as
|
|
|
925
925
|
server.decorateRequest('assert', rootAssert)
|
|
926
926
|
server.decorateReply('assert', rootAssert)
|
|
927
927
|
|
|
928
|
-
server.get('/root-assert', async (req,
|
|
928
|
+
server.get('/root-assert', async (req, res) => {
|
|
929
929
|
req.assert()
|
|
930
|
-
|
|
930
|
+
res.assert()
|
|
931
931
|
return 'done'
|
|
932
932
|
})
|
|
933
933
|
|
|
@@ -936,9 +936,9 @@ test('Request/reply decorators should be able to access the server instance', as
|
|
|
936
936
|
instance.decorateReply('assert', nestedAssert)
|
|
937
937
|
instance.decorate('foo', 'bar')
|
|
938
938
|
|
|
939
|
-
instance.get('/nested-assert', async (req,
|
|
939
|
+
instance.get('/nested-assert', async (req, res) => {
|
|
940
940
|
req.assert()
|
|
941
|
-
|
|
941
|
+
res.assert()
|
|
942
942
|
return 'done'
|
|
943
943
|
})
|
|
944
944
|
})
|
|
@@ -1260,3 +1260,173 @@ test('chain of decorators on Reply', async (t) => {
|
|
|
1260
1260
|
t.equal(response.body, 'tata')
|
|
1261
1261
|
}
|
|
1262
1262
|
})
|
|
1263
|
+
|
|
1264
|
+
test('getDecorator should return the decorator', t => {
|
|
1265
|
+
t.plan(12)
|
|
1266
|
+
const fastify = Fastify()
|
|
1267
|
+
|
|
1268
|
+
fastify.decorate('root', 'from_root')
|
|
1269
|
+
fastify.decorateRequest('root', 'from_root_request')
|
|
1270
|
+
fastify.decorateReply('root', 'from_root_reply')
|
|
1271
|
+
|
|
1272
|
+
t.equal(fastify.getDecorator('root'), 'from_root')
|
|
1273
|
+
fastify.get('/', async (req, res) => {
|
|
1274
|
+
t.equal(req.getDecorator('root'), 'from_root_request')
|
|
1275
|
+
t.equal(res.getDecorator('root'), 'from_root_reply')
|
|
1276
|
+
|
|
1277
|
+
res.send()
|
|
1278
|
+
})
|
|
1279
|
+
|
|
1280
|
+
fastify.register((child) => {
|
|
1281
|
+
child.decorate('child', 'from_child')
|
|
1282
|
+
|
|
1283
|
+
t.equal(child.getDecorator('child'), 'from_child')
|
|
1284
|
+
t.equal(child.getDecorator('root'), 'from_root')
|
|
1285
|
+
|
|
1286
|
+
child.get('/child', async (req, res) => {
|
|
1287
|
+
t.equal(req.getDecorator('root'), 'from_root_request')
|
|
1288
|
+
t.equal(res.getDecorator('root'), 'from_root_reply')
|
|
1289
|
+
|
|
1290
|
+
res.send()
|
|
1291
|
+
})
|
|
1292
|
+
})
|
|
1293
|
+
|
|
1294
|
+
fastify.ready((err) => {
|
|
1295
|
+
t.error(err)
|
|
1296
|
+
fastify.inject({ url: '/' }, (err, res) => {
|
|
1297
|
+
t.error(err)
|
|
1298
|
+
t.pass()
|
|
1299
|
+
})
|
|
1300
|
+
|
|
1301
|
+
fastify.inject({ url: '/child' }, (err, res) => {
|
|
1302
|
+
t.error(err)
|
|
1303
|
+
t.pass()
|
|
1304
|
+
})
|
|
1305
|
+
})
|
|
1306
|
+
})
|
|
1307
|
+
|
|
1308
|
+
test('getDecorator should return function decorators with expected binded context', t => {
|
|
1309
|
+
t.plan(12)
|
|
1310
|
+
const fastify = Fastify()
|
|
1311
|
+
|
|
1312
|
+
fastify.decorate('a', function () {
|
|
1313
|
+
return this
|
|
1314
|
+
})
|
|
1315
|
+
fastify.decorateRequest('b', function () {
|
|
1316
|
+
return this
|
|
1317
|
+
})
|
|
1318
|
+
fastify.decorateReply('c', function () {
|
|
1319
|
+
return this
|
|
1320
|
+
})
|
|
1321
|
+
|
|
1322
|
+
fastify.register((child) => {
|
|
1323
|
+
child.decorate('a', function () {
|
|
1324
|
+
return this
|
|
1325
|
+
})
|
|
1326
|
+
|
|
1327
|
+
t.same(child.getDecorator('a')(), child)
|
|
1328
|
+
child.get('/child', async (req, res) => {
|
|
1329
|
+
t.same(req.getDecorator('b')(), req)
|
|
1330
|
+
t.same(res.getDecorator('c')(), res)
|
|
1331
|
+
|
|
1332
|
+
res.send()
|
|
1333
|
+
})
|
|
1334
|
+
})
|
|
1335
|
+
|
|
1336
|
+
t.same(fastify.getDecorator('a')(), fastify)
|
|
1337
|
+
fastify.get('/', async (req, res) => {
|
|
1338
|
+
t.same(req.getDecorator('b')(), req)
|
|
1339
|
+
t.same(res.getDecorator('c')(), res)
|
|
1340
|
+
|
|
1341
|
+
res.send()
|
|
1342
|
+
})
|
|
1343
|
+
|
|
1344
|
+
fastify.ready((err) => {
|
|
1345
|
+
t.error(err)
|
|
1346
|
+
fastify.inject({ url: '/' }, (err, res) => {
|
|
1347
|
+
t.error(err)
|
|
1348
|
+
t.pass()
|
|
1349
|
+
})
|
|
1350
|
+
|
|
1351
|
+
fastify.inject({ url: '/child' }, (err, res) => {
|
|
1352
|
+
t.error(err)
|
|
1353
|
+
t.pass()
|
|
1354
|
+
})
|
|
1355
|
+
|
|
1356
|
+
t.pass()
|
|
1357
|
+
})
|
|
1358
|
+
})
|
|
1359
|
+
|
|
1360
|
+
test('getDecorator should only return decorators existing in the scope', t => {
|
|
1361
|
+
t.plan(9)
|
|
1362
|
+
|
|
1363
|
+
function assertsThrowOnUndeclaredDecorator (notDecorated, instanceType) {
|
|
1364
|
+
try {
|
|
1365
|
+
notDecorated.getDecorator('foo')
|
|
1366
|
+
t.fail()
|
|
1367
|
+
} catch (e) {
|
|
1368
|
+
t.same(e.code, 'FST_ERR_DEC_UNDECLARED')
|
|
1369
|
+
t.same(e.message, `No decorator 'foo' has been declared on ${instanceType}.`)
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const fastify = Fastify()
|
|
1374
|
+
fastify.register(child => {
|
|
1375
|
+
child.decorate('foo', true)
|
|
1376
|
+
child.decorateRequest('foo', true)
|
|
1377
|
+
child.decorateReply('foo', true)
|
|
1378
|
+
})
|
|
1379
|
+
|
|
1380
|
+
fastify.get('/', async (req, res) => {
|
|
1381
|
+
assertsThrowOnUndeclaredDecorator(req, 'request')
|
|
1382
|
+
assertsThrowOnUndeclaredDecorator(res, 'reply')
|
|
1383
|
+
|
|
1384
|
+
return { hello: 'world' }
|
|
1385
|
+
})
|
|
1386
|
+
|
|
1387
|
+
fastify.ready((err) => {
|
|
1388
|
+
t.error(err)
|
|
1389
|
+
|
|
1390
|
+
assertsThrowOnUndeclaredDecorator(fastify, 'instance')
|
|
1391
|
+
fastify.inject({ url: '/' }, (err, res) => {
|
|
1392
|
+
t.error(err)
|
|
1393
|
+
t.pass()
|
|
1394
|
+
})
|
|
1395
|
+
})
|
|
1396
|
+
})
|
|
1397
|
+
|
|
1398
|
+
test('Request.setDecorator should update an existing decorator', t => {
|
|
1399
|
+
t.plan(7)
|
|
1400
|
+
const fastify = Fastify()
|
|
1401
|
+
|
|
1402
|
+
fastify.decorateRequest('session', null)
|
|
1403
|
+
fastify.decorateRequest('utility', null)
|
|
1404
|
+
fastify.addHook('onRequest', async (req, reply) => {
|
|
1405
|
+
req.setDecorator('session', { user: 'Jean' })
|
|
1406
|
+
req.setDecorator('utility', function () {
|
|
1407
|
+
return this
|
|
1408
|
+
})
|
|
1409
|
+
try {
|
|
1410
|
+
req.setDecorator('foo', { user: 'Jean' })
|
|
1411
|
+
t.fail()
|
|
1412
|
+
} catch (e) {
|
|
1413
|
+
t.same(e.code, 'FST_ERR_DEC_UNDECLARED')
|
|
1414
|
+
t.same(e.message, "No decorator 'foo' has been declared on request.")
|
|
1415
|
+
}
|
|
1416
|
+
})
|
|
1417
|
+
|
|
1418
|
+
fastify.get('/', async (req, res) => {
|
|
1419
|
+
t.strictSame(req.getDecorator('session'), { user: 'Jean' })
|
|
1420
|
+
t.same(req.getDecorator('utility')(), req)
|
|
1421
|
+
|
|
1422
|
+
res.send()
|
|
1423
|
+
})
|
|
1424
|
+
|
|
1425
|
+
fastify.ready((err) => {
|
|
1426
|
+
t.error(err)
|
|
1427
|
+
fastify.inject({ url: '/' }, (err, res) => {
|
|
1428
|
+
t.error(err)
|
|
1429
|
+
t.pass()
|
|
1430
|
+
})
|
|
1431
|
+
})
|
|
1432
|
+
})
|
|
@@ -7,9 +7,12 @@ const os = require('node:os')
|
|
|
7
7
|
const {
|
|
8
8
|
kOptions,
|
|
9
9
|
kErrorHandler,
|
|
10
|
-
kChildLoggerFactory
|
|
10
|
+
kChildLoggerFactory,
|
|
11
|
+
kState
|
|
11
12
|
} = require('../lib/symbols')
|
|
12
13
|
|
|
14
|
+
const isIPv6Missing = !Object.values(os.networkInterfaces()).flat().some(({ family }) => family === 'IPv6')
|
|
15
|
+
|
|
13
16
|
test('root fastify instance is an object', t => {
|
|
14
17
|
t.plan(1)
|
|
15
18
|
t.assert.strictEqual(typeof Fastify(), 'object')
|
|
@@ -279,7 +282,7 @@ test('fastify instance should contains listeningOrigin property (unix socket)',
|
|
|
279
282
|
await fastify.close()
|
|
280
283
|
})
|
|
281
284
|
|
|
282
|
-
test('fastify instance should contains listeningOrigin property (IPv6)', async t => {
|
|
285
|
+
test('fastify instance should contains listeningOrigin property (IPv6)', { skip: isIPv6Missing }, async t => {
|
|
283
286
|
t.plan(1)
|
|
284
287
|
const port = 3000
|
|
285
288
|
const host = '::1'
|
|
@@ -288,3 +291,10 @@ test('fastify instance should contains listeningOrigin property (IPv6)', async t
|
|
|
288
291
|
t.assert.deepStrictEqual(fastify.listeningOrigin, `http://[::1]:${port}`)
|
|
289
292
|
await fastify.close()
|
|
290
293
|
})
|
|
294
|
+
|
|
295
|
+
test('fastify instance should ensure ready promise cleanup on ready', async t => {
|
|
296
|
+
t.plan(1)
|
|
297
|
+
const fastify = Fastify()
|
|
298
|
+
await fastify.ready()
|
|
299
|
+
t.assert.strictEqual(fastify[kState].readyPromise, null)
|
|
300
|
+
})
|
|
@@ -6,6 +6,9 @@ const fp = require('fastify-plugin')
|
|
|
6
6
|
const split = require('split2')
|
|
7
7
|
const helper = require('./helper')
|
|
8
8
|
const { kState } = require('../lib/symbols')
|
|
9
|
+
const { networkInterfaces } = require('node:os')
|
|
10
|
+
|
|
11
|
+
const isIPv6Missing = !Object.values(networkInterfaces()).flat().some(({ family }) => family === 'IPv6')
|
|
9
12
|
|
|
10
13
|
let localhost
|
|
11
14
|
before(async function () {
|
|
@@ -405,7 +408,7 @@ test('localhost onListen encapsulation should be called in order and should log
|
|
|
405
408
|
})
|
|
406
409
|
})
|
|
407
410
|
|
|
408
|
-
test('non-localhost onListen should be called in order', t => {
|
|
411
|
+
test('non-localhost onListen should be called in order', { skip: isIPv6Missing }, t => {
|
|
409
412
|
t.plan(2)
|
|
410
413
|
|
|
411
414
|
const fastify = Fastify()
|
|
@@ -428,7 +431,7 @@ test('non-localhost onListen should be called in order', t => {
|
|
|
428
431
|
})
|
|
429
432
|
})
|
|
430
433
|
|
|
431
|
-
test('non-localhost async onListen should be called in order', async t => {
|
|
434
|
+
test('non-localhost async onListen should be called in order', { skip: isIPv6Missing }, async t => {
|
|
432
435
|
t.plan(2)
|
|
433
436
|
const fastify = Fastify()
|
|
434
437
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -448,7 +451,7 @@ test('non-localhost async onListen should be called in order', async t => {
|
|
|
448
451
|
})
|
|
449
452
|
})
|
|
450
453
|
|
|
451
|
-
test('non-localhost sync onListen should log errors as warnings and continue', t => {
|
|
454
|
+
test('non-localhost sync onListen should log errors as warnings and continue', { skip: isIPv6Missing }, t => {
|
|
452
455
|
t.plan(4)
|
|
453
456
|
const stream = split(JSON.parse)
|
|
454
457
|
const fastify = Fastify({
|
|
@@ -488,7 +491,7 @@ test('non-localhost sync onListen should log errors as warnings and continue', t
|
|
|
488
491
|
})
|
|
489
492
|
})
|
|
490
493
|
|
|
491
|
-
test('non-localhost async onListen should log errors as warnings and continue', async t => {
|
|
494
|
+
test('non-localhost async onListen should log errors as warnings and continue', { skip: isIPv6Missing }, async t => {
|
|
492
495
|
t.plan(6)
|
|
493
496
|
const stream = split(JSON.parse)
|
|
494
497
|
const fastify = Fastify({
|
|
@@ -529,7 +532,7 @@ test('non-localhost async onListen should log errors as warnings and continue',
|
|
|
529
532
|
})
|
|
530
533
|
})
|
|
531
534
|
|
|
532
|
-
test('non-localhost Register onListen hook after a plugin inside a plugin', t => {
|
|
535
|
+
test('non-localhost Register onListen hook after a plugin inside a plugin', { skip: isIPv6Missing }, t => {
|
|
533
536
|
t.plan(3)
|
|
534
537
|
const fastify = Fastify()
|
|
535
538
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -562,7 +565,7 @@ test('non-localhost Register onListen hook after a plugin inside a plugin', t =>
|
|
|
562
565
|
})
|
|
563
566
|
})
|
|
564
567
|
|
|
565
|
-
test('non-localhost Register onListen hook after a plugin inside a plugin should log errors as warnings and continue', t => {
|
|
568
|
+
test('non-localhost Register onListen hook after a plugin inside a plugin should log errors as warnings and continue', { skip: isIPv6Missing }, t => {
|
|
566
569
|
t.plan(6)
|
|
567
570
|
const stream = split(JSON.parse)
|
|
568
571
|
const fastify = Fastify({
|
|
@@ -608,7 +611,7 @@ test('non-localhost Register onListen hook after a plugin inside a plugin should
|
|
|
608
611
|
})
|
|
609
612
|
})
|
|
610
613
|
|
|
611
|
-
test('non-localhost onListen encapsulation should be called in order', t => {
|
|
614
|
+
test('non-localhost onListen encapsulation should be called in order', { skip: isIPv6Missing }, t => {
|
|
612
615
|
t.plan(6)
|
|
613
616
|
const fastify = Fastify()
|
|
614
617
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -640,7 +643,7 @@ test('non-localhost onListen encapsulation should be called in order', t => {
|
|
|
640
643
|
})
|
|
641
644
|
})
|
|
642
645
|
|
|
643
|
-
test('non-localhost onListen encapsulation should be called in order and should log errors as warnings and continue', t => {
|
|
646
|
+
test('non-localhost onListen encapsulation should be called in order and should log errors as warnings and continue', { skip: isIPv6Missing }, t => {
|
|
644
647
|
t.plan(7)
|
|
645
648
|
const stream = split(JSON.parse)
|
|
646
649
|
const fastify = Fastify({
|
|
@@ -874,7 +877,7 @@ test('onListen localhost with callback encapsulation should be called in order',
|
|
|
874
877
|
})
|
|
875
878
|
})
|
|
876
879
|
|
|
877
|
-
test('onListen non-localhost should work in order with callback in sync', t => {
|
|
880
|
+
test('onListen non-localhost should work in order with callback in sync', { skip: isIPv6Missing }, t => {
|
|
878
881
|
t.plan(4)
|
|
879
882
|
const fastify = Fastify()
|
|
880
883
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -896,7 +899,7 @@ test('onListen non-localhost should work in order with callback in sync', t => {
|
|
|
896
899
|
})
|
|
897
900
|
})
|
|
898
901
|
|
|
899
|
-
test('onListen non-localhost should work in order with callback in async', t => {
|
|
902
|
+
test('onListen non-localhost should work in order with callback in async', { skip: isIPv6Missing }, t => {
|
|
900
903
|
t.plan(4)
|
|
901
904
|
const fastify = Fastify()
|
|
902
905
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -916,7 +919,7 @@ test('onListen non-localhost should work in order with callback in async', t =>
|
|
|
916
919
|
})
|
|
917
920
|
})
|
|
918
921
|
|
|
919
|
-
test('onListen non-localhost sync with callback should log errors as warnings and continue', t => {
|
|
922
|
+
test('onListen non-localhost sync with callback should log errors as warnings and continue', { skip: isIPv6Missing }, t => {
|
|
920
923
|
t.plan(8)
|
|
921
924
|
|
|
922
925
|
const stream = split(JSON.parse)
|
|
@@ -960,7 +963,7 @@ test('onListen non-localhost sync with callback should log errors as warnings an
|
|
|
960
963
|
})
|
|
961
964
|
})
|
|
962
965
|
|
|
963
|
-
test('onListen non-localhost async with callback should log errors as warnings and continue', t => {
|
|
966
|
+
test('onListen non-localhost async with callback should log errors as warnings and continue', { skip: isIPv6Missing }, t => {
|
|
964
967
|
t.plan(8)
|
|
965
968
|
|
|
966
969
|
const stream = split(JSON.parse)
|
|
@@ -1002,7 +1005,7 @@ test('onListen non-localhost async with callback should log errors as warnings a
|
|
|
1002
1005
|
})
|
|
1003
1006
|
})
|
|
1004
1007
|
|
|
1005
|
-
test('Register onListen hook non-localhost with callback after a plugin inside a plugin', t => {
|
|
1008
|
+
test('Register onListen hook non-localhost with callback after a plugin inside a plugin', { skip: isIPv6Missing }, t => {
|
|
1006
1009
|
t.plan(5)
|
|
1007
1010
|
const fastify = Fastify()
|
|
1008
1011
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -1035,7 +1038,7 @@ test('Register onListen hook non-localhost with callback after a plugin inside a
|
|
|
1035
1038
|
})
|
|
1036
1039
|
})
|
|
1037
1040
|
|
|
1038
|
-
test('onListen non-localhost with callback encapsulation should be called in order', t => {
|
|
1041
|
+
test('onListen non-localhost with callback encapsulation should be called in order', { skip: isIPv6Missing }, t => {
|
|
1039
1042
|
t.plan(8)
|
|
1040
1043
|
const fastify = Fastify()
|
|
1041
1044
|
t.teardown(fastify.close.bind(fastify))
|
|
@@ -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 = 85
|
|
9
9
|
|
|
10
10
|
test(`should expose ${expectedErrors} errors`, t => {
|
|
11
11
|
t.plan(1)
|
|
@@ -21,6 +21,7 @@ test(`should expose ${expectedErrors} errors`, t => {
|
|
|
21
21
|
|
|
22
22
|
test('ensure name and codes of Errors are identical', t => {
|
|
23
23
|
t.plan(expectedErrors)
|
|
24
|
+
|
|
24
25
|
const exportedKeys = Object.keys(errors)
|
|
25
26
|
for (const key of exportedKeys) {
|
|
26
27
|
if (errors[key].name === 'FastifyError') {
|
|
@@ -249,6 +250,16 @@ test('FST_ERR_DEC_REFERENCE_TYPE', t => {
|
|
|
249
250
|
t.assert.ok(error instanceof Error)
|
|
250
251
|
})
|
|
251
252
|
|
|
253
|
+
test('FST_ERR_DEC_UNDECLARED', t => {
|
|
254
|
+
t.plan(5)
|
|
255
|
+
const error = new errors.FST_ERR_DEC_UNDECLARED('myDecorator', 'request')
|
|
256
|
+
t.assert.strictEqual(error.name, 'FastifyError')
|
|
257
|
+
t.assert.strictEqual(error.code, 'FST_ERR_DEC_UNDECLARED')
|
|
258
|
+
t.assert.strictEqual(error.message, "No decorator 'myDecorator' has been declared on request.")
|
|
259
|
+
t.assert.strictEqual(error.statusCode, 500)
|
|
260
|
+
t.assert.ok(error instanceof Error)
|
|
261
|
+
})
|
|
262
|
+
|
|
252
263
|
test('FST_ERR_HOOK_INVALID_TYPE', t => {
|
|
253
264
|
t.plan(5)
|
|
254
265
|
const error = new errors.FST_ERR_HOOK_INVALID_TYPE()
|
|
@@ -881,6 +892,7 @@ test('FST_ERR_ERROR_HANDLER_NOT_FN', t => {
|
|
|
881
892
|
|
|
882
893
|
test('Ensure that all errors are in Errors.md TOC', t => {
|
|
883
894
|
t.plan(expectedErrors)
|
|
895
|
+
|
|
884
896
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
885
897
|
|
|
886
898
|
const exportedKeys = Object.keys(errors)
|
|
@@ -918,6 +930,7 @@ test('Ensure that all errors are in Errors.md documented', t => {
|
|
|
918
930
|
|
|
919
931
|
test('Ensure that non-existing errors are not in Errors.md documented', t => {
|
|
920
932
|
t.plan(expectedErrors)
|
|
933
|
+
|
|
921
934
|
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
|
|
922
935
|
|
|
923
936
|
const matchRE = /<a id="[0-9a-zA-Z_]+">([0-9a-zA-Z_]+)<\/a>/g
|
package/test/listen.2.test.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const { test, before } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
const helper = require('./helper')
|
|
6
|
+
const { networkInterfaces } = require('node:os')
|
|
7
|
+
|
|
8
|
+
const isIPv6Missing = !Object.values(networkInterfaces()).flat().some(({ family }) => family === 'IPv6')
|
|
6
9
|
|
|
7
10
|
let localhostForURL
|
|
8
11
|
|
|
@@ -59,7 +62,7 @@ test('double listen errors callback with (err, address)', (t, done) => {
|
|
|
59
62
|
})
|
|
60
63
|
})
|
|
61
64
|
|
|
62
|
-
test('nonlocalhost double listen errors callback with (err, address)', (t, done) => {
|
|
65
|
+
test('nonlocalhost double listen errors callback with (err, address)', { skip: isIPv6Missing }, (t, done) => {
|
|
63
66
|
t.plan(4)
|
|
64
67
|
const fastify = Fastify()
|
|
65
68
|
t.after(() => fastify.close())
|
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
|
+
})
|
|
@@ -522,6 +522,9 @@ expectError(server.decorateReply('typedTestReplyMethod', async function (x) {
|
|
|
522
522
|
return 'foo'
|
|
523
523
|
}))
|
|
524
524
|
|
|
525
|
+
const foo = server.getDecorator<string>('foo')
|
|
526
|
+
expectType<string>(foo)
|
|
527
|
+
|
|
525
528
|
const versionConstraintStrategy = {
|
|
526
529
|
name: 'version',
|
|
527
530
|
storage: () => ({
|
|
@@ -45,6 +45,7 @@ const getHandler: RouteHandlerMethod = function (_request, reply) {
|
|
|
45
45
|
expectAssignable<((input: { [key: string]: unknown }, schema: { [key: string]: unknown }, httpStatus?: string) => unknown)>(reply.serializeInput)
|
|
46
46
|
expectAssignable<((input: { [key: string]: unknown }, httpStatus: string) => unknown)>(reply.serializeInput)
|
|
47
47
|
expectType<ContextConfigDefault & FastifyRouteConfig & FastifyContextConfig>(reply.routeOptions.config)
|
|
48
|
+
expectType<string>(reply.getDecorator<string>('foo'))
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
interface ReplyPayload {
|
|
@@ -92,6 +92,10 @@ const getHandler: RouteHandler = function (request, _reply) {
|
|
|
92
92
|
expectAssignable<(schema: { [key: string]: unknown }) => ExpectedGetValidationFunction>(request.getValidationFunction)
|
|
93
93
|
expectAssignable<(input: { [key: string]: unknown }, schema: { [key: string]: unknown }, httpPart?: HTTPRequestPart) => boolean>(request.validateInput)
|
|
94
94
|
expectAssignable<(input: { [key: string]: unknown }, httpPart?: HTTPRequestPart) => boolean>(request.validateInput)
|
|
95
|
+
expectType<string>(request.getDecorator<string>('foo'))
|
|
96
|
+
expectType<void>(request.setDecorator('foo', 'hello'))
|
|
97
|
+
expectType<void>(request.setDecorator<string>('foo', 'hello'))
|
|
98
|
+
expectError(request.setDecorator<string>('foo', true))
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
const getHandlerWithCustomLogger: RouteHandlerMethod<RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, RouteGenericInterface, ContextConfigDefault, FastifySchema, FastifyTypeProviderDefault, CustomLoggerInterface> = function (request, _reply) {
|
|
@@ -1009,6 +1009,46 @@ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{ Reply:
|
|
|
1009
1009
|
}
|
|
1010
1010
|
))
|
|
1011
1011
|
|
|
1012
|
+
// -------------------------------------------------------------------
|
|
1013
|
+
// RouteGeneric Reply Type Return (Different Status Codes)
|
|
1014
|
+
// -------------------------------------------------------------------
|
|
1015
|
+
|
|
1016
|
+
expectAssignable(server.get<{
|
|
1017
|
+
Reply: {
|
|
1018
|
+
200: string | { msg: string }
|
|
1019
|
+
400: number
|
|
1020
|
+
'5xx': { error: string }
|
|
1021
|
+
}
|
|
1022
|
+
}>(
|
|
1023
|
+
'/',
|
|
1024
|
+
async (_, res) => {
|
|
1025
|
+
const option = 1 as 1 | 2 | 3 | 4
|
|
1026
|
+
switch (option) {
|
|
1027
|
+
case 1: return 'hello'
|
|
1028
|
+
case 2: return { msg: 'hello' }
|
|
1029
|
+
case 3: return 400
|
|
1030
|
+
case 4: return { error: 'error' }
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
))
|
|
1034
|
+
|
|
1035
|
+
// -------------------------------------------------------------------
|
|
1036
|
+
// RouteGeneric Reply Type Return: Non Assignable (Different Status Codes)
|
|
1037
|
+
// -------------------------------------------------------------------
|
|
1038
|
+
|
|
1039
|
+
expectError(server.get<{
|
|
1040
|
+
Reply: {
|
|
1041
|
+
200: string | { msg: string }
|
|
1042
|
+
400: number
|
|
1043
|
+
'5xx': { error: string }
|
|
1044
|
+
}
|
|
1045
|
+
}>(
|
|
1046
|
+
'/',
|
|
1047
|
+
async (_, res) => {
|
|
1048
|
+
return true
|
|
1049
|
+
}
|
|
1050
|
+
))
|
|
1051
|
+
|
|
1012
1052
|
// -------------------------------------------------------------------
|
|
1013
1053
|
// FastifyPlugin: Auxiliary
|
|
1014
1054
|
// -------------------------------------------------------------------
|