fastify 5.11.3 → 5.12.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 +1 -0
- package/docs/Guides/Serverless.md +34 -2
- package/docs/Reference/Reply.md +14 -0
- package/fastify.js +1 -1
- package/lib/content-type.js +4 -1
- package/lib/reply.js +11 -1
- package/package.json +2 -3
- package/test/content-parser.test.js +16 -18
- package/test/content-type.test.js +4 -0
- package/test/helper.js +73 -1
- package/test/hooks-async.test.js +1 -2
- package/test/hooks.test.js +1 -2
- package/test/internals/reply.test.js +76 -1
- package/test/listen.1.test.js +17 -52
- package/test/listen.5.test.js +6 -9
- package/test/logger/instantiation.test.js +1 -1
- package/test/logger/logging.test.js +1 -1
- package/test/logger/request.test.js +1 -1
- package/test/logger/response.test.js +1 -1
- package/test/plugin.4.test.js +5 -4
- package/test/reply-media-type.test.js +131 -0
- package/test/route-prefix.test.js +1 -1
- package/test/route.6.test.js +4 -11
- package/test/schema-feature.test.js +36 -65
- package/test/schema-serialization.test.js +1 -1
- package/test/schema-special-usage.test.js +1 -1
- package/test/schema-validation.test.js +1 -1
- package/test/stream.2.test.js +1 -1
- package/test/toolkit.js +0 -63
package/SPONSORS.md
CHANGED
|
@@ -19,6 +19,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
|
|
|
19
19
|
- [atagon GmbH](https://github.com/atagon-GmbH)
|
|
20
20
|
- [Photon](https://github.com/photon-hq)
|
|
21
21
|
- [N-iX](https://www.n-ix.com/)
|
|
22
|
+
- [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=fastify)
|
|
22
23
|
|
|
23
24
|
## Tier 2
|
|
24
25
|
|
|
@@ -256,9 +256,41 @@ curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \
|
|
|
256
256
|
{"message":"Hello Fastify!"}
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
+
#### Per-route logging
|
|
260
|
+
|
|
261
|
+
Google Cloud Functions exposes one entry point for your Fastify instance, so
|
|
262
|
+
the dashboard may show all HTTP traffic under that function. Instead of
|
|
263
|
+
deploying one function per endpoint, keep the single Fastify instance and emit
|
|
264
|
+
a structured log line after each request with the matched route:
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
fastify.addHook('onResponse', async (request, reply) => {
|
|
268
|
+
request.log.info({
|
|
269
|
+
route: request.routeOptions.url,
|
|
270
|
+
method: request.method,
|
|
271
|
+
statusCode: reply.statusCode,
|
|
272
|
+
responseTime: reply.elapsedTime
|
|
273
|
+
}, 'request completed')
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`request.routeOptions.url` is the route pattern, so `/users/123` and
|
|
278
|
+
`/users/456` are grouped under `/users/:id`. With Fastify's logger enabled, the
|
|
279
|
+
fields are emitted as structured log fields, as described in the [Cloud
|
|
280
|
+
Logging structured logging documentation](https://cloud.google.com/logging/docs/structured-logging)
|
|
281
|
+
and the [Cloud Functions logging guide](https://cloud.google.com/functions/docs/monitoring/logging).
|
|
282
|
+
In Cloud Logging, create a [log-based
|
|
283
|
+
metric](https://cloud.google.com/logging/docs/logs-based-metrics) or filter on
|
|
284
|
+
`jsonPayload.route` to get per-route request counts, latency, and error rates.
|
|
285
|
+
|
|
286
|
+
The same hook works when deploying through Firebase Functions with `onRequest`.
|
|
287
|
+
|
|
259
288
|
### References
|
|
260
289
|
- [Google Cloud Functions - Node.js Quickstart
|
|
261
|
-
](https://
|
|
290
|
+
](https://cloud.google.com/run/docs/quickstarts/functions/deploy-functions-gcloud)
|
|
291
|
+
- [Cloud Logging - Structured Logging](https://cloud.google.com/logging/docs/structured-logging)
|
|
292
|
+
- [Cloud Logging - Log-based Metrics](https://cloud.google.com/logging/docs/logs-based-metrics)
|
|
293
|
+
- [Cloud Functions - Logging](https://cloud.google.com/functions/docs/monitoring/logging)
|
|
262
294
|
|
|
263
295
|
## Google Firebase Functions
|
|
264
296
|
|
|
@@ -594,4 +626,4 @@ https://vercel.com/templates/backend/fastify-on-vercel).
|
|
|
594
626
|
[Fluid compute](https://vercel.com/docs/fluid-compute) currently
|
|
595
627
|
requires an explicit opt-in. Learn more about enabling Fluid compute
|
|
596
628
|
[here](
|
|
597
|
-
https://vercel.com/docs/fluid-compute#enabling-fluid-compute).
|
|
629
|
+
https://vercel.com/docs/fluid-compute#enabling-fluid-compute).
|
package/docs/Reference/Reply.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- [.code(statusCode)](#codestatuscode)
|
|
7
7
|
- [.elapsedTime](#elapsedtime)
|
|
8
8
|
- [.statusCode](#statuscode)
|
|
9
|
+
- [.mediaType](#mediatype)
|
|
9
10
|
- [.server](#server)
|
|
10
11
|
- [.header(key, value)](#headerkey-value)
|
|
11
12
|
- [.headers(object)](#headersobject)
|
|
@@ -51,6 +52,7 @@ object that exposes the following functions and properties:
|
|
|
51
52
|
- `.statusCode` - Read and set the HTTP status code.
|
|
52
53
|
- `.elapsedTime` - Returns the amount of time passed
|
|
53
54
|
since the request was received by Fastify.
|
|
55
|
+
- `.mediaType` - The media type extracted from `Content-Type` header.
|
|
54
56
|
- `.server` - A reference to the fastify instance object.
|
|
55
57
|
- `.header(name, value)` - Sets a response header.
|
|
56
58
|
- `.headers(object)` - Sets all the keys of the object as response headers.
|
|
@@ -129,6 +131,18 @@ if (reply.statusCode >= 299) {
|
|
|
129
131
|
reply.statusCode = 500
|
|
130
132
|
}
|
|
131
133
|
```
|
|
134
|
+
### .mediaType
|
|
135
|
+
<a id="mediatype"></a>
|
|
136
|
+
|
|
137
|
+
Returns the media type extracted from `Content-Type` header. When `Content-Type`
|
|
138
|
+
header is missing, it will return `undefined`.
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
if (reply.mediaType === 'image/gif') {
|
|
142
|
+
const versionBuffer = new Uint8Array(reply.payload, 3, 3)
|
|
143
|
+
reply.header('x-gif-version', new TextDecoder().decode(versionBuffer))
|
|
144
|
+
}
|
|
145
|
+
```
|
|
132
146
|
|
|
133
147
|
### .server
|
|
134
148
|
<a id="server"></a>
|
package/fastify.js
CHANGED
package/lib/content-type.js
CHANGED
|
@@ -183,7 +183,10 @@ class ContentType {
|
|
|
183
183
|
|
|
184
184
|
get isValid () { return this.#valid }
|
|
185
185
|
|
|
186
|
-
get mediaType () {
|
|
186
|
+
get mediaType () {
|
|
187
|
+
if (this.#valid === false) return undefined
|
|
188
|
+
return `${this.#type}/${this.#subtype}`
|
|
189
|
+
}
|
|
187
190
|
|
|
188
191
|
get type () { return this.#type }
|
|
189
192
|
|
package/lib/reply.js
CHANGED
|
@@ -93,6 +93,12 @@ Object.defineProperties(Reply.prototype, {
|
|
|
93
93
|
return (this[kReplyEndTime] || now()) - this[kReplyStartTime]
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
|
+
mediaType: {
|
|
97
|
+
get () {
|
|
98
|
+
const contentTypeHeader = this[kReplyHeaders]['content-type']
|
|
99
|
+
return ContentType.from(contentTypeHeader).mediaType
|
|
100
|
+
}
|
|
101
|
+
},
|
|
96
102
|
server: {
|
|
97
103
|
get () {
|
|
98
104
|
return this.request[kRouteContext].server
|
|
@@ -254,9 +260,13 @@ Reply.prototype.hasHeader = function (key) {
|
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
Reply.prototype.removeHeader = function (key) {
|
|
263
|
+
key = key.toLowerCase()
|
|
257
264
|
// Node.js does not like headers with keys set to undefined,
|
|
258
265
|
// so we have to delete the key.
|
|
259
|
-
delete this[kReplyHeaders][key
|
|
266
|
+
delete this[kReplyHeaders][key]
|
|
267
|
+
if (!this.raw.headersSent) {
|
|
268
|
+
this.raw.removeHeader(key)
|
|
269
|
+
}
|
|
260
270
|
return this
|
|
261
271
|
}
|
|
262
272
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -167,7 +167,6 @@
|
|
|
167
167
|
],
|
|
168
168
|
"devDependencies": {
|
|
169
169
|
"@jsumners/line-reporter": "^1.0.1",
|
|
170
|
-
"@sinonjs/fake-timers": "^11.2.2",
|
|
171
170
|
"@stylistic/eslint-plugin": "^5.1.0",
|
|
172
171
|
"@stylistic/eslint-plugin-js": "^4.1.0",
|
|
173
172
|
"@types/node": "^26.0.1",
|
|
@@ -214,7 +213,7 @@
|
|
|
214
213
|
"find-my-way": "^9.6.0",
|
|
215
214
|
"light-my-request": "^6.0.0",
|
|
216
215
|
"pino": "^9.14.0 || ^10.1.0",
|
|
217
|
-
"process-warning": "^5.
|
|
216
|
+
"process-warning": "^5.1.0",
|
|
218
217
|
"rfdc": "^1.3.1",
|
|
219
218
|
"secure-json-parse": "^4.0.0",
|
|
220
219
|
"semver": "^7.6.0",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
|
+
const { spyWarning } = require('process-warning')
|
|
4
5
|
const Fastify = require('..')
|
|
5
6
|
const keys = require('../lib/symbols')
|
|
6
7
|
const { FST_ERR_CTP_ALREADY_PRESENT, FST_ERR_CTP_INVALID_TYPE, FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('../lib/errors')
|
|
8
|
+
const { FSTSEC001 } = require('../lib/warnings')
|
|
7
9
|
|
|
8
10
|
const first = function (req, payload, done) {}
|
|
9
11
|
const second = function (req, payload, done) {}
|
|
@@ -488,41 +490,37 @@ test('Safeguard against content-type spoofing - string', async t => {
|
|
|
488
490
|
})
|
|
489
491
|
|
|
490
492
|
test('Warning against improper content-type - regexp', async t => {
|
|
491
|
-
await t.test('improper regex - text plain', (t
|
|
493
|
+
await t.test('improper regex - text plain', async (t) => {
|
|
492
494
|
t.plan(2)
|
|
493
|
-
const
|
|
495
|
+
const spyData = spyWarning(FSTSEC001)
|
|
496
|
+
t.after(spyData.restore)
|
|
494
497
|
|
|
495
|
-
|
|
496
|
-
function onWarning (warning) {
|
|
497
|
-
t.assert.strictEqual(warning.name, 'FastifySecurity')
|
|
498
|
-
t.assert.strictEqual(warning.code, 'FSTSEC001')
|
|
499
|
-
done()
|
|
500
|
-
}
|
|
501
|
-
t.after(() => process.removeListener('warning', onWarning))
|
|
498
|
+
const fastify = Fastify()
|
|
502
499
|
|
|
503
500
|
fastify.removeAllContentTypeParsers()
|
|
504
501
|
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
|
|
505
502
|
done(null, body)
|
|
506
503
|
})
|
|
504
|
+
|
|
505
|
+
await fastify.ready()
|
|
506
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['text\\/plain'], result: true }])
|
|
507
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
507
508
|
})
|
|
508
509
|
|
|
509
|
-
await t.test('improper regex - application json', (t
|
|
510
|
+
await t.test('improper regex - application json', async (t) => {
|
|
510
511
|
t.plan(2)
|
|
512
|
+
const spyData = spyWarning(FSTSEC001)
|
|
513
|
+
t.after(spyData.restore)
|
|
511
514
|
const fastify = Fastify()
|
|
512
515
|
|
|
513
|
-
process.on('warning', onWarning)
|
|
514
|
-
function onWarning (warning) {
|
|
515
|
-
t.assert.strictEqual(warning.name, 'FastifySecurity')
|
|
516
|
-
t.assert.strictEqual(warning.code, 'FSTSEC001')
|
|
517
|
-
done()
|
|
518
|
-
}
|
|
519
|
-
t.after(() => process.removeListener('warning', onWarning))
|
|
520
|
-
|
|
521
516
|
fastify.removeAllContentTypeParsers()
|
|
522
517
|
|
|
523
518
|
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
|
|
524
519
|
done(null, body)
|
|
525
520
|
})
|
|
521
|
+
|
|
522
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['application\\/json'], result: true }])
|
|
523
|
+
t.assert.deepEqual(spyData.callCount(), 1)
|
|
526
524
|
})
|
|
527
525
|
})
|
|
528
526
|
|
|
@@ -46,18 +46,22 @@ describe('ContentType class', () => {
|
|
|
46
46
|
test('returns empty instance for empty value', (t) => {
|
|
47
47
|
let found = new ContentType('')
|
|
48
48
|
t.assert.equal(found.isEmpty, true)
|
|
49
|
+
t.assert.equal(found.mediaType, undefined)
|
|
49
50
|
|
|
50
51
|
found = new ContentType('undefined')
|
|
51
52
|
t.assert.equal(found.isEmpty, true)
|
|
53
|
+
t.assert.equal(found.mediaType, undefined)
|
|
52
54
|
|
|
53
55
|
found = new ContentType()
|
|
54
56
|
t.assert.equal(found.isEmpty, true)
|
|
57
|
+
t.assert.equal(found.mediaType, undefined)
|
|
55
58
|
})
|
|
56
59
|
|
|
57
60
|
test('indicates media type is not correct format', (t) => {
|
|
58
61
|
let found = new ContentType('foo')
|
|
59
62
|
t.assert.equal(found.isEmpty, true)
|
|
60
63
|
t.assert.equal(found.isValid, false)
|
|
64
|
+
t.assert.equal(found.mediaType, undefined)
|
|
61
65
|
|
|
62
66
|
found = new ContentType('foo /bar')
|
|
63
67
|
t.assert.equal(found.isEmpty, true)
|
package/test/helper.js
CHANGED
|
@@ -4,11 +4,42 @@ const dns = require('node:dns').promises
|
|
|
4
4
|
const stream = require('node:stream')
|
|
5
5
|
const { promisify } = require('node:util')
|
|
6
6
|
const symbols = require('../lib/symbols')
|
|
7
|
-
const { waitForCb } = require('./toolkit')
|
|
8
7
|
const assert = require('node:assert')
|
|
9
8
|
|
|
10
9
|
module.exports.sleep = promisify(setTimeout)
|
|
11
10
|
|
|
11
|
+
function waitForCb (options) {
|
|
12
|
+
let count = null
|
|
13
|
+
let done = false
|
|
14
|
+
let iResolve
|
|
15
|
+
let iReject
|
|
16
|
+
|
|
17
|
+
function stepIn () {
|
|
18
|
+
if (done) {
|
|
19
|
+
iReject(new Error('Unexpected done call'))
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (--count) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
done = true
|
|
28
|
+
iResolve()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const patience = new Promise((resolve, reject) => {
|
|
32
|
+
iResolve = resolve
|
|
33
|
+
iReject = reject
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
count = options.steps || 1
|
|
37
|
+
done = false
|
|
38
|
+
|
|
39
|
+
return { stepIn, patience }
|
|
40
|
+
}
|
|
41
|
+
module.exports.waitForCb = waitForCb
|
|
42
|
+
|
|
12
43
|
/**
|
|
13
44
|
* @param method HTTP request method
|
|
14
45
|
* @param t node:test instance
|
|
@@ -494,3 +525,44 @@ module.exports.getServerUrl = function (app) {
|
|
|
494
525
|
? `http://[${address}]:${port}`
|
|
495
526
|
: `http://${address}:${port}`
|
|
496
527
|
}
|
|
528
|
+
|
|
529
|
+
module.exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) {
|
|
530
|
+
if (typeof expected !== 'object' || expected === null) {
|
|
531
|
+
return actual === expected
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (typeof actual !== 'object' || actual === null) {
|
|
535
|
+
return false
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (Array.isArray(expected)) {
|
|
539
|
+
if (!Array.isArray(actual)) return false
|
|
540
|
+
if (expected.length > actual.length) return false
|
|
541
|
+
|
|
542
|
+
for (let i = 0; i < expected.length; i++) {
|
|
543
|
+
if (!partialDeepStrictEqual(actual[i], expected[i])) {
|
|
544
|
+
return false
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return true
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
for (const key of Object.keys(expected)) {
|
|
551
|
+
if (!(key in actual)) return false
|
|
552
|
+
if (!partialDeepStrictEqual(actual[key], expected[key])) {
|
|
553
|
+
return false
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return true
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
module.exports.assertNoWarning = function (t) {
|
|
561
|
+
function doNotWarn () {
|
|
562
|
+
t.assert.fail('no warning')
|
|
563
|
+
}
|
|
564
|
+
process.on('warning', doNotWarn)
|
|
565
|
+
t.after(() => {
|
|
566
|
+
process.off('warning', doNotWarn)
|
|
567
|
+
})
|
|
568
|
+
}
|
package/test/hooks-async.test.js
CHANGED
|
@@ -4,8 +4,7 @@ const { Readable } = require('node:stream')
|
|
|
4
4
|
const { test, describe } = require('node:test')
|
|
5
5
|
const Fastify = require('../fastify')
|
|
6
6
|
const fs = require('node:fs')
|
|
7
|
-
const { sleep } = require('./helper')
|
|
8
|
-
const { waitForCb } = require('./toolkit')
|
|
7
|
+
const { sleep, waitForCb } = require('./helper')
|
|
9
8
|
|
|
10
9
|
process.removeAllListeners('warning')
|
|
11
10
|
|
package/test/hooks.test.js
CHANGED
|
@@ -10,8 +10,7 @@ const symbols = require('../lib/symbols.js')
|
|
|
10
10
|
const payload = { hello: 'world' }
|
|
11
11
|
const proxyquire = require('proxyquire')
|
|
12
12
|
const { connect } = require('node:net')
|
|
13
|
-
const { sleep } = require('./helper')
|
|
14
|
-
const { waitForCb } = require('./toolkit.js')
|
|
13
|
+
const { sleep, waitForCb } = require('./helper')
|
|
15
14
|
const { fetch } = require('undici')
|
|
16
15
|
|
|
17
16
|
process.removeAllListeners('warning')
|
|
@@ -34,7 +34,7 @@ const doGet = async function (url) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
test('Once called, Reply should return an object with methods', t => {
|
|
37
|
-
t.plan(
|
|
37
|
+
t.plan(16)
|
|
38
38
|
const response = { res: 'res' }
|
|
39
39
|
const context = {
|
|
40
40
|
config: { onSend: [] },
|
|
@@ -49,6 +49,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
49
49
|
t.assert.strictEqual(typeof reply[kReplyErrorHandlerCalled], 'boolean')
|
|
50
50
|
t.assert.strictEqual(typeof reply.send, 'function')
|
|
51
51
|
t.assert.strictEqual(typeof reply.code, 'function')
|
|
52
|
+
t.assert.strictEqual(typeof reply.mediaType, 'string')
|
|
52
53
|
t.assert.strictEqual(typeof reply.status, 'function')
|
|
53
54
|
t.assert.strictEqual(typeof reply.header, 'function')
|
|
54
55
|
t.assert.strictEqual(typeof reply.serialize, 'function')
|
|
@@ -1114,6 +1115,80 @@ test('reply.removeHeader can remove the value', async t => {
|
|
|
1114
1115
|
await fetch(`${fastifyServer}/headers`)
|
|
1115
1116
|
})
|
|
1116
1117
|
|
|
1118
|
+
test('reply.removeHeader removes raw headers', async t => {
|
|
1119
|
+
t.plan(9)
|
|
1120
|
+
|
|
1121
|
+
const fastify = require('../../')()
|
|
1122
|
+
|
|
1123
|
+
t.after(() => fastify.close())
|
|
1124
|
+
|
|
1125
|
+
fastify.get('/headers', function (req, reply) {
|
|
1126
|
+
reply.raw.setHeader('X-Foo', 'raw')
|
|
1127
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), 'raw')
|
|
1128
|
+
t.assert.strictEqual(reply.getHeaders()['x-foo'], 'raw')
|
|
1129
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), true)
|
|
1130
|
+
|
|
1131
|
+
t.assert.strictEqual(reply.removeHeader('x-FoO'), reply)
|
|
1132
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), undefined)
|
|
1133
|
+
t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false)
|
|
1134
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), false)
|
|
1135
|
+
t.assert.strictEqual(reply.removeHeader('X-FOO'), reply)
|
|
1136
|
+
|
|
1137
|
+
reply.send()
|
|
1138
|
+
})
|
|
1139
|
+
|
|
1140
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1141
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1142
|
+
t.assert.strictEqual(response.headers.get('x-foo'), null)
|
|
1143
|
+
})
|
|
1144
|
+
|
|
1145
|
+
test('reply.removeHeader removes layered headers', async t => {
|
|
1146
|
+
t.plan(7)
|
|
1147
|
+
|
|
1148
|
+
const fastify = require('../../')()
|
|
1149
|
+
|
|
1150
|
+
t.after(() => fastify.close())
|
|
1151
|
+
|
|
1152
|
+
fastify.get('/headers', function (req, reply) {
|
|
1153
|
+
reply.raw.setHeader('x-foo', 'raw')
|
|
1154
|
+
reply.header('x-foo', 'fastify')
|
|
1155
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), 'fastify')
|
|
1156
|
+
t.assert.strictEqual(reply.getHeaders()['x-foo'], 'fastify')
|
|
1157
|
+
|
|
1158
|
+
t.assert.strictEqual(reply.removeHeader('x-foo'), reply)
|
|
1159
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), undefined)
|
|
1160
|
+
t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false)
|
|
1161
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), false)
|
|
1162
|
+
|
|
1163
|
+
reply.send()
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1166
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1167
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1168
|
+
t.assert.strictEqual(response.headers.get('x-foo'), null)
|
|
1169
|
+
})
|
|
1170
|
+
|
|
1171
|
+
test('reply.removeHeader does not throw after headers are sent', async t => {
|
|
1172
|
+
t.plan(3)
|
|
1173
|
+
|
|
1174
|
+
const fastify = require('../../')()
|
|
1175
|
+
|
|
1176
|
+
t.after(() => fastify.close())
|
|
1177
|
+
|
|
1178
|
+
fastify.get('/headers', function (req, reply) {
|
|
1179
|
+
reply.hijack()
|
|
1180
|
+
reply.raw.setHeader('x-foo', 'raw')
|
|
1181
|
+
reply.raw.flushHeaders()
|
|
1182
|
+
t.assert.strictEqual(reply.raw.headersSent, true)
|
|
1183
|
+
t.assert.doesNotThrow(() => reply.removeHeader('x-foo'))
|
|
1184
|
+
reply.raw.end()
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1188
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1189
|
+
t.assert.strictEqual(response.headers.get('x-foo'), 'raw')
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1117
1192
|
test('reply.header can reset the value', async t => {
|
|
1118
1193
|
t.plan(1)
|
|
1119
1194
|
|
package/test/listen.1.test.js
CHANGED
|
@@ -4,6 +4,7 @@ const { networkInterfaces } = require('node:os')
|
|
|
4
4
|
const { test, before } = require('node:test')
|
|
5
5
|
const Fastify = require('..')
|
|
6
6
|
const helper = require('./helper')
|
|
7
|
+
const { assertNoWarning } = require('./helper')
|
|
7
8
|
|
|
8
9
|
let localhost
|
|
9
10
|
let localhostForURL
|
|
@@ -13,33 +14,22 @@ before(async function () {
|
|
|
13
14
|
})
|
|
14
15
|
|
|
15
16
|
test('listen works without arguments', async t => {
|
|
16
|
-
|
|
17
|
-
t.assert.fail('should not be deprecated')
|
|
18
|
-
}
|
|
19
|
-
process.on('warning', doNotWarn)
|
|
17
|
+
assertNoWarning(t)
|
|
20
18
|
|
|
21
19
|
const fastify = Fastify()
|
|
22
|
-
|
|
23
|
-
fastify.close()
|
|
24
|
-
process.removeListener('warning', doNotWarn)
|
|
25
|
-
})
|
|
20
|
+
|
|
26
21
|
await fastify.listen()
|
|
27
22
|
const address = fastify.server.address()
|
|
28
23
|
t.assert.strictEqual(address.address, localhost)
|
|
29
24
|
t.assert.ok(address.port > 0)
|
|
25
|
+
await fastify.close()
|
|
30
26
|
})
|
|
31
27
|
|
|
32
28
|
test('Async/await listen with arguments', async t => {
|
|
33
|
-
|
|
34
|
-
t.assert.fail('should not be deprecated')
|
|
35
|
-
}
|
|
36
|
-
process.on('warning', doNotWarn)
|
|
29
|
+
assertNoWarning(t)
|
|
37
30
|
|
|
38
31
|
const fastify = Fastify()
|
|
39
|
-
|
|
40
|
-
fastify.close()
|
|
41
|
-
process.removeListener('warning', doNotWarn)
|
|
42
|
-
})
|
|
32
|
+
|
|
43
33
|
const addr = await fastify.listen({ port: 0, host: '0.0.0.0' })
|
|
44
34
|
const address = fastify.server.address()
|
|
45
35
|
const { protocol, hostname, port, pathname } = new URL(addr)
|
|
@@ -55,39 +45,27 @@ test('Async/await listen with arguments', async t => {
|
|
|
55
45
|
family: 'IPv4',
|
|
56
46
|
port: address.port
|
|
57
47
|
})
|
|
48
|
+
await fastify.close()
|
|
58
49
|
})
|
|
59
50
|
|
|
60
51
|
test('listen accepts a callback', (t, done) => {
|
|
61
52
|
t.plan(2)
|
|
62
|
-
|
|
63
|
-
t.assert.fail('should not be deprecated')
|
|
64
|
-
}
|
|
65
|
-
process.on('warning', doNotWarn)
|
|
53
|
+
assertNoWarning(t)
|
|
66
54
|
|
|
67
55
|
const fastify = Fastify()
|
|
68
|
-
|
|
69
|
-
fastify.close()
|
|
70
|
-
process.removeListener('warning', doNotWarn)
|
|
71
|
-
})
|
|
56
|
+
|
|
72
57
|
fastify.listen({ port: 0 }, (err) => {
|
|
73
58
|
t.assert.ifError(err)
|
|
74
59
|
t.assert.strictEqual(fastify.server.address().address, localhost)
|
|
75
|
-
done
|
|
60
|
+
fastify.close(done)
|
|
76
61
|
})
|
|
77
62
|
})
|
|
78
63
|
|
|
79
64
|
test('listen accepts options and a callback', (t, done) => {
|
|
80
65
|
t.plan(1)
|
|
81
|
-
|
|
82
|
-
t.assert.fail('should not be deprecated')
|
|
83
|
-
}
|
|
84
|
-
process.on('warning', doNotWarn)
|
|
66
|
+
assertNoWarning(t)
|
|
85
67
|
|
|
86
68
|
const fastify = Fastify()
|
|
87
|
-
t.after(() => {
|
|
88
|
-
fastify.close()
|
|
89
|
-
process.removeListener('warning', doNotWarn)
|
|
90
|
-
})
|
|
91
69
|
fastify.listen({
|
|
92
70
|
port: 0,
|
|
93
71
|
host: 'localhost',
|
|
@@ -98,7 +76,7 @@ test('listen accepts options and a callback', (t, done) => {
|
|
|
98
76
|
ipv6Only: false
|
|
99
77
|
}, (err) => {
|
|
100
78
|
t.assert.ifError(err)
|
|
101
|
-
done
|
|
79
|
+
fastify.close(done)
|
|
102
80
|
})
|
|
103
81
|
})
|
|
104
82
|
|
|
@@ -118,37 +96,24 @@ test('listen after Promise.resolve()', (t, done) => {
|
|
|
118
96
|
})
|
|
119
97
|
|
|
120
98
|
test('listen works with undefined host', async t => {
|
|
121
|
-
|
|
122
|
-
t.assert.fail('should not be deprecated')
|
|
123
|
-
}
|
|
124
|
-
process.on('warning', doNotWarn)
|
|
99
|
+
assertNoWarning(t)
|
|
125
100
|
|
|
126
101
|
const fastify = Fastify()
|
|
127
|
-
t.after(() => fastify.close())
|
|
128
|
-
t.after(() => {
|
|
129
|
-
fastify.close()
|
|
130
|
-
process.removeListener('warning', doNotWarn)
|
|
131
|
-
})
|
|
132
102
|
await fastify.listen({ host: undefined, port: 0 })
|
|
133
103
|
const address = fastify.server.address()
|
|
134
104
|
t.assert.strictEqual(address.address, localhost)
|
|
135
105
|
t.assert.ok(address.port > 0)
|
|
106
|
+
await fastify.close()
|
|
136
107
|
})
|
|
137
108
|
|
|
138
109
|
test('listen works with null host', async t => {
|
|
139
|
-
|
|
140
|
-
t.fail('should not be deprecated')
|
|
141
|
-
}
|
|
142
|
-
process.on('warning', doNotWarn)
|
|
110
|
+
assertNoWarning(t)
|
|
143
111
|
|
|
144
112
|
const fastify = Fastify()
|
|
145
|
-
|
|
146
|
-
t.after(() => {
|
|
147
|
-
fastify.close()
|
|
148
|
-
process.removeListener('warning', doNotWarn)
|
|
149
|
-
})
|
|
113
|
+
|
|
150
114
|
await fastify.listen({ host: null, port: 0 })
|
|
151
115
|
const address = fastify.server.address()
|
|
152
116
|
t.assert.strictEqual(address.address, localhost)
|
|
153
117
|
t.assert.ok(address.port > 0)
|
|
118
|
+
await fastify.close()
|
|
154
119
|
})
|
package/test/listen.5.test.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const net = require('node:net')
|
|
5
|
-
const Fastify = require('../fastify')
|
|
6
5
|
const { once } = require('node:events')
|
|
6
|
+
const { spyWarning } = require('process-warning')
|
|
7
|
+
const Fastify = require('../fastify')
|
|
7
8
|
const { FSTWRN003 } = require('../lib/warnings.js')
|
|
8
9
|
|
|
9
10
|
function createDeferredPromise () {
|
|
@@ -102,21 +103,17 @@ test('same port conflict and success should not fire callback multiple times - p
|
|
|
102
103
|
test('should emit a warning when using async callback', (t, done) => {
|
|
103
104
|
t.plan(2)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
function onWarning (warning) {
|
|
107
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
108
|
-
t.assert.strictEqual(warning.code, FSTWRN003.code)
|
|
109
|
-
}
|
|
110
|
-
|
|
106
|
+
const spyData = spyWarning(FSTWRN003)
|
|
111
107
|
const fastify = Fastify()
|
|
112
108
|
|
|
113
109
|
t.after(async () => {
|
|
114
110
|
await fastify.close()
|
|
115
|
-
|
|
116
|
-
FSTWRN003.emitted = false
|
|
111
|
+
spyData.restore()
|
|
117
112
|
})
|
|
118
113
|
|
|
119
114
|
fastify.listen({ port: 0 }, async function doNotUseAsyncCallback () {
|
|
115
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['listen method'], result: true }])
|
|
116
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
120
117
|
done()
|
|
121
118
|
})
|
|
122
119
|
})
|
|
@@ -14,7 +14,7 @@ const helper = require('../helper')
|
|
|
14
14
|
const { FST_ERR_LOG_INVALID_LOGGER } = require('../../lib/errors')
|
|
15
15
|
const { once, on } = stream
|
|
16
16
|
const { createTempFile, request } = require('./logger-test-utils')
|
|
17
|
-
const { partialDeepStrictEqual } = require('../
|
|
17
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
18
18
|
|
|
19
19
|
t.test('logger instantiation', { timeout: 60000 }, async (t) => {
|
|
20
20
|
let localhost
|
|
@@ -11,7 +11,7 @@ const { LogController } = require('../../lib/log-controller')
|
|
|
11
11
|
const helper = require('../helper')
|
|
12
12
|
const { once, on } = stream
|
|
13
13
|
const { request } = require('./logger-test-utils')
|
|
14
|
-
const { partialDeepStrictEqual } = require('../
|
|
14
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
15
15
|
|
|
16
16
|
t.test('logging', { timeout: 60000 }, async (t) => {
|
|
17
17
|
let localhost
|
|
@@ -9,7 +9,7 @@ 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('../
|
|
12
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
13
13
|
|
|
14
14
|
t.test('request', { timeout: 60000 }, async (t) => {
|
|
15
15
|
let localhost
|
|
@@ -7,7 +7,7 @@ const split = require('split2')
|
|
|
7
7
|
const pino = require('pino')
|
|
8
8
|
|
|
9
9
|
const Fastify = require('../../fastify')
|
|
10
|
-
const { partialDeepStrictEqual } = require('../
|
|
10
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
11
11
|
const { on } = stream
|
|
12
12
|
|
|
13
13
|
t.test('response serialization', { timeout: 60000 }, async (t) => {
|
package/test/plugin.4.test.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { test, describe } = require('node:test')
|
|
4
4
|
const Fastify = require('../fastify')
|
|
5
5
|
const fp = require('fastify-plugin')
|
|
6
|
-
const fakeTimer = require('@sinonjs/fake-timers')
|
|
7
6
|
const { FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER } = require('../lib/errors')
|
|
8
7
|
|
|
9
8
|
test('pluginTimeout', (t, testDone) => {
|
|
@@ -46,12 +45,14 @@ test('pluginTimeout - named function', (t, testDone) => {
|
|
|
46
45
|
|
|
47
46
|
test('pluginTimeout default', (t, testDone) => {
|
|
48
47
|
t.plan(5)
|
|
49
|
-
|
|
48
|
+
t.mock.timers.enable({
|
|
49
|
+
apis: ['setTimeout', 'Date']
|
|
50
|
+
})
|
|
50
51
|
|
|
51
52
|
const fastify = Fastify()
|
|
52
53
|
fastify.register(function (app, opts, done) {
|
|
53
54
|
// default time elapsed without calling done
|
|
54
|
-
|
|
55
|
+
t.mock.timers.tick(10000)
|
|
55
56
|
})
|
|
56
57
|
|
|
57
58
|
fastify.ready((err) => {
|
|
@@ -64,7 +65,7 @@ test('pluginTimeout default', (t, testDone) => {
|
|
|
64
65
|
testDone()
|
|
65
66
|
})
|
|
66
67
|
|
|
67
|
-
t.after(
|
|
68
|
+
t.after(() => t.mock.timers.reset())
|
|
68
69
|
})
|
|
69
70
|
|
|
70
71
|
test('plugin metadata - version', (t, testDone) => {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
|
|
6
|
+
test('reply.mediaType should match the content-type header', async (t) => {
|
|
7
|
+
t.plan(2)
|
|
8
|
+
|
|
9
|
+
const fastify = Fastify()
|
|
10
|
+
|
|
11
|
+
fastify.get('/', (request, reply) => {
|
|
12
|
+
reply.type('application/json')
|
|
13
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
14
|
+
reply.send({ mediaType: reply.mediaType })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const response = await fastify.inject({
|
|
18
|
+
method: 'GET',
|
|
19
|
+
url: '/'
|
|
20
|
+
})
|
|
21
|
+
const body = await response.json()
|
|
22
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('reply.mediaType should strip the charset parameter', async (t) => {
|
|
26
|
+
t.plan(2)
|
|
27
|
+
|
|
28
|
+
const fastify = Fastify()
|
|
29
|
+
|
|
30
|
+
fastify.get('/', (request, reply) => {
|
|
31
|
+
reply.header('content-type', 'application/json; charset=utf-8')
|
|
32
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
33
|
+
reply.send({ mediaType: reply.mediaType })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const response = await fastify.inject({
|
|
37
|
+
method: 'GET',
|
|
38
|
+
url: '/'
|
|
39
|
+
})
|
|
40
|
+
const body = await response.json()
|
|
41
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('reply.mediaType should strip the space', async (t) => {
|
|
45
|
+
t.plan(2)
|
|
46
|
+
|
|
47
|
+
const fastify = Fastify()
|
|
48
|
+
|
|
49
|
+
fastify.get('/', (request, reply) => {
|
|
50
|
+
reply.header('content-type', ' application/json ; charset=utf-8')
|
|
51
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
52
|
+
reply.send({ mediaType: reply.mediaType })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const response = await fastify.inject({
|
|
56
|
+
method: 'GET',
|
|
57
|
+
url: '/'
|
|
58
|
+
})
|
|
59
|
+
const body = await response.json()
|
|
60
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('reply.mediaType is undefined when content-type is not set', async (t) => {
|
|
64
|
+
t.plan(2)
|
|
65
|
+
|
|
66
|
+
const fastify = Fastify()
|
|
67
|
+
|
|
68
|
+
fastify.get('/', (request, reply) => {
|
|
69
|
+
t.assert.strictEqual(reply.mediaType, undefined)
|
|
70
|
+
reply.send({ mediaType: reply.mediaType })
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const response = await fastify.inject({
|
|
74
|
+
method: 'GET',
|
|
75
|
+
url: '/'
|
|
76
|
+
})
|
|
77
|
+
const body = await response.json()
|
|
78
|
+
t.assert.strictEqual(body.mediaType, undefined)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('reply.mediaType supported in hooks', async (t) => {
|
|
82
|
+
t.plan(5)
|
|
83
|
+
|
|
84
|
+
const fastify = Fastify()
|
|
85
|
+
|
|
86
|
+
fastify.get('/', {
|
|
87
|
+
preHandler: (request, reply, done) => {
|
|
88
|
+
reply.type('application/json')
|
|
89
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
90
|
+
done()
|
|
91
|
+
},
|
|
92
|
+
preSerialization: (request, reply, payload, done) => {
|
|
93
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
94
|
+
done(null, payload)
|
|
95
|
+
},
|
|
96
|
+
onSend: (request, reply, payload, done) => {
|
|
97
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
98
|
+
done(null, payload)
|
|
99
|
+
}
|
|
100
|
+
}, (request, reply) => {
|
|
101
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
102
|
+
reply.send({ mediaType: reply.mediaType })
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const response = await fastify.inject({
|
|
106
|
+
method: 'GET',
|
|
107
|
+
url: '/'
|
|
108
|
+
})
|
|
109
|
+
const body = await response.json()
|
|
110
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('reply.mediaType should reflect the last type set', async (t) => {
|
|
114
|
+
t.plan(2)
|
|
115
|
+
|
|
116
|
+
const fastify = Fastify()
|
|
117
|
+
|
|
118
|
+
fastify.get('/', (request, reply) => {
|
|
119
|
+
reply.type('application/json')
|
|
120
|
+
reply.type('text/plain')
|
|
121
|
+
t.assert.strictEqual(reply.mediaType, 'text/plain')
|
|
122
|
+
reply.send(`mediaType = ${reply.mediaType}`)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const response = await fastify.inject({
|
|
126
|
+
method: 'GET',
|
|
127
|
+
url: '/'
|
|
128
|
+
})
|
|
129
|
+
const body = response.body
|
|
130
|
+
t.assert.strictEqual(body, 'mediaType = text/plain')
|
|
131
|
+
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
|
-
const { waitForCb } = require('./
|
|
5
|
+
const { waitForCb } = require('./helper')
|
|
6
6
|
|
|
7
7
|
test('Prefix options should add a prefix for all the routes inside a register / 1', (t, testDone) => {
|
|
8
8
|
t.plan(6)
|
package/test/route.6.test.js
CHANGED
|
@@ -4,6 +4,7 @@ const stream = require('node:stream')
|
|
|
4
4
|
const { ReadableStream } = require('node:stream/web')
|
|
5
5
|
const { test } = require('node:test')
|
|
6
6
|
const Fastify = require('..')
|
|
7
|
+
const { assertNoWarning } = require('./helper')
|
|
7
8
|
|
|
8
9
|
test('Creates a HEAD route for a GET one with prefixTrailingSlash', async (t) => {
|
|
9
10
|
t.plan(1)
|
|
@@ -281,6 +282,8 @@ test('route onSend can be function or array of functions', async t => {
|
|
|
281
282
|
})
|
|
282
283
|
|
|
283
284
|
test('no warning for exposeHeadRoute', async t => {
|
|
285
|
+
assertNoWarning(t)
|
|
286
|
+
|
|
284
287
|
const fastify = Fastify()
|
|
285
288
|
|
|
286
289
|
fastify.route({
|
|
@@ -292,15 +295,5 @@ test('no warning for exposeHeadRoute', async t => {
|
|
|
292
295
|
}
|
|
293
296
|
})
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
t.assert.fail('no warning')
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
process.on('warning', listener)
|
|
300
|
-
|
|
301
|
-
await fastify.listen({ port: 0 })
|
|
302
|
-
|
|
303
|
-
process.removeListener('warning', listener)
|
|
304
|
-
|
|
305
|
-
await fastify.close()
|
|
298
|
+
await fastify.ready()
|
|
306
299
|
})
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
|
-
const Fastify = require('..')
|
|
5
4
|
const fp = require('fastify-plugin')
|
|
5
|
+
const { spyWarning } = require('process-warning')
|
|
6
|
+
const Fastify = require('..')
|
|
6
7
|
const deepClone = require('rfdc')({ circles: true, proto: false })
|
|
7
8
|
const Ajv = require('ajv')
|
|
8
9
|
const { kSchemaController } = require('../lib/symbols.js')
|
|
9
10
|
const { FSTWRN001 } = require('../lib/warnings')
|
|
10
|
-
const { waitForCb } = require('./
|
|
11
|
+
const { waitForCb } = require('./helper')
|
|
11
12
|
|
|
12
13
|
const echoParams = (req, reply) => { reply.send(req.params) }
|
|
13
14
|
const echoBody = (req, reply) => { reply.send(req.body) }
|
|
@@ -320,18 +321,10 @@ test('Should not change the input schemas', (t, testDone) => {
|
|
|
320
321
|
|
|
321
322
|
test('Should emit warning if the schema headers is undefined', (t, testDone) => {
|
|
322
323
|
t.plan(4)
|
|
323
|
-
const
|
|
324
|
+
const spyData = spyWarning(FSTWRN001)
|
|
325
|
+
t.after(spyData.restore)
|
|
324
326
|
|
|
325
|
-
|
|
326
|
-
function onWarning (warning) {
|
|
327
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
328
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
t.after(() => {
|
|
332
|
-
process.removeListener('warning', onWarning)
|
|
333
|
-
FSTWRN001.emitted = false
|
|
334
|
-
})
|
|
327
|
+
const fastify = Fastify()
|
|
335
328
|
|
|
336
329
|
fastify.post('/:id', {
|
|
337
330
|
handler: echoParams,
|
|
@@ -346,24 +339,18 @@ test('Should emit warning if the schema headers is undefined', (t, testDone) =>
|
|
|
346
339
|
}, (error, res) => {
|
|
347
340
|
t.assert.ifError(error)
|
|
348
341
|
t.assert.strictEqual(res.statusCode, 200)
|
|
342
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['headers', 'POST', '/:id'], result: true }])
|
|
343
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
349
344
|
testDone()
|
|
350
345
|
})
|
|
351
346
|
})
|
|
352
347
|
|
|
353
348
|
test('Should emit warning if the schema body is undefined', (t, testDone) => {
|
|
354
349
|
t.plan(4)
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
process.on('warning', onWarning)
|
|
358
|
-
function onWarning (warning) {
|
|
359
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
360
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
361
|
-
}
|
|
350
|
+
const spyData = spyWarning(FSTWRN001)
|
|
351
|
+
t.after(spyData.restore)
|
|
362
352
|
|
|
363
|
-
|
|
364
|
-
process.removeListener('warning', onWarning)
|
|
365
|
-
FSTWRN001.emitted = false
|
|
366
|
-
})
|
|
353
|
+
const fastify = Fastify()
|
|
367
354
|
|
|
368
355
|
fastify.post('/:id', {
|
|
369
356
|
handler: echoParams,
|
|
@@ -378,24 +365,18 @@ test('Should emit warning if the schema body is undefined', (t, testDone) => {
|
|
|
378
365
|
}, (error, res) => {
|
|
379
366
|
t.assert.ifError(error)
|
|
380
367
|
t.assert.strictEqual(res.statusCode, 200)
|
|
368
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['body', 'POST', '/:id'], result: true }])
|
|
369
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
381
370
|
testDone()
|
|
382
371
|
})
|
|
383
372
|
})
|
|
384
373
|
|
|
385
374
|
test('Should emit warning if the schema query is undefined', (t, testDone) => {
|
|
386
375
|
t.plan(4)
|
|
387
|
-
const
|
|
376
|
+
const spyData = spyWarning(FSTWRN001)
|
|
377
|
+
t.after(spyData.restore)
|
|
388
378
|
|
|
389
|
-
|
|
390
|
-
function onWarning (warning) {
|
|
391
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
392
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
t.after(() => {
|
|
396
|
-
process.removeListener('warning', onWarning)
|
|
397
|
-
FSTWRN001.emitted = false
|
|
398
|
-
})
|
|
379
|
+
const fastify = Fastify()
|
|
399
380
|
|
|
400
381
|
fastify.post('/:id', {
|
|
401
382
|
handler: echoParams,
|
|
@@ -410,24 +391,18 @@ test('Should emit warning if the schema query is undefined', (t, testDone) => {
|
|
|
410
391
|
}, (error, res) => {
|
|
411
392
|
t.assert.ifError(error)
|
|
412
393
|
t.assert.strictEqual(res.statusCode, 200)
|
|
394
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['querystring', 'POST', '/:id'], result: true }])
|
|
395
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
413
396
|
testDone()
|
|
414
397
|
})
|
|
415
398
|
})
|
|
416
399
|
|
|
417
400
|
test('Should emit warning if the schema params is undefined', (t, testDone) => {
|
|
418
401
|
t.plan(4)
|
|
419
|
-
const
|
|
402
|
+
const spyData = spyWarning(FSTWRN001)
|
|
403
|
+
t.after(spyData.restore)
|
|
420
404
|
|
|
421
|
-
|
|
422
|
-
function onWarning (warning) {
|
|
423
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
424
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
t.after(() => {
|
|
428
|
-
process.removeListener('warning', onWarning)
|
|
429
|
-
FSTWRN001.emitted = false
|
|
430
|
-
})
|
|
405
|
+
const fastify = Fastify()
|
|
431
406
|
|
|
432
407
|
fastify.post('/:id', {
|
|
433
408
|
handler: echoParams,
|
|
@@ -442,31 +417,18 @@ test('Should emit warning if the schema params is undefined', (t, testDone) => {
|
|
|
442
417
|
}, (error, res) => {
|
|
443
418
|
t.assert.ifError(error)
|
|
444
419
|
t.assert.strictEqual(res.statusCode, 200)
|
|
420
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['params', 'POST', '/:id'], result: true }])
|
|
421
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
445
422
|
testDone()
|
|
446
423
|
})
|
|
447
424
|
})
|
|
448
425
|
|
|
449
426
|
test('Should emit a warning for every route with undefined schema', (t, testDone) => {
|
|
450
|
-
t.plan(
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
let runs = 0
|
|
454
|
-
const expectedWarningEmitted = [0, 1, 2, 3]
|
|
455
|
-
// It emits 4 warnings:
|
|
456
|
-
// - 2 - GET and HEAD for /undefinedParams/:id
|
|
457
|
-
// - 2 - GET and HEAD for /undefinedBody/:id
|
|
458
|
-
// => 3 x 4 assertions = 12 assertions
|
|
459
|
-
function onWarning (warning) {
|
|
460
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
461
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
462
|
-
t.assert.strictEqual(runs++, expectedWarningEmitted.shift())
|
|
463
|
-
}
|
|
427
|
+
t.plan(9)
|
|
428
|
+
const spyData = spyWarning(FSTWRN001)
|
|
429
|
+
t.after(spyData.restore)
|
|
464
430
|
|
|
465
|
-
|
|
466
|
-
t.after(() => {
|
|
467
|
-
process.removeListener('warning', onWarning)
|
|
468
|
-
FSTWRN001.emitted = false
|
|
469
|
-
})
|
|
431
|
+
const fastify = Fastify()
|
|
470
432
|
|
|
471
433
|
fastify.get('/undefinedParams/:id', {
|
|
472
434
|
handler: echoParams,
|
|
@@ -496,6 +458,15 @@ test('Should emit a warning for every route with undefined schema', (t, testDone
|
|
|
496
458
|
}, (error, res) => {
|
|
497
459
|
t.assert.ifError(error)
|
|
498
460
|
t.assert.strictEqual(res.statusCode, 200)
|
|
461
|
+
// fastify.inject run in series
|
|
462
|
+
// last callback recieve all warnings at once
|
|
463
|
+
// GET /undefinedParams/123
|
|
464
|
+
t.assert.deepStrictEqual(spyData.calls[0], { arguments: ['params', 'GET', '/undefinedParams/:id'], result: true })
|
|
465
|
+
t.assert.deepStrictEqual(spyData.calls[1], { arguments: ['params', 'HEAD', '/undefinedParams/:id'], result: true })
|
|
466
|
+
// GET /undefinedBody/123
|
|
467
|
+
t.assert.deepStrictEqual(spyData.calls[2], { arguments: ['body', 'GET', '/undefinedBody/:id'], result: true })
|
|
468
|
+
t.assert.deepStrictEqual(spyData.calls[3], { arguments: ['body', 'HEAD', '/undefinedBody/:id'], result: true })
|
|
469
|
+
t.assert.strictEqual(spyData.callCount(), 4)
|
|
499
470
|
testDone()
|
|
500
471
|
})
|
|
501
472
|
})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
const ContentType = require('../lib/content-type')
|
|
6
|
-
const { waitForCb } = require('./
|
|
6
|
+
const { waitForCb } = require('./helper')
|
|
7
7
|
|
|
8
8
|
const echoBody = (req, reply) => { reply.send(req.body) }
|
|
9
9
|
|
|
@@ -9,7 +9,7 @@ const Fastify = require('..')
|
|
|
9
9
|
const ajvMergePatch = require('ajv-merge-patch')
|
|
10
10
|
const ajvErrors = require('ajv-errors')
|
|
11
11
|
const proxyquire = require('proxyquire')
|
|
12
|
-
const { waitForCb } = require('./
|
|
12
|
+
const { waitForCb } = require('./helper')
|
|
13
13
|
|
|
14
14
|
test('Ajv plugins array parameter', (t, testDone) => {
|
|
15
15
|
t.plan(3)
|
|
@@ -5,7 +5,7 @@ const Fastify = require('..')
|
|
|
5
5
|
|
|
6
6
|
const AJV = require('ajv')
|
|
7
7
|
const Schema = require('fluent-json-schema')
|
|
8
|
-
const { waitForCb } = require('./
|
|
8
|
+
const { waitForCb } = require('./helper')
|
|
9
9
|
const { kRequestContentType } = require('../lib/symbols')
|
|
10
10
|
|
|
11
11
|
const customSchemaCompilers = {
|
package/test/stream.2.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const resolve = require('node:path').resolve
|
|
|
7
7
|
const zlib = require('node:zlib')
|
|
8
8
|
const pipeline = require('node:stream').pipeline
|
|
9
9
|
const Fastify = require('..')
|
|
10
|
-
const { waitForCb } = require('./
|
|
10
|
+
const { waitForCb } = require('./helper')
|
|
11
11
|
|
|
12
12
|
test('onSend hook stream', t => {
|
|
13
13
|
t.plan(4)
|
package/test/toolkit.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
exports.waitForCb = function (options) {
|
|
4
|
-
let count = null
|
|
5
|
-
let done = false
|
|
6
|
-
let iResolve
|
|
7
|
-
let iReject
|
|
8
|
-
|
|
9
|
-
function stepIn () {
|
|
10
|
-
if (done) {
|
|
11
|
-
iReject(new Error('Unexpected done call'))
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (--count) {
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
done = true
|
|
20
|
-
iResolve()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const patience = new Promise((resolve, reject) => {
|
|
24
|
-
iResolve = resolve
|
|
25
|
-
iReject = reject
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
count = options.steps || 1
|
|
29
|
-
done = false
|
|
30
|
-
|
|
31
|
-
return { stepIn, patience }
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) {
|
|
35
|
-
if (typeof expected !== 'object' || expected === null) {
|
|
36
|
-
return actual === expected
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (typeof actual !== 'object' || actual === null) {
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (Array.isArray(expected)) {
|
|
44
|
-
if (!Array.isArray(actual)) return false
|
|
45
|
-
if (expected.length > actual.length) return false
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < expected.length; i++) {
|
|
48
|
-
if (!partialDeepStrictEqual(actual[i], expected[i])) {
|
|
49
|
-
return false
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return true
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
for (const key of Object.keys(expected)) {
|
|
56
|
-
if (!(key in actual)) return false
|
|
57
|
-
if (!partialDeepStrictEqual(actual[key], expected[key])) {
|
|
58
|
-
return false
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return true
|
|
63
|
-
}
|