fastify 5.3.3 → 5.4.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 (70) hide show
  1. package/README.md +2 -0
  2. package/build/build-validation.js +2 -1
  3. package/docs/Guides/Delay-Accepting-Requests.md +3 -3
  4. package/docs/Guides/Ecosystem.md +9 -5
  5. package/docs/Reference/ContentTypeParser.md +1 -1
  6. package/docs/Reference/Errors.md +2 -2
  7. package/docs/Reference/Hooks.md +14 -14
  8. package/docs/Reference/Logging.md +3 -3
  9. package/docs/Reference/Middleware.md +1 -1
  10. package/docs/Reference/Reply.md +8 -8
  11. package/docs/Reference/Request.md +1 -1
  12. package/docs/Reference/Routes.md +3 -3
  13. package/docs/Reference/Server.md +35 -21
  14. package/docs/Reference/Validation-and-Serialization.md +1 -1
  15. package/fastify.d.ts +2 -1
  16. package/fastify.js +14 -2
  17. package/lib/configValidator.js +1 -1
  18. package/lib/errors.js +6 -0
  19. package/lib/pluginOverride.js +3 -1
  20. package/lib/reply.js +7 -11
  21. package/lib/request.js +3 -10
  22. package/lib/symbols.js +1 -0
  23. package/lib/warnings.js +8 -0
  24. package/package.json +8 -4
  25. package/test/404s.test.js +226 -325
  26. package/test/allow-unsafe-regex.test.js +19 -48
  27. package/test/als.test.js +28 -40
  28. package/test/async-await.test.js +11 -2
  29. package/test/body-limit.test.js +41 -65
  30. package/test/build-certificate.js +1 -1
  31. package/test/custom-parser-async.test.js +17 -22
  32. package/test/decorator-namespace.test._js_ +3 -4
  33. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  34. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  35. package/test/helper.js +1 -1
  36. package/test/hooks-async.test.js +248 -218
  37. package/test/hooks.test.js +910 -769
  38. package/test/http-methods/lock.test.js +31 -31
  39. package/test/http-methods/mkcol.test.js +5 -9
  40. package/test/http-methods/proppatch.test.js +23 -29
  41. package/test/http-methods/report.test.js +44 -69
  42. package/test/http-methods/search.test.js +67 -82
  43. package/test/http2/closing.test.js +38 -20
  44. package/test/http2/secure-with-fallback.test.js +28 -27
  45. package/test/https/https.test.js +56 -53
  46. package/test/internals/errors.test.js +1 -1
  47. package/test/internals/handle-request.test.js +49 -66
  48. package/test/issue-4959.test.js +12 -3
  49. package/test/listen.4.test.js +31 -43
  50. package/test/nullable-validation.test.js +33 -46
  51. package/test/output-validation.test.js +24 -26
  52. package/test/plugin.2.test.js +104 -86
  53. package/test/plugin.3.test.js +56 -35
  54. package/test/plugin.4.test.js +124 -119
  55. package/test/proto-poisoning.test.js +78 -97
  56. package/test/request-error.test.js +0 -46
  57. package/test/route-hooks.test.js +112 -92
  58. package/test/route-prefix.test.js +194 -133
  59. package/test/schema-serialization.test.js +177 -154
  60. package/test/schema-special-usage.test.js +165 -132
  61. package/test/schema-validation.test.js +242 -205
  62. package/test/set-error-handler.test.js +58 -1
  63. package/test/skip-reply-send.test.js +64 -69
  64. package/test/trust-proxy.test.js +32 -58
  65. package/test/types/fastify.test-d.ts +3 -0
  66. package/test/types/request.test-d.ts +1 -0
  67. package/test/url-rewriting.test.js +45 -62
  68. package/types/request.d.ts +1 -0
  69. package/.taprc +0 -7
  70. package/.vscode/settings.json +0 -22
@@ -3,10 +3,10 @@
3
3
  const { test } = require('node:test')
4
4
  const Fastify = require('../..')
5
5
  const h2url = require('h2url')
6
- const sget = require('simple-get').concat
7
6
  const msg = { hello: 'world' }
8
7
 
9
8
  const { buildCertificate } = require('../build-certificate')
9
+ const { Agent } = require('undici')
10
10
  test.before(buildCertificate)
11
11
 
12
12
  test('secure with fallback', async (t) => {
@@ -41,12 +41,12 @@ test('secure with fallback', async (t) => {
41
41
 
42
42
  t.after(() => { fastify.close() })
43
43
 
44
- await fastify.listen({ port: 0 })
44
+ const fastifyServer = await fastify.listen({ port: 0 })
45
45
 
46
46
  await t.test('https get error', async (t) => {
47
47
  t.plan(1)
48
48
 
49
- const url = `https://localhost:${fastify.server.address().port}/error`
49
+ const url = `${fastifyServer}/error`
50
50
  const res = await h2url.concat({ url })
51
51
 
52
52
  t.assert.strictEqual(res.headers[':status'], 500)
@@ -55,9 +55,8 @@ test('secure with fallback', async (t) => {
55
55
  await t.test('https post', async (t) => {
56
56
  t.plan(2)
57
57
 
58
- const url = `https://localhost:${fastify.server.address().port}`
59
58
  const res = await h2url.concat({
60
- url,
59
+ url: fastifyServer,
61
60
  method: 'POST',
62
61
  body: JSON.stringify({ hello: 'http2' }),
63
62
  headers: {
@@ -72,39 +71,41 @@ test('secure with fallback', async (t) => {
72
71
  await t.test('https get request', async (t) => {
73
72
  t.plan(3)
74
73
 
75
- const url = `https://localhost:${fastify.server.address().port}`
76
- const res = await h2url.concat({ url })
74
+ const res = await h2url.concat({ url: fastifyServer })
77
75
 
78
76
  t.assert.strictEqual(res.headers[':status'], 200)
79
77
  t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
80
78
  t.assert.deepStrictEqual(JSON.parse(res.body), msg)
81
79
  })
82
80
 
83
- await t.test('http1 get request', (t, done) => {
81
+ await t.test('http1 get request', async t => {
84
82
  t.plan(4)
85
- sget({
86
- method: 'GET',
87
- url: 'https://localhost:' + fastify.server.address().port,
88
- rejectUnauthorized: false
89
- }, (err, response, body) => {
90
- t.assert.ifError(err)
91
- t.assert.strictEqual(response.statusCode, 200)
92
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
93
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
94
- done()
83
+ const result = await fetch(fastifyServer, {
84
+ dispatcher: new Agent({
85
+ connect: {
86
+ rejectUnauthorized: false
87
+ }
88
+ })
95
89
  })
90
+
91
+ const body = await result.text()
92
+ t.assert.ok(result.ok)
93
+ t.assert.strictEqual(result.status, 200)
94
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
95
+ t.assert.deepStrictEqual(JSON.parse(body), msg)
96
96
  })
97
97
 
98
- await t.test('http1 get error', (t, done) => {
98
+ await t.test('http1 get error', async t => {
99
99
  t.plan(2)
100
- sget({
101
- method: 'GET',
102
- url: 'https://localhost:' + fastify.server.address().port + '/error',
103
- rejectUnauthorized: false
104
- }, (err, response, body) => {
105
- t.assert.ifError(err)
106
- t.assert.strictEqual(response.statusCode, 500)
107
- done()
100
+ const result = await fetch(`${fastifyServer}/error`, {
101
+ dispatcher: new Agent({
102
+ connect: {
103
+ rejectUnauthorized: false
104
+ }
105
+ })
108
106
  })
107
+
108
+ t.assert.ok(!result.ok)
109
+ t.assert.strictEqual(result.status, 500)
109
110
  })
110
111
  })
@@ -1,10 +1,11 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
4
+ const { request } = require('undici')
5
5
  const Fastify = require('../..')
6
6
 
7
7
  const { buildCertificate } = require('../build-certificate')
8
+ const { Agent } = require('undici')
8
9
  test.before(buildCertificate)
9
10
 
10
11
  test('https', async (t) => {
@@ -35,43 +36,45 @@ test('https', async (t) => {
35
36
 
36
37
  t.after(() => { fastify.close() })
37
38
 
38
- await t.test('https get request', (t, done) => {
39
+ await t.test('https get request', async t => {
39
40
  t.plan(4)
40
- sget({
41
- method: 'GET',
42
- url: 'https://localhost:' + fastify.server.address().port,
43
- rejectUnauthorized: false
44
- }, (err, response, body) => {
45
- t.assert.ifError(err)
46
- t.assert.strictEqual(response.statusCode, 200)
47
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
48
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
49
- done()
41
+ const result = await fetch('https://localhost:' + fastify.server.address().port, {
42
+ dispatcher: new Agent({
43
+ connect: {
44
+ rejectUnauthorized: false
45
+ }
46
+ })
50
47
  })
48
+ t.assert.ok(result.ok)
49
+ t.assert.strictEqual(result.status, 200)
50
+ const body = await result.text()
51
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
52
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
51
53
  })
52
54
 
53
- await t.test('https get request without trust proxy - protocol', (t, done) => {
54
- t.plan(4)
55
- sget({
56
- method: 'GET',
57
- url: 'https://localhost:' + fastify.server.address().port + '/proto',
58
- rejectUnauthorized: false
59
- }, (err, response, body) => {
60
- t.assert.ifError(err)
61
- t.assert.deepStrictEqual(JSON.parse(body), { proto: 'https' })
55
+ await t.test('https get request without trust proxy - protocol', async t => {
56
+ t.plan(3)
57
+ const result1 = await fetch(`${'https://localhost:' + fastify.server.address().port}/proto`, {
58
+ dispatcher: new Agent({
59
+ connect: {
60
+ rejectUnauthorized: false
61
+ }
62
+ })
62
63
  })
63
- sget({
64
- method: 'GET',
65
- url: 'https://localhost:' + fastify.server.address().port + '/proto',
66
- rejectUnauthorized: false,
64
+ t.assert.ok(result1.ok)
65
+ t.assert.deepStrictEqual(await result1.json(), { proto: 'https' })
66
+
67
+ const result2 = await fetch(`${'https://localhost:' + fastify.server.address().port}/proto`, {
68
+ dispatcher: new Agent({
69
+ connect: {
70
+ rejectUnauthorized: false
71
+ }
72
+ }),
67
73
  headers: {
68
74
  'x-forwarded-proto': 'lorem'
69
75
  }
70
- }, (err, response, body) => {
71
- t.assert.ifError(err)
72
- t.assert.deepStrictEqual(JSON.parse(body), { proto: 'https' })
73
- done()
74
76
  })
77
+ t.assert.deepStrictEqual(await result2.json(), { proto: 'https' })
75
78
  })
76
79
  })
77
80
 
@@ -98,36 +101,36 @@ test('https - headers', async (t) => {
98
101
 
99
102
  await fastify.listen({ port: 0 })
100
103
 
101
- await t.test('https get request', (t, done) => {
102
- t.plan(4)
103
- sget({
104
- method: 'GET',
105
- url: 'https://localhost:' + fastify.server.address().port,
106
- rejectUnauthorized: false
107
- }, (err, response, body) => {
108
- t.assert.ifError(err)
109
- t.assert.strictEqual(response.statusCode, 200)
110
- const parsedBody = JSON.parse(body)
111
- t.assert.strictEqual(parsedBody.hostname, 'localhost')
112
- t.assert.strictEqual(parsedBody.port, fastify.server.address().port)
113
- done()
104
+ await t.test('https get request', async t => {
105
+ t.plan(3)
106
+ const result = await fetch('https://localhost:' + fastify.server.address().port, {
107
+ dispatcher: new Agent({
108
+ connect: {
109
+ rejectUnauthorized: false
110
+ }
111
+ })
114
112
  })
113
+ t.assert.ok(result.ok)
114
+ t.assert.strictEqual(result.status, 200)
115
+ t.assert.deepStrictEqual(await result.json(), { hostname: 'localhost', port: fastify.server.address().port, hello: 'world' })
115
116
  })
116
- await t.test('https get request - test port fall back', (t, done) => {
117
- t.plan(3)
118
- sget({
117
+
118
+ await t.test('https get request - test port fall back', async t => {
119
+ t.plan(2)
120
+
121
+ const result = await request('https://localhost:' + fastify.server.address().port, {
119
122
  method: 'GET',
120
123
  headers: {
121
124
  host: 'example.com'
122
125
  },
123
- url: 'https://localhost:' + fastify.server.address().port,
124
- rejectUnauthorized: false
125
- }, (err, response, body) => {
126
- t.assert.ifError(err)
127
- t.assert.strictEqual(response.statusCode, 200)
128
- const parsedBody = JSON.parse(body)
129
- t.assert.strictEqual(parsedBody.port, null)
130
- done()
126
+ dispatcher: new Agent({
127
+ connect: {
128
+ rejectUnauthorized: false
129
+ }
130
+ })
131
131
  })
132
+
133
+ t.assert.strictEqual(result.statusCode, 200)
134
+ t.assert.deepStrictEqual(await result.body.json(), { hello: 'world', hostname: 'example.com', port: null })
132
135
  })
133
136
  })
@@ -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 = 84
8
+ const expectedErrors = 85
9
9
 
10
10
  test(`should expose ${expectedErrors} errors`, t => {
11
11
  t.plan(1)
@@ -7,7 +7,6 @@ const Request = require('../../lib/request')
7
7
  const Reply = require('../../lib/reply')
8
8
  const { kRouteContext } = require('../../lib/symbols')
9
9
  const buildSchema = require('../../lib/validation').compileSchemasForValidation
10
- const sget = require('simple-get').concat
11
10
 
12
11
  const Ajv = require('ajv')
13
12
  const ajv = new Ajv({ coerceTypes: true })
@@ -129,8 +128,8 @@ test('handler function - preValidationCallback with finished response', t => {
129
128
  internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
130
129
  })
131
130
 
132
- test('request should be defined in onSend Hook on post request with content type application/json', (t, done) => {
133
- t.plan(8)
131
+ test('request should be defined in onSend Hook on post request with content type application/json', async t => {
132
+ t.plan(6)
134
133
  const fastify = require('../..')()
135
134
 
136
135
  t.after(() => {
@@ -149,28 +148,24 @@ test('request should be defined in onSend Hook on post request with content type
149
148
  reply.send(200)
150
149
  })
151
150
 
152
- fastify.listen({ port: 0 }, err => {
153
- t.assert.ifError(err)
154
- sget({
155
- method: 'POST',
156
- url: 'http://localhost:' + fastify.server.address().port,
157
- headers: {
158
- 'content-type': 'application/json'
159
- }
160
- }, (err, response, body) => {
161
- t.assert.ifError(err)
162
- // a 400 error is expected because of no body
163
- t.assert.strictEqual(response.statusCode, 400)
164
- done()
165
- })
151
+ const fastifyServer = await fastify.listen({ port: 0 })
152
+ const result = await fetch(fastifyServer, {
153
+ method: 'POST',
154
+ headers: {
155
+ 'content-type': 'application/json'
156
+ }
166
157
  })
158
+
159
+ t.assert.strictEqual(result.status, 400)
167
160
  })
168
161
 
169
- test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', (t, done) => {
170
- t.plan(7)
162
+ test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', async t => {
163
+ t.plan(5)
171
164
  const fastify = require('../..')()
172
165
 
173
- t.after(() => { fastify.close() })
166
+ t.after(() => {
167
+ fastify.close()
168
+ })
174
169
 
175
170
  fastify.addHook('onSend', (request, reply, payload, done) => {
176
171
  t.assert.ok(request)
@@ -183,29 +178,25 @@ test('request should be defined in onSend Hook on post request with content type
183
178
  reply.send(200)
184
179
  })
185
180
 
186
- fastify.listen({ port: 0 }, err => {
187
- t.assert.ifError(err)
188
-
189
- sget({
190
- method: 'POST',
191
- url: 'http://localhost:' + fastify.server.address().port,
192
- headers: {
193
- 'content-type': 'application/x-www-form-urlencoded'
194
- }
195
- }, (err, response, body) => {
196
- t.assert.ifError(err)
197
- // a 415 error is expected because of missing content type parser
198
- t.assert.strictEqual(response.statusCode, 415)
199
- done()
200
- })
181
+ const fastifyServer = await fastify.listen({ port: 0 })
182
+ const result = await fetch(fastifyServer, {
183
+ method: 'POST',
184
+ headers: {
185
+ 'content-type': 'application/x-www-form-urlencoded'
186
+ }
201
187
  })
188
+
189
+ // a 415 error is expected because of missing content type parser
190
+ t.assert.strictEqual(result.status, 415)
202
191
  })
203
192
 
204
- test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', (t, done) => {
205
- t.plan(7)
193
+ test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', async t => {
194
+ t.plan(5)
206
195
  const fastify = require('../..')()
207
196
 
208
- t.after(() => { fastify.close() })
197
+ t.after(() => {
198
+ fastify.close()
199
+ })
209
200
 
210
201
  fastify.addHook('onSend', (request, reply, payload, done) => {
211
202
  t.assert.ok(request)
@@ -218,26 +209,20 @@ test('request should be defined in onSend Hook on options request with content t
218
209
  reply.send(200)
219
210
  })
220
211
 
221
- fastify.listen({ port: 0 }, err => {
222
- t.assert.ifError(err)
223
-
224
- sget({
225
- method: 'OPTIONS',
226
- url: 'http://localhost:' + fastify.server.address().port,
227
- headers: {
228
- 'content-type': 'application/x-www-form-urlencoded'
229
- }
230
- }, (err, response, body) => {
231
- t.assert.ifError(err)
232
- // Body parsing skipped, so no body sent
233
- t.assert.strictEqual(response.statusCode, 200)
234
- done()
235
- })
212
+ const fastifyServer = await fastify.listen({ port: 0 })
213
+ const result = await fetch(fastifyServer, {
214
+ method: 'OPTIONS',
215
+ headers: {
216
+ 'content-type': 'application/x-www-form-urlencoded'
217
+ }
236
218
  })
219
+
220
+ // Body parsing skipped, so no body sent
221
+ t.assert.strictEqual(result.status, 200)
237
222
  })
238
223
 
239
- test('request should respond with an error if an unserialized payload is sent inside an async handler', (t, done) => {
240
- t.plan(3)
224
+ test('request should respond with an error if an unserialized payload is sent inside an async handler', async t => {
225
+ t.plan(2)
241
226
 
242
227
  const fastify = require('../..')()
243
228
 
@@ -246,18 +231,16 @@ test('request should respond with an error if an unserialized payload is sent in
246
231
  return Promise.resolve(request.headers)
247
232
  })
248
233
 
249
- fastify.inject({
234
+ const res = await fastify.inject({
250
235
  method: 'GET',
251
236
  url: '/'
252
- }, (err, res) => {
253
- t.assert.ifError(err)
254
- t.assert.strictEqual(res.statusCode, 500)
255
- t.assert.deepStrictEqual(JSON.parse(res.payload), {
256
- error: 'Internal Server Error',
257
- code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
258
- message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',
259
- statusCode: 500
260
- })
261
- done()
237
+ })
238
+
239
+ t.assert.strictEqual(res.statusCode, 500)
240
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
241
+ error: 'Internal Server Error',
242
+ code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
243
+ message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',
244
+ statusCode: 500
262
245
  })
263
246
  })
@@ -3,7 +3,14 @@
3
3
  const { test } = require('node:test')
4
4
  const http = require('node:http')
5
5
  const Fastify = require('../fastify')
6
-
6
+ const { setTimeout } = require('node:timers')
7
+
8
+ /*
9
+ * Ensure that a socket error during the request does not cause the
10
+ * onSend hook to be called multiple times.
11
+ *
12
+ * @see https://github.com/fastify/fastify/issues/4959
13
+ */
7
14
  function runBadClientCall (reqOptions, payload) {
8
15
  let innerResolve, innerReject
9
16
  const promise = new Promise((resolve, reject) => {
@@ -25,7 +32,9 @@ function runBadClientCall (reqOptions, payload) {
25
32
 
26
33
  // Kill the socket immediately (before sending data)
27
34
  req.on('socket', (socket) => {
28
- setTimeout(() => { socket.destroy() }, 5)
35
+ socket.on('connect', () => {
36
+ setTimeout(() => { socket.destroy() }, 0)
37
+ })
29
38
  })
30
39
  req.on('error', innerResolve)
31
40
  req.write(postData)
@@ -34,7 +43,7 @@ function runBadClientCall (reqOptions, payload) {
34
43
  return promise
35
44
  }
36
45
 
37
- test('should handle a soket error', async (t) => {
46
+ test('should handle a socket error', async (t) => {
38
47
  t.plan(4)
39
48
  const fastify = Fastify()
40
49
 
@@ -3,10 +3,8 @@
3
3
  const { test, before } = require('node:test')
4
4
  const dns = require('node:dns').promises
5
5
  const dnsCb = require('node:dns')
6
- const sget = require('simple-get').concat
7
6
  const Fastify = require('../fastify')
8
7
  const helper = require('./helper')
9
- const { waitForCb } = require('./toolkit')
10
8
 
11
9
  let localhostForURL
12
10
 
@@ -90,7 +88,7 @@ test('listen logs the port as info', async t => {
90
88
 
91
89
  test('listen on localhost binds IPv4 and IPv6 - promise interface', async t => {
92
90
  const localAddresses = await dns.lookup('localhost', { all: true })
93
- t.plan(2 * localAddresses.length)
91
+ t.plan(3 * localAddresses.length)
94
92
 
95
93
  const app = Fastify()
96
94
  app.get('/', async () => 'hello localhost')
@@ -98,52 +96,42 @@ test('listen on localhost binds IPv4 and IPv6 - promise interface', async t => {
98
96
  await app.listen({ port: 0, host: 'localhost' })
99
97
 
100
98
  for (const lookup of localAddresses) {
101
- await new Promise((resolve, reject) => {
102
- sget({
103
- method: 'GET',
104
- url: getUrl(app, lookup)
105
- }, (err, response, body) => {
106
- if (err) { return reject(err) }
107
- t.assert.strictEqual(response.statusCode, 200)
108
- t.assert.deepStrictEqual(body.toString(), 'hello localhost')
109
- resolve()
110
- })
99
+ const result = await fetch(getUrl(app, lookup), {
100
+ method: 'GET'
111
101
  })
102
+
103
+ t.assert.ok(result.ok)
104
+ t.assert.deepEqual(result.status, 200)
105
+ t.assert.deepStrictEqual(await result.text(), 'hello localhost')
112
106
  }
113
107
  })
114
108
 
115
- test('listen on localhost binds to all interfaces (both IPv4 and IPv6 if present) - callback interface', (t, done) => {
116
- dnsCb.lookup('localhost', { all: true }, (err, lookups) => {
117
- t.plan(2 + (3 * lookups.length))
118
- t.assert.ifError(err)
119
-
120
- const app = Fastify()
121
- app.get('/', async () => 'hello localhost')
122
- app.listen({ port: 0, host: 'localhost' }, (err) => {
123
- t.assert.ifError(err)
124
- t.after(() => app.close())
125
-
126
- const { stepIn, patience } = waitForCb({ steps: lookups.length })
127
-
128
- // Loop over each lookup and perform the assertions
129
- if (lookups.length > 0) {
130
- for (const lookup of lookups) {
131
- sget({
132
- method: 'GET',
133
- url: getUrl(app, lookup)
134
- }, (err, response, body) => {
135
- t.assert.ifError(err)
136
- t.assert.strictEqual(response.statusCode, 200)
137
- t.assert.deepStrictEqual(body.toString(), 'hello localhost')
138
- // Call stepIn to report that a request has been completed
139
- stepIn()
140
- })
141
- }
142
- // When all requests have been completed, call done
143
- patience.then(() => done())
144
- }
109
+ test('listen on localhost binds to all interfaces (both IPv4 and IPv6 if present) - callback interface', async (t) => {
110
+ const lookups = await new Promise((resolve, reject) => {
111
+ dnsCb.lookup('localhost', { all: true }, (err, lookups) => {
112
+ if (err) return reject(err)
113
+ resolve(lookups)
145
114
  })
146
115
  })
116
+
117
+ t.plan(3 * lookups.length)
118
+
119
+ const app = Fastify()
120
+ app.get('/', async () => 'hello localhost')
121
+ t.after(() => app.close())
122
+
123
+ await app.listen({ port: 0, host: 'localhost' })
124
+
125
+ // Loop over each lookup and perform the assertions
126
+ for (const lookup of lookups) {
127
+ const result = await fetch(getUrl(app, lookup), {
128
+ method: 'GET'
129
+ })
130
+
131
+ t.assert.ok(result.ok)
132
+ t.assert.deepEqual(result.status, 200)
133
+ t.assert.deepStrictEqual(await result.text(), 'hello localhost')
134
+ }
147
135
  })
148
136
 
149
137
  test('addresses getter', async t => {