fastify 4.23.2 → 4.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/Guides/Ecosystem.md +6 -0
- package/docs/Reference/Hooks.md +1 -0
- package/docs/Reference/Plugins.md +1 -1
- package/docs/Reference/Reply.md +4 -3
- package/docs/Reference/Request.md +3 -2
- package/docs/Reference/Server.md +31 -3
- package/docs/Reference/Type-Providers.md +2 -2
- package/docs/Reference/TypeScript.md +21 -7
- package/fastify.d.ts +2 -2
- package/fastify.js +8 -1
- package/lib/contentTypeParser.js +1 -1
- package/lib/reply.js +20 -3
- package/lib/reqIdGenFactory.js +15 -9
- package/lib/request.js +1 -1
- package/lib/route.js +28 -7
- package/lib/schemas.js +3 -3
- package/lib/warnings.js +3 -1
- package/package.json +33 -33
- package/test/404s.test.js +31 -39
- package/test/async-await.test.js +1 -1
- package/test/async-dispose.test.js +21 -0
- package/test/build-certificate.js +90 -1
- package/test/close-pipelining.test.js +5 -5
- package/test/close.test.js +1 -5
- package/test/constrained-routes.test.js +127 -3
- package/test/custom-http-server.test.js +94 -91
- package/test/custom-parser.0.test.js +21 -47
- package/test/custom-parser.1.test.js +10 -732
- package/test/custom-parser.2.test.js +102 -0
- package/test/custom-parser.3.test.js +245 -0
- package/test/custom-parser.4.test.js +239 -0
- package/test/custom-parser.5.test.js +149 -0
- package/test/head.test.js +204 -0
- package/test/helper.js +30 -8
- package/test/hooks-async.test.js +163 -13
- package/test/hooks.on-listen.test.js +7 -6
- package/test/hooks.test.js +4 -15
- package/test/http2/closing.test.js +7 -15
- package/test/https/custom-https-server.test.js +43 -40
- package/test/input-validation.js +3 -3
- package/test/internals/reply.test.js +33 -4
- package/test/listen.1.test.js +101 -0
- package/test/listen.2.test.js +103 -0
- package/test/listen.3.test.js +87 -0
- package/test/listen.4.test.js +164 -0
- package/test/listen.deprecated.test.js +3 -9
- package/test/logger/instantiation.test.js +347 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +406 -0
- package/test/logger/options.test.js +500 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +184 -0
- package/test/plugin.1.test.js +249 -0
- package/test/plugin.2.test.js +328 -0
- package/test/plugin.3.test.js +311 -0
- package/test/plugin.4.test.js +416 -0
- package/test/reply-code.test.js +64 -0
- package/test/reply-trailers.test.js +1 -2
- package/test/route.1.test.js +309 -0
- package/test/route.2.test.js +99 -0
- package/test/route.3.test.js +205 -0
- package/test/route.4.test.js +131 -0
- package/test/route.5.test.js +230 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +370 -0
- package/test/route.8.test.js +142 -0
- package/test/stream.1.test.js +108 -0
- package/test/stream.2.test.js +119 -0
- package/test/stream.3.test.js +192 -0
- package/test/stream.4.test.js +223 -0
- package/test/stream.5.test.js +194 -0
- package/test/trust-proxy.test.js +2 -4
- package/test/types/reply.test-d.ts +3 -3
- package/test/types/request.test-d.ts +9 -9
- package/test/types/type-provider.test-d.ts +89 -0
- package/test/types/using.test-d.ts +14 -0
- package/test/upgrade.test.js +3 -3
- package/types/context.d.ts +9 -2
- package/types/instance.d.ts +4 -1
- package/types/plugin.d.ts +2 -1
- package/types/reply.d.ts +2 -2
- package/types/request.d.ts +3 -3
- package/types/route.d.ts +5 -5
- package/test/listen.test.js +0 -427
- package/test/plugin.test.js +0 -1275
- package/test/route.test.js +0 -1762
- package/test/serial/logger.0.test.js +0 -866
- package/test/serial/logger.1.test.js +0 -862
- package/test/stream.test.js +0 -816
- /package/test/{serial → logger}/tap-parallel-not-ok +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test, before } = require('tap')
|
|
4
|
+
const dns = require('node:dns').promises
|
|
5
|
+
const dnsCb = require('node:dns')
|
|
6
|
+
const sget = require('simple-get').concat
|
|
7
|
+
const Fastify = require('../fastify')
|
|
8
|
+
const helper = require('./helper')
|
|
9
|
+
|
|
10
|
+
let localhostForURL
|
|
11
|
+
|
|
12
|
+
function getUrl (fastify, lookup) {
|
|
13
|
+
const { port } = fastify.server.address()
|
|
14
|
+
if (lookup.family === 6) {
|
|
15
|
+
return `http://[${lookup.address}]:${port}/`
|
|
16
|
+
} else {
|
|
17
|
+
return `http://${lookup.address}:${port}/`
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
before(async function () {
|
|
22
|
+
[, localhostForURL] = await helper.getLoopbackHost()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('listen twice on the same port without callback rejects', t => {
|
|
26
|
+
t.plan(1)
|
|
27
|
+
const fastify = Fastify()
|
|
28
|
+
t.teardown(fastify.close.bind(fastify))
|
|
29
|
+
|
|
30
|
+
fastify.listen({ port: 0 })
|
|
31
|
+
.then(() => {
|
|
32
|
+
const s2 = Fastify()
|
|
33
|
+
t.teardown(s2.close.bind(s2))
|
|
34
|
+
s2.listen({ port: fastify.server.address().port })
|
|
35
|
+
.catch(err => {
|
|
36
|
+
t.ok(err)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
.catch(err => t.error(err))
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('listen twice on the same port without callback rejects with (address)', t => {
|
|
43
|
+
t.plan(2)
|
|
44
|
+
const fastify = Fastify()
|
|
45
|
+
t.teardown(fastify.close.bind(fastify))
|
|
46
|
+
fastify.listen({ port: 0 })
|
|
47
|
+
.then(address => {
|
|
48
|
+
const s2 = Fastify()
|
|
49
|
+
t.teardown(s2.close.bind(s2))
|
|
50
|
+
t.equal(address, `http://${localhostForURL}:${fastify.server.address().port}`)
|
|
51
|
+
s2.listen({ port: fastify.server.address().port })
|
|
52
|
+
.catch(err => {
|
|
53
|
+
t.ok(err)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
.catch(err => t.error(err))
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('listen on invalid port without callback rejects', t => {
|
|
60
|
+
const fastify = Fastify()
|
|
61
|
+
t.teardown(fastify.close.bind(fastify))
|
|
62
|
+
return fastify.listen({ port: -1 })
|
|
63
|
+
.catch(err => {
|
|
64
|
+
t.ok(err)
|
|
65
|
+
return true
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('listen logs the port as info', t => {
|
|
70
|
+
t.plan(1)
|
|
71
|
+
const fastify = Fastify()
|
|
72
|
+
t.teardown(fastify.close.bind(fastify))
|
|
73
|
+
|
|
74
|
+
const msgs = []
|
|
75
|
+
fastify.log.info = function (msg) {
|
|
76
|
+
msgs.push(msg)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fastify.listen({ port: 0 })
|
|
80
|
+
.then(() => {
|
|
81
|
+
t.ok(/http:\/\//.test(msgs[0]))
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('listen on localhost binds IPv4 and IPv6 - promise interface', async t => {
|
|
86
|
+
const localAddresses = await dns.lookup('localhost', { all: true })
|
|
87
|
+
t.plan(2 * localAddresses.length)
|
|
88
|
+
|
|
89
|
+
const app = Fastify()
|
|
90
|
+
app.get('/', async () => 'hello localhost')
|
|
91
|
+
t.teardown(app.close.bind(app))
|
|
92
|
+
await app.listen({ port: 0, host: 'localhost' })
|
|
93
|
+
|
|
94
|
+
for (const lookup of localAddresses) {
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
sget({
|
|
97
|
+
method: 'GET',
|
|
98
|
+
url: getUrl(app, lookup)
|
|
99
|
+
}, (err, response, body) => {
|
|
100
|
+
if (err) { return reject(err) }
|
|
101
|
+
t.equal(response.statusCode, 200)
|
|
102
|
+
t.same(body.toString(), 'hello localhost')
|
|
103
|
+
resolve()
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('listen on localhost binds to all interfaces (both IPv4 and IPv6 if present) - callback interface', t => {
|
|
110
|
+
dnsCb.lookup('localhost', { all: true }, (err, lookups) => {
|
|
111
|
+
t.plan(2 + (3 * lookups.length))
|
|
112
|
+
t.error(err)
|
|
113
|
+
|
|
114
|
+
const app = Fastify()
|
|
115
|
+
app.get('/', async () => 'hello localhost')
|
|
116
|
+
app.listen({ port: 0, host: 'localhost' }, (err) => {
|
|
117
|
+
t.error(err)
|
|
118
|
+
t.teardown(app.close.bind(app))
|
|
119
|
+
|
|
120
|
+
for (const lookup of lookups) {
|
|
121
|
+
sget({
|
|
122
|
+
method: 'GET',
|
|
123
|
+
url: getUrl(app, lookup)
|
|
124
|
+
}, (err, response, body) => {
|
|
125
|
+
t.error(err)
|
|
126
|
+
t.equal(response.statusCode, 200)
|
|
127
|
+
t.same(body.toString(), 'hello localhost')
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('addresses getter', async t => {
|
|
135
|
+
let localAddresses = await dns.lookup('localhost', { all: true })
|
|
136
|
+
|
|
137
|
+
t.plan(4)
|
|
138
|
+
const app = Fastify()
|
|
139
|
+
app.get('/', async () => 'hello localhost')
|
|
140
|
+
|
|
141
|
+
t.same(app.addresses(), [], 'before ready')
|
|
142
|
+
await app.ready()
|
|
143
|
+
|
|
144
|
+
t.same(app.addresses(), [], 'after ready')
|
|
145
|
+
await app.listen({ port: 0, host: 'localhost' })
|
|
146
|
+
|
|
147
|
+
// fix citgm
|
|
148
|
+
// dns lookup may have duplicated addresses (rhel8-s390x rhel8-ppc64le debian10-x64)
|
|
149
|
+
|
|
150
|
+
localAddresses = [...new Set([...localAddresses.map(a => JSON.stringify({
|
|
151
|
+
address: a.address,
|
|
152
|
+
family: typeof a.family === 'number' ? 'IPv' + a.family : a.family
|
|
153
|
+
}))])].sort()
|
|
154
|
+
|
|
155
|
+
const appAddresses = app.addresses().map(a => JSON.stringify({
|
|
156
|
+
address: a.address,
|
|
157
|
+
family: typeof a.family === 'number' ? 'IPv' + a.family : a.family
|
|
158
|
+
})).sort()
|
|
159
|
+
|
|
160
|
+
t.same(appAddresses, localAddresses, 'after listen')
|
|
161
|
+
|
|
162
|
+
await app.close()
|
|
163
|
+
t.same(app.addresses(), [], 'after close')
|
|
164
|
+
})
|
|
@@ -4,22 +4,16 @@
|
|
|
4
4
|
// removed when the deprecation is complete.
|
|
5
5
|
|
|
6
6
|
const { test, before } = require('tap')
|
|
7
|
-
const dns = require('node:dns').promises
|
|
8
7
|
const Fastify = require('..')
|
|
8
|
+
const helper = require('./helper')
|
|
9
9
|
|
|
10
10
|
let localhost
|
|
11
11
|
let localhostForURL
|
|
12
12
|
|
|
13
13
|
process.removeAllListeners('warning')
|
|
14
14
|
|
|
15
|
-
before(async
|
|
16
|
-
|
|
17
|
-
localhost = lookup.address
|
|
18
|
-
if (lookup.family === 6) {
|
|
19
|
-
localhostForURL = `[${lookup.address}]`
|
|
20
|
-
} else {
|
|
21
|
-
localhostForURL = localhost
|
|
22
|
-
}
|
|
15
|
+
before(async () => {
|
|
16
|
+
[localhost, localhostForURL] = await helper.getLoopbackHost()
|
|
23
17
|
})
|
|
24
18
|
|
|
25
19
|
test('listen accepts a port and a callback', t => {
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const stream = require('node:stream')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const fs = require('node:fs')
|
|
6
|
+
|
|
7
|
+
const t = require('tap')
|
|
8
|
+
const split = require('split2')
|
|
9
|
+
|
|
10
|
+
const { streamSym } = require('pino/lib/symbols')
|
|
11
|
+
|
|
12
|
+
const Fastify = require('../../fastify')
|
|
13
|
+
const helper = require('../helper')
|
|
14
|
+
const { FST_ERR_LOG_INVALID_LOGGER } = require('../../lib/errors')
|
|
15
|
+
const { once, on } = stream
|
|
16
|
+
const { createTempFile, request } = require('./logger-test-utils')
|
|
17
|
+
|
|
18
|
+
t.test('logger instantiation', (t) => {
|
|
19
|
+
t.setTimeout(60000)
|
|
20
|
+
|
|
21
|
+
let localhost
|
|
22
|
+
let localhostForURL
|
|
23
|
+
|
|
24
|
+
t.plan(11)
|
|
25
|
+
t.before(async function () {
|
|
26
|
+
[localhost, localhostForURL] = await helper.getLoopbackHost()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
t.test('can use external logger instance', async (t) => {
|
|
30
|
+
const lines = [/^Server listening at /, /^incoming request$/, /^log success$/, /^request completed$/]
|
|
31
|
+
t.plan(lines.length + 1)
|
|
32
|
+
|
|
33
|
+
const stream = split(JSON.parse)
|
|
34
|
+
|
|
35
|
+
const logger = require('pino')(stream)
|
|
36
|
+
|
|
37
|
+
const fastify = Fastify({ logger })
|
|
38
|
+
t.teardown(fastify.close.bind(fastify))
|
|
39
|
+
|
|
40
|
+
fastify.get('/foo', function (req, reply) {
|
|
41
|
+
t.ok(req.log)
|
|
42
|
+
req.log.info('log success')
|
|
43
|
+
reply.send({ hello: 'world' })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await fastify.listen({ port: 0, host: localhost })
|
|
47
|
+
|
|
48
|
+
await request(`http://${localhostForURL}:` + fastify.server.address().port + '/foo')
|
|
49
|
+
|
|
50
|
+
for await (const [line] of on(stream, 'data')) {
|
|
51
|
+
const regex = lines.shift()
|
|
52
|
+
t.ok(regex.test(line.msg), '"' + line.msg + '" dont match "' + regex + '"')
|
|
53
|
+
if (lines.length === 0) break
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
t.test('should create a default logger if provided one is invalid', (t) => {
|
|
58
|
+
t.plan(8)
|
|
59
|
+
|
|
60
|
+
const logger = new Date()
|
|
61
|
+
|
|
62
|
+
const fastify = Fastify({ logger })
|
|
63
|
+
t.teardown(fastify.close.bind(fastify))
|
|
64
|
+
|
|
65
|
+
t.equal(typeof fastify.log, 'object')
|
|
66
|
+
t.equal(typeof fastify.log.fatal, 'function')
|
|
67
|
+
t.equal(typeof fastify.log.error, 'function')
|
|
68
|
+
t.equal(typeof fastify.log.warn, 'function')
|
|
69
|
+
t.equal(typeof fastify.log.info, 'function')
|
|
70
|
+
t.equal(typeof fastify.log.debug, 'function')
|
|
71
|
+
t.equal(typeof fastify.log.trace, 'function')
|
|
72
|
+
t.equal(typeof fastify.log.child, 'function')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
t.test('expose the logger', async (t) => {
|
|
76
|
+
t.plan(2)
|
|
77
|
+
const stream = split(JSON.parse)
|
|
78
|
+
const fastify = Fastify({
|
|
79
|
+
logger: {
|
|
80
|
+
stream,
|
|
81
|
+
level: 'info'
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
t.teardown(fastify.close.bind(fastify))
|
|
85
|
+
|
|
86
|
+
await fastify.ready()
|
|
87
|
+
|
|
88
|
+
t.ok(fastify.log)
|
|
89
|
+
t.same(typeof fastify.log, 'object')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
t.test('Wrap IPv6 address in listening log message', async (t) => {
|
|
93
|
+
t.plan(1)
|
|
94
|
+
|
|
95
|
+
const interfaces = os.networkInterfaces()
|
|
96
|
+
const ipv6 = Object.keys(interfaces)
|
|
97
|
+
.filter(name => name.substr(0, 2) === 'lo')
|
|
98
|
+
.map(name => interfaces[name])
|
|
99
|
+
.reduce((list, set) => list.concat(set), [])
|
|
100
|
+
.filter(info => info.family === 'IPv6')
|
|
101
|
+
.map(info => info.address)
|
|
102
|
+
.shift()
|
|
103
|
+
|
|
104
|
+
if (ipv6 === undefined) {
|
|
105
|
+
t.pass('No IPv6 loopback interface')
|
|
106
|
+
} else {
|
|
107
|
+
const stream = split(JSON.parse)
|
|
108
|
+
const fastify = Fastify({
|
|
109
|
+
logger: {
|
|
110
|
+
stream,
|
|
111
|
+
level: 'info'
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
t.teardown(fastify.close.bind(fastify))
|
|
115
|
+
|
|
116
|
+
await fastify.ready()
|
|
117
|
+
await fastify.listen({ port: 0, host: ipv6 })
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
const [line] = await once(stream, 'data')
|
|
121
|
+
t.same(line.msg, `Server listening at http://[${ipv6}]:${fastify.server.address().port}`)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
t.test('Do not wrap IPv4 address', async (t) => {
|
|
127
|
+
t.plan(1)
|
|
128
|
+
const stream = split(JSON.parse)
|
|
129
|
+
const fastify = Fastify({
|
|
130
|
+
logger: {
|
|
131
|
+
stream,
|
|
132
|
+
level: 'info'
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
t.teardown(fastify.close.bind(fastify))
|
|
136
|
+
|
|
137
|
+
await fastify.ready()
|
|
138
|
+
await fastify.listen({ port: 0, host: '127.0.0.1' })
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
const [line] = await once(stream, 'data')
|
|
142
|
+
t.same(line.msg, `Server listening at http://127.0.0.1:${fastify.server.address().port}`)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
t.test('file option', async (t) => {
|
|
147
|
+
const lines = [
|
|
148
|
+
{ msg: /Server listening at/ },
|
|
149
|
+
{ reqId: /req-/, req: { method: 'GET', url: '/' }, msg: 'incoming request' },
|
|
150
|
+
{ reqId: /req-/, res: { statusCode: 200 }, msg: 'request completed' }
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
const { file, cleanup } = createTempFile(t)
|
|
154
|
+
if (process.env.CITGM) { fs.writeFileSync(file, '') }
|
|
155
|
+
|
|
156
|
+
const fastify = Fastify({
|
|
157
|
+
logger: { file }
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
t.teardown(async () => {
|
|
161
|
+
await helper.sleep(250)
|
|
162
|
+
// may fail on win
|
|
163
|
+
try {
|
|
164
|
+
// cleanup the file after sonic-boom closed
|
|
165
|
+
// otherwise we may face racing condition
|
|
166
|
+
fastify.log[streamSym].once('close', cleanup)
|
|
167
|
+
// we must flush the stream ourself
|
|
168
|
+
// otherwise buffer may whole sonic-boom
|
|
169
|
+
fastify.log[streamSym].flushSync()
|
|
170
|
+
// end after flushing to actually close file
|
|
171
|
+
fastify.log[streamSym].end()
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.warn(err)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
t.teardown(fastify.close.bind(fastify))
|
|
177
|
+
|
|
178
|
+
fastify.get('/', function (req, reply) {
|
|
179
|
+
t.ok(req.log)
|
|
180
|
+
reply.send({ hello: 'world' })
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
await fastify.ready()
|
|
184
|
+
await fastify.listen({ port: 0, host: localhost })
|
|
185
|
+
await request(`http://${localhostForURL}:` + fastify.server.address().port)
|
|
186
|
+
|
|
187
|
+
await helper.sleep(250)
|
|
188
|
+
|
|
189
|
+
const log = fs.readFileSync(file, 'utf8').split('\n')
|
|
190
|
+
// strip last line
|
|
191
|
+
log.pop()
|
|
192
|
+
|
|
193
|
+
let id
|
|
194
|
+
for (let line of log) {
|
|
195
|
+
line = JSON.parse(line)
|
|
196
|
+
if (id === undefined && line.reqId) id = line.reqId
|
|
197
|
+
if (id !== undefined && line.reqId) t.equal(line.reqId, id)
|
|
198
|
+
t.match(line, lines.shift())
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
t.test('should be able to use a custom logger', (t) => {
|
|
203
|
+
t.plan(7)
|
|
204
|
+
|
|
205
|
+
const logger = {
|
|
206
|
+
fatal: (msg) => { t.equal(msg, 'fatal') },
|
|
207
|
+
error: (msg) => { t.equal(msg, 'error') },
|
|
208
|
+
warn: (msg) => { t.equal(msg, 'warn') },
|
|
209
|
+
info: (msg) => { t.equal(msg, 'info') },
|
|
210
|
+
debug: (msg) => { t.equal(msg, 'debug') },
|
|
211
|
+
trace: (msg) => { t.equal(msg, 'trace') },
|
|
212
|
+
child: () => logger
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const fastify = Fastify({ logger })
|
|
216
|
+
t.teardown(fastify.close.bind(fastify))
|
|
217
|
+
|
|
218
|
+
fastify.log.fatal('fatal')
|
|
219
|
+
fastify.log.error('error')
|
|
220
|
+
fastify.log.warn('warn')
|
|
221
|
+
fastify.log.info('info')
|
|
222
|
+
fastify.log.debug('debug')
|
|
223
|
+
fastify.log.trace('trace')
|
|
224
|
+
const child = fastify.log.child()
|
|
225
|
+
t.equal(child, logger)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
t.test('should throw in case a partially matching logger is provided', async (t) => {
|
|
229
|
+
t.plan(1)
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const fastify = Fastify({ logger: console })
|
|
233
|
+
await fastify.ready()
|
|
234
|
+
} catch (err) {
|
|
235
|
+
t.equal(
|
|
236
|
+
err instanceof FST_ERR_LOG_INVALID_LOGGER,
|
|
237
|
+
true,
|
|
238
|
+
"Invalid logger object provided. The logger instance should have these functions(s): 'fatal,child'."
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
t.test('can use external logger instance with custom serializer', async (t) => {
|
|
244
|
+
const lines = [['level', 30], ['req', { url: '/foo' }], ['level', 30], ['res', { statusCode: 200 }]]
|
|
245
|
+
t.plan(lines.length + 1)
|
|
246
|
+
|
|
247
|
+
const stream = split(JSON.parse)
|
|
248
|
+
const logger = require('pino')({
|
|
249
|
+
level: 'info',
|
|
250
|
+
serializers: {
|
|
251
|
+
req: function (req) {
|
|
252
|
+
return {
|
|
253
|
+
url: req.url
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}, stream)
|
|
258
|
+
|
|
259
|
+
const fastify = Fastify({
|
|
260
|
+
logger
|
|
261
|
+
})
|
|
262
|
+
t.teardown(fastify.close.bind(fastify))
|
|
263
|
+
|
|
264
|
+
fastify.get('/foo', function (req, reply) {
|
|
265
|
+
t.ok(req.log)
|
|
266
|
+
req.log.info('log success')
|
|
267
|
+
reply.send({ hello: 'world' })
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
await fastify.ready()
|
|
271
|
+
await fastify.listen({ port: 0, host: localhost })
|
|
272
|
+
|
|
273
|
+
await request(`http://${localhostForURL}:` + fastify.server.address().port + '/foo')
|
|
274
|
+
|
|
275
|
+
for await (const [line] of on(stream, 'data')) {
|
|
276
|
+
const check = lines.shift()
|
|
277
|
+
const key = check[0]
|
|
278
|
+
const value = check[1]
|
|
279
|
+
t.same(line[key], value)
|
|
280
|
+
if (lines.length === 0) break
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
t.test('The logger should accept custom serializer', async (t) => {
|
|
285
|
+
const lines = [
|
|
286
|
+
{ msg: /^Server listening at / },
|
|
287
|
+
{ req: { url: '/custom' }, msg: 'incoming request' },
|
|
288
|
+
{ res: { statusCode: 500 }, msg: 'kaboom' },
|
|
289
|
+
{ res: { statusCode: 500 }, msg: 'request completed' }
|
|
290
|
+
]
|
|
291
|
+
t.plan(lines.length + 1)
|
|
292
|
+
|
|
293
|
+
const stream = split(JSON.parse)
|
|
294
|
+
const fastify = Fastify({
|
|
295
|
+
logger: {
|
|
296
|
+
stream,
|
|
297
|
+
level: 'info',
|
|
298
|
+
serializers: {
|
|
299
|
+
req: function (req) {
|
|
300
|
+
return {
|
|
301
|
+
url: req.url
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
t.teardown(fastify.close.bind(fastify))
|
|
308
|
+
|
|
309
|
+
fastify.get('/custom', function (req, reply) {
|
|
310
|
+
t.ok(req.log)
|
|
311
|
+
reply.send(new Error('kaboom'))
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
await fastify.ready()
|
|
315
|
+
await fastify.listen({ port: 0, host: localhost })
|
|
316
|
+
|
|
317
|
+
await request(`http://${localhostForURL}:` + fastify.server.address().port + '/custom')
|
|
318
|
+
|
|
319
|
+
for await (const [line] of on(stream, 'data')) {
|
|
320
|
+
t.match(line, lines.shift())
|
|
321
|
+
if (lines.length === 0) break
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
t.test('should throw in case the external logger provided does not have a child method', async (t) => {
|
|
326
|
+
t.plan(1)
|
|
327
|
+
const loggerInstance = {
|
|
328
|
+
info: console.info,
|
|
329
|
+
error: console.error,
|
|
330
|
+
debug: console.debug,
|
|
331
|
+
fatal: console.error,
|
|
332
|
+
warn: console.warn,
|
|
333
|
+
trace: console.trace
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const fastify = Fastify({ logger: loggerInstance })
|
|
338
|
+
await fastify.ready()
|
|
339
|
+
} catch (err) {
|
|
340
|
+
t.equal(
|
|
341
|
+
err instanceof FST_ERR_LOG_INVALID_LOGGER,
|
|
342
|
+
true,
|
|
343
|
+
"Invalid logger object provided. The logger instance should have these functions(s): 'child'."
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const http = require('node:http')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const fs = require('node:fs')
|
|
6
|
+
|
|
7
|
+
const path = require('node:path')
|
|
8
|
+
|
|
9
|
+
function createDeferredPromise () {
|
|
10
|
+
const promise = {}
|
|
11
|
+
promise.promise = new Promise(function (resolve) {
|
|
12
|
+
promise.resolve = resolve
|
|
13
|
+
})
|
|
14
|
+
return promise
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let count = 0
|
|
18
|
+
function createTempFile () {
|
|
19
|
+
const file = path.join(os.tmpdir(), `sonic-boom-${process.pid}-${count++}`)
|
|
20
|
+
function cleanup () {
|
|
21
|
+
try {
|
|
22
|
+
fs.unlinkSync(file)
|
|
23
|
+
} catch { }
|
|
24
|
+
}
|
|
25
|
+
return { file, cleanup }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function request (url, cleanup = () => { }) {
|
|
29
|
+
const promise = createDeferredPromise()
|
|
30
|
+
http.get(url, (res) => {
|
|
31
|
+
const chunks = []
|
|
32
|
+
// we consume the response
|
|
33
|
+
res.on('data', function (chunk) {
|
|
34
|
+
chunks.push(chunk)
|
|
35
|
+
})
|
|
36
|
+
res.once('end', function () {
|
|
37
|
+
cleanup(res, Buffer.concat(chunks).toString())
|
|
38
|
+
promise.resolve()
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
return promise.promise
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
request,
|
|
46
|
+
createTempFile
|
|
47
|
+
}
|