fastify 5.2.1 → 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.
Files changed (85) hide show
  1. package/LICENSE +1 -1
  2. package/PROJECT_CHARTER.md +7 -7
  3. package/README.md +24 -25
  4. package/SPONSORS.md +1 -0
  5. package/docs/Guides/Benchmarking.md +4 -4
  6. package/docs/Guides/Database.md +1 -1
  7. package/docs/Guides/Delay-Accepting-Requests.md +10 -10
  8. package/docs/Guides/Ecosystem.md +7 -1
  9. package/docs/Guides/Fluent-Schema.md +1 -1
  10. package/docs/Guides/Getting-Started.md +9 -5
  11. package/docs/Guides/Index.md +1 -1
  12. package/docs/Guides/Migration-Guide-V4.md +1 -1
  13. package/docs/Guides/Migration-Guide-V5.md +12 -2
  14. package/docs/Guides/Plugins-Guide.md +6 -6
  15. package/docs/Guides/Serverless.md +14 -48
  16. package/docs/Guides/Style-Guide.md +2 -2
  17. package/docs/Guides/Testing.md +2 -2
  18. package/docs/Guides/Write-Plugin.md +2 -3
  19. package/docs/Reference/ContentTypeParser.md +58 -78
  20. package/docs/Reference/Decorators.md +249 -60
  21. package/docs/Reference/Encapsulation.md +28 -33
  22. package/docs/Reference/Errors.md +52 -53
  23. package/docs/Reference/HTTP2.md +7 -7
  24. package/docs/Reference/Hooks.md +31 -30
  25. package/docs/Reference/LTS.md +10 -15
  26. package/docs/Reference/Lifecycle.md +19 -24
  27. package/docs/Reference/Logging.md +59 -56
  28. package/docs/Reference/Middleware.md +19 -19
  29. package/docs/Reference/Plugins.md +55 -71
  30. package/docs/Reference/Principles.md +25 -30
  31. package/docs/Reference/Reply.md +11 -10
  32. package/docs/Reference/Request.md +89 -98
  33. package/docs/Reference/Routes.md +108 -128
  34. package/docs/Reference/Server.md +18 -16
  35. package/docs/Reference/Type-Providers.md +19 -21
  36. package/docs/Reference/TypeScript.md +1 -18
  37. package/docs/Reference/Validation-and-Serialization.md +134 -159
  38. package/docs/Reference/Warnings.md +22 -25
  39. package/fastify.js +3 -2
  40. package/lib/contentTypeParser.js +7 -8
  41. package/lib/decorate.js +18 -3
  42. package/lib/error-handler.js +14 -12
  43. package/lib/errors.js +4 -0
  44. package/lib/headRoute.js +4 -2
  45. package/lib/pluginUtils.js +4 -2
  46. package/lib/reply.js +17 -2
  47. package/lib/request.js +28 -2
  48. package/lib/server.js +5 -0
  49. package/lib/validation.js +1 -1
  50. package/lib/warnings.js +9 -0
  51. package/lib/wrapThenable.js +8 -1
  52. package/package.json +12 -12
  53. package/test/bundler/esbuild/package.json +1 -1
  54. package/test/close.test.js +125 -108
  55. package/test/custom-parser-async.test.js +34 -36
  56. package/test/custom-parser.4.test.js +55 -38
  57. package/test/decorator.test.js +174 -4
  58. package/test/fastify-instance.test.js +12 -2
  59. package/test/genReqId.test.js +125 -174
  60. package/test/has-route.test.js +1 -3
  61. package/test/hooks.on-listen.test.js +17 -14
  62. package/test/internals/content-type-parser.test.js +1 -1
  63. package/test/internals/errors.test.js +14 -1
  64. package/test/issue-4959.test.js +84 -0
  65. package/test/listen.1.test.js +37 -34
  66. package/test/listen.2.test.js +50 -40
  67. package/test/listen.3.test.js +28 -32
  68. package/test/listen.4.test.js +61 -45
  69. package/test/listen.5.test.js +23 -0
  70. package/test/register.test.js +55 -50
  71. package/test/request-error.test.js +114 -94
  72. package/test/route-shorthand.test.js +36 -32
  73. package/test/stream.5.test.js +35 -33
  74. package/test/throw.test.js +87 -91
  75. package/test/toolkit.js +32 -0
  76. package/test/trust-proxy.test.js +23 -23
  77. package/test/types/instance.test-d.ts +4 -0
  78. package/test/types/reply.test-d.ts +1 -0
  79. package/test/types/request.test-d.ts +4 -0
  80. package/test/types/type-provider.test-d.ts +40 -0
  81. package/test/upgrade.test.js +32 -33
  82. package/types/instance.d.ts +6 -0
  83. package/types/reply.d.ts +1 -0
  84. package/types/request.d.ts +2 -0
  85. package/types/type-provider.d.ts +12 -3
@@ -1,14 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
3
+ const { test } = require('node:test')
5
4
  const sget = require('simple-get').concat
6
5
  const Fastify = require('../fastify')
7
6
 
8
7
  process.removeAllListeners('warning')
9
8
 
10
- test('contentTypeParser should add a custom async parser', t => {
11
- t.plan(3)
9
+ test('contentTypeParser should add a custom async parser', async t => {
10
+ t.plan(2)
12
11
  const fastify = Fastify()
13
12
 
14
13
  fastify.post('/', (req, reply) => {
@@ -24,43 +23,42 @@ test('contentTypeParser should add a custom async parser', t => {
24
23
  return res
25
24
  })
26
25
 
27
- fastify.listen({ port: 0 }, err => {
28
- t.error(err)
26
+ t.after(() => fastify.close())
27
+ await fastify.listen({ port: 0 })
29
28
 
30
- t.teardown(() => fastify.close())
29
+ await t.test('in POST', (t, done) => {
30
+ t.plan(3)
31
31
 
32
- t.test('in POST', t => {
33
- t.plan(3)
34
-
35
- sget({
36
- method: 'POST',
37
- url: 'http://localhost:' + fastify.server.address().port,
38
- body: '{"hello":"world"}',
39
- headers: {
40
- 'Content-Type': 'application/jsoff'
41
- }
42
- }, (err, response, body) => {
43
- t.error(err)
44
- t.equal(response.statusCode, 200)
45
- t.same(body.toString(), JSON.stringify({ hello: 'world' }))
46
- })
32
+ sget({
33
+ method: 'POST',
34
+ url: 'http://localhost:' + fastify.server.address().port,
35
+ body: '{"hello":"world"}',
36
+ headers: {
37
+ 'Content-Type': 'application/jsoff'
38
+ }
39
+ }, (err, response, body) => {
40
+ t.assert.ifError(err)
41
+ t.assert.strictEqual(response.statusCode, 200)
42
+ t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
43
+ done()
47
44
  })
45
+ })
48
46
 
49
- t.test('in OPTIONS', t => {
50
- t.plan(3)
47
+ await t.test('in OPTIONS', (t, done) => {
48
+ t.plan(3)
51
49
 
52
- sget({
53
- method: 'OPTIONS',
54
- url: 'http://localhost:' + fastify.server.address().port,
55
- body: '{"hello":"world"}',
56
- headers: {
57
- 'Content-Type': 'application/jsoff'
58
- }
59
- }, (err, response, body) => {
60
- t.error(err)
61
- t.equal(response.statusCode, 200)
62
- t.same(body.toString(), JSON.stringify({ hello: 'world' }))
63
- })
50
+ sget({
51
+ method: 'OPTIONS',
52
+ url: 'http://localhost:' + fastify.server.address().port,
53
+ body: '{"hello":"world"}',
54
+ headers: {
55
+ 'Content-Type': 'application/jsoff'
56
+ }
57
+ }, (err, response, body) => {
58
+ t.assert.ifError(err)
59
+ t.assert.strictEqual(response.statusCode, 200)
60
+ t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
61
+ done()
64
62
  })
65
63
  })
66
64
  })
@@ -1,19 +1,18 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
3
+ const { test } = require('node:test')
5
4
  const sget = require('simple-get').concat
6
5
  const Fastify = require('../fastify')
7
6
  const jsonParser = require('fast-json-body')
8
7
  const { getServerUrl } = require('./helper')
8
+ const { waitForCb } = require('./toolkit')
9
9
 
10
10
  process.removeAllListeners('warning')
11
11
 
12
- test('should prefer string content types over RegExp ones', t => {
12
+ test('should prefer string content types over RegExp ones', (t, testDone) => {
13
13
  t.plan(7)
14
14
  const fastify = Fastify()
15
- t.teardown(fastify.close.bind(fastify))
16
-
15
+ t.after(() => { fastify.close() })
17
16
  fastify.post('/', (req, reply) => {
18
17
  reply.send(req.body)
19
18
  })
@@ -33,7 +32,8 @@ test('should prefer string content types over RegExp ones', t => {
33
32
  })
34
33
 
35
34
  fastify.listen({ port: 0 }, err => {
36
- t.error(err)
35
+ t.assert.ifError(err)
36
+ const completion = waitForCb({ steps: 2 })
37
37
 
38
38
  sget({
39
39
  method: 'POST',
@@ -43,9 +43,10 @@ test('should prefer string content types over RegExp ones', t => {
43
43
  'Content-Type': 'application/json'
44
44
  }
45
45
  }, (err, response, body) => {
46
- t.error(err)
47
- t.equal(response.statusCode, 200)
48
- t.same(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
46
+ t.assert.ifError(err)
47
+ t.assert.strictEqual(response.statusCode, 200)
48
+ t.assert.equal(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
49
+ completion.stepIn()
49
50
  })
50
51
 
51
52
  sget({
@@ -56,18 +57,21 @@ test('should prefer string content types over RegExp ones', t => {
56
57
  'Content-Type': 'application/javascript'
57
58
  }
58
59
  }, (err, response, body) => {
59
- t.error(err)
60
- t.equal(response.statusCode, 200)
61
- t.same(body.toString(), 'javascript')
60
+ t.assert.ifError(err)
61
+ t.assert.strictEqual(response.statusCode, 200)
62
+ t.assert.equal(body.toString(), 'javascript')
63
+ completion.stepIn()
62
64
  })
65
+
66
+ completion.patience.then(testDone)
63
67
  })
64
68
  })
65
69
 
66
- test('removeContentTypeParser should support arrays of content types to remove', t => {
70
+ test('removeContentTypeParser should support arrays of content types to remove', (t, testDone) => {
67
71
  t.plan(8)
68
72
 
69
73
  const fastify = Fastify()
70
- t.teardown(fastify.close.bind(fastify))
74
+ t.after(() => fastify.close())
71
75
 
72
76
  fastify.addContentTypeParser('application/xml', function (req, payload, done) {
73
77
  payload.on('data', () => {})
@@ -90,7 +94,8 @@ test('removeContentTypeParser should support arrays of content types to remove',
90
94
  })
91
95
 
92
96
  fastify.listen({ port: 0 }, err => {
93
- t.error(err)
97
+ t.assert.ifError(err)
98
+ const completion = waitForCb({ steps: 3 })
94
99
 
95
100
  sget({
96
101
  method: 'POST',
@@ -100,9 +105,10 @@ test('removeContentTypeParser should support arrays of content types to remove',
100
105
  'Content-Type': 'application/xml'
101
106
  }
102
107
  }, (err, response, body) => {
103
- t.error(err)
104
- t.equal(response.statusCode, 200)
105
- t.same(body.toString(), 'xml')
108
+ t.assert.ifError(err)
109
+ t.assert.strictEqual(response.statusCode, 200)
110
+ t.assert.equal(body.toString(), 'xml')
111
+ completion.stepIn()
106
112
  })
107
113
 
108
114
  sget({
@@ -113,8 +119,9 @@ test('removeContentTypeParser should support arrays of content types to remove',
113
119
  'Content-Type': 'image/png'
114
120
  }
115
121
  }, (err, response, body) => {
116
- t.error(err)
117
- t.equal(response.statusCode, 415)
122
+ t.assert.ifError(err)
123
+ t.assert.strictEqual(response.statusCode, 415)
124
+ completion.stepIn()
118
125
  })
119
126
 
120
127
  sget({
@@ -125,16 +132,19 @@ test('removeContentTypeParser should support arrays of content types to remove',
125
132
  'Content-Type': 'application/json'
126
133
  }
127
134
  }, (err, response, body) => {
128
- t.error(err)
129
- t.equal(response.statusCode, 415)
135
+ t.assert.ifError(err)
136
+ t.assert.strictEqual(response.statusCode, 415)
137
+ completion.stepIn()
130
138
  })
139
+ completion.patience.then(testDone)
131
140
  })
132
141
  })
133
142
 
134
- test('removeContentTypeParser should support encapsulation', t => {
143
+ test('removeContentTypeParser should support encapsulation', (t, testDone) => {
135
144
  t.plan(6)
136
145
 
137
146
  const fastify = Fastify()
147
+ t.after(() => fastify.close())
138
148
 
139
149
  fastify.addContentTypeParser('application/xml', function (req, payload, done) {
140
150
  payload.on('data', () => {})
@@ -158,7 +168,8 @@ test('removeContentTypeParser should support encapsulation', t => {
158
168
  })
159
169
 
160
170
  fastify.listen({ port: 0 }, err => {
161
- t.error(err)
171
+ t.assert.ifError(err)
172
+ const completion = waitForCb({ steps: 2 })
162
173
 
163
174
  sget({
164
175
  method: 'POST',
@@ -168,8 +179,9 @@ test('removeContentTypeParser should support encapsulation', t => {
168
179
  'Content-Type': 'application/xml'
169
180
  }
170
181
  }, (err, response, body) => {
171
- t.error(err)
172
- t.equal(response.statusCode, 415)
182
+ t.assert.ifError(err)
183
+ t.assert.strictEqual(response.statusCode, 415)
184
+ completion.stepIn()
173
185
  })
174
186
 
175
187
  sget({
@@ -180,18 +192,20 @@ test('removeContentTypeParser should support encapsulation', t => {
180
192
  'Content-Type': 'application/xml'
181
193
  }
182
194
  }, (err, response, body) => {
183
- t.error(err)
184
- t.equal(response.statusCode, 200)
185
- t.same(body.toString(), 'xml')
186
- fastify.close()
195
+ t.assert.ifError(err)
196
+ t.assert.strictEqual(response.statusCode, 200)
197
+ t.assert.equal(body.toString(), 'xml')
198
+ completion.stepIn()
187
199
  })
200
+ completion.patience.then(testDone)
188
201
  })
189
202
  })
190
203
 
191
- test('removeAllContentTypeParsers should support encapsulation', t => {
204
+ test('removeAllContentTypeParsers should support encapsulation', (t, testDone) => {
192
205
  t.plan(6)
193
206
 
194
207
  const fastify = Fastify()
208
+ t.after(() => fastify.close())
195
209
 
196
210
  fastify.post('/', (req, reply) => {
197
211
  reply.send(req.body)
@@ -208,7 +222,8 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
208
222
  })
209
223
 
210
224
  fastify.listen({ port: 0 }, err => {
211
- t.error(err)
225
+ t.assert.ifError(err)
226
+ const completion = waitForCb({ steps: 2 })
212
227
 
213
228
  sget({
214
229
  method: 'POST',
@@ -218,8 +233,9 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
218
233
  'Content-Type': 'application/json'
219
234
  }
220
235
  }, (err, response, body) => {
221
- t.error(err)
222
- t.equal(response.statusCode, 415)
236
+ t.assert.ifError(err)
237
+ t.assert.strictEqual(response.statusCode, 415)
238
+ completion.stepIn()
223
239
  })
224
240
 
225
241
  sget({
@@ -230,10 +246,11 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
230
246
  'Content-Type': 'application/json'
231
247
  }
232
248
  }, (err, response, body) => {
233
- t.error(err)
234
- t.equal(response.statusCode, 200)
235
- t.same(JSON.parse(body.toString()).test, 1)
236
- fastify.close()
249
+ t.assert.ifError(err)
250
+ t.assert.strictEqual(response.statusCode, 200)
251
+ t.assert.equal(JSON.parse(body.toString()).test, 1)
252
+ completion.stepIn()
237
253
  })
254
+ completion.patience.then(testDone)
238
255
  })
239
256
  })
@@ -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, rep) => {
928
+ server.get('/root-assert', async (req, res) => {
929
929
  req.assert()
930
- rep.assert()
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, rep) => {
939
+ instance.get('/nested-assert', async (req, res) => {
940
940
  req.assert()
941
- rep.assert()
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
+ })