UNPKG

8.84 kBJavaScriptView Raw
1'use strict'
2
3const { test } = require('tap')
4const Fastify = require('../..')
5const fs = require('fs')
6const path = require('path')
7const http = require('http')
8const pino = require('pino')
9const split = require('split2')
10const deepClone = require('rfdc')({ circles: true, proto: false })
11const { deepFreezeObject } = require('../../lib/initialConfigValidation').utils
12
13test('Fastify.initialConfig is an object', t => {
14 t.plan(1)
15 t.type(Fastify().initialConfig, 'object')
16})
17
18test('without options passed to Fastify, initialConfig should expose default values', t => {
19 t.plan(1)
20
21 const fastifyDefaultOptions = {
22 bodyLimit: 1024 * 1024,
23 caseSensitive: true,
24 disableRequestLogging: false,
25 ignoreTrailingSlash: false,
26 maxParamLength: 100,
27 onProtoPoisoning: 'error',
28 onConstructorPoisoning: 'ignore',
29 pluginTimeout: 10000,
30 requestIdHeader: 'request-id',
31 requestIdLogLabel: 'reqId',
32 http2SessionTimeout: 5000
33 }
34
35 t.deepEquals(Fastify().initialConfig, fastifyDefaultOptions)
36})
37
38test('Fastify.initialConfig should expose all options', t => {
39 t.plan(16)
40
41 const serverFactory = (handler, opts) => {
42 const server = http.createServer((req, res) => {
43 handler(req, res)
44 })
45
46 return server
47 }
48
49 const versioning = {
50 storage: function () {
51 let versions = {}
52 return {
53 get: (version) => { return versions[version] || null },
54 set: (version, store) => { versions[version] = store },
55 del: (version) => { delete versions[version] },
56 empty: () => { versions = {} }
57 }
58 },
59 deriveVersion: (req, ctx) => {
60 return req.headers.accept
61 }
62 }
63
64 const options = {
65 http2: true,
66 https: {
67 key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
68 cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
69 },
70 ignoreTrailingSlash: true,
71 maxParamLength: 200,
72 bodyLimit: 1049600,
73 onProtoPoisoning: 'remove',
74 serverFactory,
75 caseSensitive: true,
76 requestIdHeader: 'request-id-alt',
77 pluginTimeout: 20000,
78 querystringParser: str => str,
79 genReqId: function (req) {
80 let i = 0
81 return i++
82 },
83 logger: pino({ level: 'info' }),
84 versioning,
85 trustProxy: function myTrustFn (address, hop) {
86 return address === '1.2.3.4' || hop === 1
87 }
88 }
89
90 const fastify = Fastify(options)
91 t.strictEqual(fastify.initialConfig.http2, true)
92 t.strictEqual(fastify.initialConfig.https, true)
93 t.strictEqual(fastify.initialConfig.ignoreTrailingSlash, true)
94 t.strictEqual(fastify.initialConfig.maxParamLength, 200)
95 t.strictEqual(fastify.initialConfig.bodyLimit, 1049600)
96 t.strictEqual(fastify.initialConfig.onProtoPoisoning, 'remove')
97 t.strictEqual(fastify.initialConfig.caseSensitive, true)
98 t.strictEqual(fastify.initialConfig.requestIdHeader, 'request-id-alt')
99 t.strictEqual(fastify.initialConfig.pluginTimeout, 20000)
100
101 // obfuscated options:
102 t.strictEqual(fastify.initialConfig.serverFactory, undefined)
103 t.strictEqual(fastify.initialConfig.trustProxy, undefined)
104 t.strictEqual(fastify.initialConfig.genReqId, undefined)
105 t.strictEqual(fastify.initialConfig.querystringParser, undefined)
106 t.strictEqual(fastify.initialConfig.logger, undefined)
107 t.strictEqual(fastify.initialConfig.versioning, undefined)
108 t.strictEqual(fastify.initialConfig.trustProxy, undefined)
109})
110
111test('Should throw if you try to modify Fastify.initialConfig', t => {
112 t.plan(4)
113
114 const fastify = Fastify({ ignoreTrailingSlash: true })
115 try {
116 fastify.initialConfig.ignoreTrailingSlash = false
117 t.fail()
118 } catch (error) {
119 t.type(error, TypeError)
120 t.equal(error.message, "Cannot assign to read only property 'ignoreTrailingSlash' of object '#<Object>'")
121 t.ok(error.stack)
122 t.pass()
123 }
124})
125
126test('We must avoid shallow freezing and ensure that the whole object is freezed', t => {
127 t.plan(4)
128
129 const fastify = Fastify({
130 https: {
131 allowHTTP1: true,
132 key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
133 cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
134 }
135 })
136
137 try {
138 fastify.initialConfig.https.allowHTTP1 = false
139 t.fail()
140 } catch (error) {
141 t.type(error, TypeError)
142 t.equal(error.message, "Cannot assign to read only property 'allowHTTP1' of object '#<Object>'")
143 t.ok(error.stack)
144 t.pass()
145 }
146})
147
148test('Return an error if options do not match the validation schema', t => {
149 t.plan(6)
150
151 try {
152 Fastify({ ignoreTrailingSlash: 'string instead of boolean' })
153
154 t.fail()
155 } catch (error) {
156 t.type(error, Error)
157 t.equal(error.name, 'FastifyError [FST_ERR_INIT_OPTS_INVALID]')
158 t.equal(error.message, 'FST_ERR_INIT_OPTS_INVALID: Invalid initialization options: \'["should be boolean"]\'')
159 t.equal(error.code, 'FST_ERR_INIT_OPTS_INVALID')
160 t.ok(error.stack)
161 t.pass()
162 }
163})
164
165test('Original options must not be frozen', t => {
166 t.plan(4)
167
168 const originalOptions = {
169 https: {
170 allowHTTP1: true,
171 key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
172 cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
173 }
174 }
175
176 const fastify = Fastify(originalOptions)
177
178 t.strictEqual(Object.isFrozen(originalOptions), false)
179 t.strictEqual(Object.isFrozen(originalOptions.https), false)
180 t.strictEqual(Object.isFrozen(fastify.initialConfig), true)
181 t.strictEqual(Object.isFrozen(fastify.initialConfig.https), true)
182})
183
184test('Original options must not be altered (test deep cloning)', t => {
185 t.plan(3)
186
187 const originalOptions = {
188 https: {
189 allowHTTP1: true,
190 key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
191 cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
192 }
193 }
194
195 const originalOptionsClone = deepClone(originalOptions)
196
197 const fastify = Fastify(originalOptions)
198
199 // initialConfig has been triggered
200 t.strictEqual(Object.isFrozen(fastify.initialConfig), true)
201
202 // originalOptions must not have been altered
203 t.deepEqual(originalOptions.https.key, originalOptionsClone.https.key)
204 t.deepEqual(originalOptions.https.cert, originalOptionsClone.https.cert)
205})
206
207test('Should not have issues when passing stream options to Pino.js', t => {
208 t.plan(15)
209
210 const stream = split(JSON.parse)
211
212 const originalOptions = {
213 ignoreTrailingSlash: true,
214 logger: {
215 level: 'trace',
216 stream
217 }
218 }
219
220 let fastify
221
222 try {
223 fastify = Fastify(originalOptions)
224
225 t.type(fastify, 'object')
226 t.deepEqual(fastify.initialConfig, {
227 bodyLimit: 1024 * 1024,
228 caseSensitive: true,
229 disableRequestLogging: false,
230 ignoreTrailingSlash: true,
231 maxParamLength: 100,
232 onProtoPoisoning: 'error',
233 onConstructorPoisoning: 'ignore',
234 pluginTimeout: 10000,
235 requestIdHeader: 'request-id',
236 requestIdLogLabel: 'reqId',
237 http2SessionTimeout: 5000
238 })
239 } catch (error) {
240 t.fail()
241 }
242
243 fastify.get('/', function (req, reply) {
244 t.ok(req.log)
245 reply.send({ hello: 'world' })
246 })
247
248 stream.once('data', listenAtLogLine => {
249 t.ok(listenAtLogLine, 'listen at log message is ok')
250
251 stream.once('data', line => {
252 const id = line.reqId
253 t.ok(line.reqId, 'reqId is defined')
254 t.ok(line.req, 'req is defined')
255 t.equal(line.msg, 'incoming request', 'message is set')
256 t.equal(line.req.method, 'GET', 'method is get')
257
258 stream.once('data', line => {
259 t.equal(line.reqId, id)
260 t.ok(line.reqId, 'reqId is defined')
261 t.ok(line.res, 'res is defined')
262 t.equal(line.msg, 'request completed', 'message is set')
263 t.equal(line.res.statusCode, 200, 'statusCode is 200')
264 t.ok(line.responseTime, 'responseTime is defined')
265 })
266 })
267 })
268
269 fastify.listen(0, err => {
270 t.error(err)
271 fastify.server.unref()
272
273 http.get('http://localhost:' + fastify.server.address().port)
274 })
275})
276
277test('deepFreezeObject() should not throw on TypedArray', t => {
278 t.plan(5)
279
280 const object = {
281 buffer: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
282 dataView: new DataView(new ArrayBuffer(16)),
283 float: 1.1,
284 integer: 1,
285 object: {
286 nested: { string: 'string' }
287 },
288 stream: split(JSON.parse),
289 string: 'string'
290 }
291
292 try {
293 const frozenObject = deepFreezeObject(object)
294
295 // Buffers should not be frozen, as they are Uint8Array inherited instances
296 t.strictEqual(Object.isFrozen(frozenObject.buffer), false)
297
298 t.strictEqual(Object.isFrozen(frozenObject), true)
299 t.strictEqual(Object.isFrozen(frozenObject.object), true)
300 t.strictEqual(Object.isFrozen(frozenObject.object.nested), true)
301
302 t.pass()
303 } catch (error) {
304 t.fail()
305 }
306})