fastify 4.0.0-rc.3 → 4.0.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 (48) hide show
  1. package/README.md +2 -1
  2. package/build/build-validation.js +14 -2
  3. package/docs/Guides/Ecosystem.md +23 -11
  4. package/docs/Guides/Index.md +1 -1
  5. package/docs/Guides/Migration-Guide-V3.md +1 -1
  6. package/docs/Guides/Plugins-Guide.md +3 -3
  7. package/docs/Guides/Prototype-Poisoning.md +1 -1
  8. package/docs/Guides/Recommendations.md +2 -2
  9. package/docs/Guides/Serverless.md +5 -5
  10. package/docs/Guides/Testing.md +3 -1
  11. package/docs/Guides/Write-Plugin.md +3 -3
  12. package/docs/Migration-Guide-V4.md +1 -1
  13. package/docs/Reference/Logging.md +10 -5
  14. package/docs/Reference/Middleware.md +3 -3
  15. package/docs/Reference/Routes.md +2 -2
  16. package/docs/Reference/Server.md +59 -10
  17. package/docs/Reference/TypeScript.md +2 -2
  18. package/docs/Reference/Validation-and-Serialization.md +11 -1
  19. package/fastify.d.ts +2 -1
  20. package/fastify.js +38 -14
  21. package/lib/configValidator.js +456 -332
  22. package/lib/errors.js +4 -0
  23. package/lib/request.js +2 -2
  24. package/lib/route.js +5 -2
  25. package/lib/schemas.js +3 -0
  26. package/lib/validation.js +8 -2
  27. package/package.json +34 -34
  28. package/test/close-pipelining.test.js +4 -2
  29. package/test/close.test.js +93 -3
  30. package/test/custom-http-server.test.js +22 -0
  31. package/test/decorator.test.js +146 -0
  32. package/test/internals/initialConfig.test.js +5 -3
  33. package/test/output-validation.test.js +6 -2
  34. package/test/plugin.test.js +177 -14
  35. package/test/route-prefix.test.js +250 -0
  36. package/test/router-options.test.js +61 -0
  37. package/test/schema-feature.test.js +24 -0
  38. package/test/schema-serialization.test.js +57 -0
  39. package/test/types/fastify.test-d.ts +1 -0
  40. package/test/types/instance.test-d.ts +1 -0
  41. package/test/types/logger.test-d.ts +13 -1
  42. package/test/types/route.test-d.ts +14 -0
  43. package/test/types/type-provider.test-d.ts +6 -0
  44. package/types/instance.d.ts +1 -0
  45. package/types/logger.d.ts +3 -1
  46. package/types/route.d.ts +1 -1
  47. package/types/schema.d.ts +1 -1
  48. package/types/type-provider.d.ts +3 -4
package/lib/errors.js CHANGED
@@ -200,6 +200,10 @@ const codes = {
200
200
  'FST_ERR_INIT_OPTS_INVALID',
201
201
  "Invalid initialization options: '%s'"
202
202
  ),
203
+ FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE: createError(
204
+ 'FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE',
205
+ "Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
206
+ ),
203
207
 
204
208
  /**
205
209
  * router
package/lib/request.js CHANGED
@@ -65,8 +65,8 @@ function buildRegularRequest (R) {
65
65
  this[prop.key] = prop.value
66
66
  }
67
67
  }
68
- Object.setPrototypeOf(_Request.prototype, Request.prototype)
69
- Object.setPrototypeOf(_Request, Request)
68
+ Object.setPrototypeOf(_Request.prototype, R.prototype)
69
+ Object.setPrototypeOf(_Request, R)
70
70
  _Request.props = props
71
71
  _Request.parent = R
72
72
 
package/lib/route.js CHANGED
@@ -42,7 +42,6 @@ const {
42
42
  const { buildErrorHandler } = require('./error-handler')
43
43
 
44
44
  function buildRouting (options) {
45
- const { keepAliveConnections } = options
46
45
  const router = FindMyWay(options.config)
47
46
 
48
47
  let avvio
@@ -56,9 +55,11 @@ function buildRouting (options) {
56
55
  let genReqId
57
56
  let disableRequestLogging
58
57
  let ignoreTrailingSlash
58
+ let ignoreDuplicateSlashes
59
59
  let return503OnClosing
60
60
  let globalExposeHeadRoutes
61
61
  let validateHTTPVersion
62
+ let keepAliveConnections
62
63
 
63
64
  let closing = false
64
65
 
@@ -78,7 +79,9 @@ function buildRouting (options) {
78
79
  genReqId = options.genReqId
79
80
  disableRequestLogging = options.disableRequestLogging
80
81
  ignoreTrailingSlash = options.ignoreTrailingSlash
82
+ ignoreDuplicateSlashes = options.ignoreDuplicateSlashes
81
83
  return503OnClosing = Object.prototype.hasOwnProperty.call(options, 'return503OnClosing') ? options.return503OnClosing : true
84
+ keepAliveConnections = fastifyArgs.keepAliveConnections
82
85
  },
83
86
  routing: router.lookup.bind(router), // router func to find the right handler to call
84
87
  route, // configure a route in the fastify instance
@@ -182,7 +185,7 @@ function buildRouting (options) {
182
185
  default:
183
186
  addNewRoute.call(this, '')
184
187
  // If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
185
- if (ignoreTrailingSlash !== true) {
188
+ if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
186
189
  addNewRoute.call(this, path, true)
187
190
  }
188
191
  }
package/lib/schemas.js CHANGED
@@ -143,6 +143,9 @@ function getSchemaSerializer (context, statusCode) {
143
143
  if (responseSchemaDef[fallbackStatusCode]) {
144
144
  return responseSchemaDef[fallbackStatusCode]
145
145
  }
146
+ if (responseSchemaDef.default) {
147
+ return responseSchemaDef.default
148
+ }
146
149
  return false
147
150
  }
148
151
 
package/lib/validation.js CHANGED
@@ -7,17 +7,23 @@ const {
7
7
  kSchemaBody: bodySchema,
8
8
  kSchemaResponse: responseSchema
9
9
  } = require('./symbols')
10
+ const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$|^default$/
10
11
 
11
12
  function compileSchemasForSerialization (context, compile) {
12
13
  if (!context.schema || !context.schema.response) {
13
14
  return
14
15
  }
15
-
16
16
  const { method, url } = context.config || {}
17
17
  context[responseSchema] = Object.keys(context.schema.response)
18
18
  .reduce(function (acc, statusCode) {
19
+ const schema = context.schema.response[statusCode]
20
+ statusCode = statusCode.toLowerCase()
21
+ if (!scChecker.exec(statusCode)) {
22
+ throw new Error('response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }')
23
+ }
24
+
19
25
  acc[statusCode] = compile({
20
- schema: context.schema.response[statusCode],
26
+ schema,
21
27
  url,
22
28
  method,
23
29
  httpStatus: statusCode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.0.0-rc.3",
3
+ "version": "4.0.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -124,13 +124,13 @@
124
124
  },
125
125
  "homepage": "https://www.fastify.io/",
126
126
  "devDependencies": {
127
- "@fastify/pre-commit": "^2.0.1",
128
- "@sinclair/typebox": "^0.23.1",
129
- "@sinonjs/fake-timers": "^9.1.0",
130
- "@types/node": "^17.0.18",
131
- "@typescript-eslint/eslint-plugin": "^5.7.0",
132
- "@typescript-eslint/parser": "^5.7.0",
133
- "ajv": "^8.10.0",
127
+ "@fastify/pre-commit": "^2.0.2",
128
+ "@sinclair/typebox": "^0.23.5",
129
+ "@sinonjs/fake-timers": "^9.1.2",
130
+ "@types/node": "^17.0.38",
131
+ "@typescript-eslint/eslint-plugin": "^5.27.0",
132
+ "@typescript-eslint/parser": "^5.27.0",
133
+ "ajv": "^8.11.0",
134
134
  "ajv-errors": "^3.0.0",
135
135
  "ajv-formats": "^2.1.1",
136
136
  "ajv-i18n": "^4.2.0",
@@ -138,57 +138,57 @@
138
138
  "branch-comparer": "^1.1.0",
139
139
  "cors": "^2.8.5",
140
140
  "dns-prefetch-control": "^0.3.0",
141
- "eslint": "^8.0.1",
141
+ "eslint": "^8.16.0",
142
142
  "eslint-config-standard": "^17.0.0-1",
143
- "eslint-import-resolver-node": "^0.3.2",
144
- "eslint-plugin-import": "^2.25.4",
145
- "eslint-plugin-n": "^15.1.0",
143
+ "eslint-import-resolver-node": "^0.3.6",
144
+ "eslint-plugin-import": "^2.26.0",
145
+ "eslint-plugin-n": "^15.2.0",
146
146
  "eslint-plugin-promise": "^6.0.0",
147
147
  "fast-json-body": "^1.1.0",
148
- "fast-json-stringify": "^3.0.0",
149
- "fastify-plugin": "^3.0.0",
150
- "fluent-json-schema": "^3.0.1",
148
+ "fast-json-stringify": "^4.0.0",
149
+ "fastify-plugin": "^3.0.1",
150
+ "fluent-json-schema": "^3.1.0",
151
151
  "form-data": "^4.0.0",
152
152
  "frameguard": "^4.0.0",
153
153
  "h2url": "^0.2.0",
154
- "helmet": "^5.0.1",
154
+ "helmet": "^5.1.0",
155
155
  "hide-powered-by": "^1.1.0",
156
156
  "http-errors": "^2.0.0",
157
- "joi": "^17.5.0",
158
- "json-schema-to-ts": "^2.0.1",
157
+ "joi": "^17.6.0",
158
+ "json-schema-to-ts": "^2.5.3",
159
159
  "JSONStream": "^1.3.5",
160
160
  "license-checker": "^25.0.1",
161
161
  "proxyquire": "^2.1.3",
162
162
  "pump": "^3.0.0",
163
163
  "self-cert": "^2.0.0",
164
164
  "send": "^0.18.0",
165
- "serve-static": "^1.14.1",
166
- "simple-get": "^4.0.0",
165
+ "serve-static": "^1.15.0",
166
+ "simple-get": "^4.0.1",
167
167
  "snazzy": "^9.0.0",
168
168
  "split2": "^4.1.0",
169
169
  "standard": "^17.0.0-2",
170
- "tap": "^16.0.0",
170
+ "tap": "^16.2.0",
171
171
  "tsd": "^0.20.0",
172
- "typescript": "^4.5.4",
173
- "undici": "^4.11.3",
172
+ "typescript": "^4.7.2",
173
+ "undici": "^5.4.0",
174
174
  "x-xss-protection": "^2.0.0",
175
175
  "yup": "^0.32.11"
176
176
  },
177
177
  "dependencies": {
178
178
  "@fastify/ajv-compiler": "^3.1.0",
179
- "@fastify/error": "^2.0.0",
180
- "@fastify/fast-json-stringify-compiler": "^1.0.0",
179
+ "@fastify/error": "^3.0.0",
180
+ "@fastify/fast-json-stringify-compiler": "^3.0.0",
181
181
  "abstract-logging": "^2.0.1",
182
- "avvio": "^8.1.0",
183
- "find-my-way": "^6.0.0",
184
- "light-my-request": "^4.7.0",
185
- "pino": "^7.5.1",
186
- "process-warning": "^1.0.0",
182
+ "avvio": "^8.1.3",
183
+ "find-my-way": "^6.3.0",
184
+ "light-my-request": "^5.0.0",
185
+ "pino": "^8.0.0",
186
+ "process-warning": "^2.0.0",
187
187
  "proxy-addr": "^2.0.7",
188
- "rfdc": "^1.1.4",
189
- "secure-json-parse": "^2.0.0",
190
- "semver": "^7.3.2",
191
- "tiny-lru": "^8.0.1"
188
+ "rfdc": "^1.3.0",
189
+ "secure-json-parse": "^2.4.0",
190
+ "semver": "^7.3.7",
191
+ "tiny-lru": "^8.0.2"
192
192
  },
193
193
  "standard": {
194
194
  "ignore": [
@@ -7,7 +7,8 @@ const { Client } = require('undici')
7
7
 
8
8
  test('Should return 503 while closing - pipelining', t => {
9
9
  const fastify = Fastify({
10
- return503OnClosing: true
10
+ return503OnClosing: true,
11
+ forceCloseConnections: false
11
12
  })
12
13
 
13
14
  fastify.get('/', (req, reply) => {
@@ -40,7 +41,8 @@ test('Should return 503 while closing - pipelining', t => {
40
41
 
41
42
  test('Should not return 503 while closing - pipelining - return503OnClosing', t => {
42
43
  const fastify = Fastify({
43
- return503OnClosing: false
44
+ return503OnClosing: false,
45
+ forceCloseConnections: false
44
46
  })
45
47
 
46
48
  fastify.get('/', (req, reply) => {
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const net = require('net')
4
+ const http = require('http')
4
5
  const t = require('tap')
5
6
  const test = t.test
6
7
  const Fastify = require('..')
@@ -203,7 +204,8 @@ test('Should return error while closing (callback) - injection', t => {
203
204
 
204
205
  t.test('Current opened connection should continue to work after closing and return "connection: close" header - return503OnClosing: false', t => {
205
206
  const fastify = Fastify({
206
- return503OnClosing: false
207
+ return503OnClosing: false,
208
+ forceCloseConnections: false
207
209
  })
208
210
 
209
211
  fastify.get('/', (req, reply) => {
@@ -240,7 +242,7 @@ t.test('Current opened connection should continue to work after closing and retu
240
242
 
241
243
  t.test('Current opened connection should not accept new incoming connections', t => {
242
244
  t.plan(3)
243
- const fastify = Fastify()
245
+ const fastify = Fastify({ forceCloseConnections: false })
244
246
  fastify.get('/', (req, reply) => {
245
247
  fastify.close()
246
248
  setTimeout(() => {
@@ -292,7 +294,10 @@ test('Cannot be reopened the closed server has listen callback', async t => {
292
294
  })
293
295
  })
294
296
 
295
- test('shutsdown while keep-alive connections are active (non-async)', t => {
297
+ const server = http.createServer()
298
+ const noSupport = typeof server.closeAllConnections !== 'function'
299
+
300
+ test('shutsdown while keep-alive connections are active (non-async, native)', { skip: noSupport }, t => {
296
301
  t.plan(5)
297
302
 
298
303
  const timeoutTime = 2 * 60 * 1000
@@ -329,3 +334,88 @@ test('shutsdown while keep-alive connections are active (non-async)', t => {
329
334
  })
330
335
  })
331
336
  })
337
+
338
+ test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, t => {
339
+ t.plan(5)
340
+
341
+ const timeoutTime = 2 * 60 * 1000
342
+ const fastify = Fastify({ forceCloseConnections: 'idle' })
343
+
344
+ fastify.server.setTimeout(timeoutTime)
345
+ fastify.server.keepAliveTimeout = timeoutTime
346
+
347
+ fastify.get('/', (req, reply) => {
348
+ reply.send({ hello: 'world' })
349
+ })
350
+
351
+ fastify.listen({ port: 0 }, (err, address) => {
352
+ t.error(err)
353
+
354
+ const client = new Client(
355
+ 'http://localhost:' + fastify.server.address().port,
356
+ { keepAliveTimeout: 1 * 60 * 1000 }
357
+ )
358
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
359
+ t.error(err)
360
+ t.equal(client.closed, false)
361
+
362
+ fastify.close((err) => {
363
+ t.error(err)
364
+
365
+ // Due to the nature of the way we reap these keep-alive connections,
366
+ // there hasn't been enough time before the server fully closed in order
367
+ // for the client to have seen the socket get destroyed. The mere fact
368
+ // that we have reached this callback is enough indication that the
369
+ // feature being tested works as designed.
370
+ t.equal(client.closed, false)
371
+ })
372
+ })
373
+ })
374
+ })
375
+
376
+ test('shutsdown while keep-alive connections are active (non-async, custom)', t => {
377
+ t.plan(5)
378
+
379
+ const timeoutTime = 2 * 60 * 1000
380
+ const fastify = Fastify({
381
+ forceCloseConnections: true,
382
+ serverFactory (handler) {
383
+ const server = http.createServer(handler)
384
+
385
+ server.closeAllConnections = null
386
+
387
+ return server
388
+ }
389
+ })
390
+
391
+ fastify.server.setTimeout(timeoutTime)
392
+ fastify.server.keepAliveTimeout = timeoutTime
393
+
394
+ fastify.get('/', (req, reply) => {
395
+ reply.send({ hello: 'world' })
396
+ })
397
+
398
+ fastify.listen({ port: 0 }, (err, address) => {
399
+ t.error(err)
400
+
401
+ const client = new Client(
402
+ 'http://localhost:' + fastify.server.address().port,
403
+ { keepAliveTimeout: 1 * 60 * 1000 }
404
+ )
405
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
406
+ t.error(err)
407
+ t.equal(client.closed, false)
408
+
409
+ fastify.close((err) => {
410
+ t.error(err)
411
+
412
+ // Due to the nature of the way we reap these keep-alive connections,
413
+ // there hasn't been enough time before the server fully closed in order
414
+ // for the client to have seen the socket get destroyed. The mere fact
415
+ // that we have reached this callback is enough indication that the
416
+ // feature being tested works as designed.
417
+ t.equal(client.closed, false)
418
+ })
419
+ })
420
+ })
421
+ })
@@ -4,6 +4,7 @@ const t = require('tap')
4
4
  const test = t.test
5
5
  const Fastify = require('..')
6
6
  const http = require('http')
7
+ const { FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE } = require('../lib/errors')
7
8
  const sget = require('simple-get').concat
8
9
  const dns = require('dns').promises
9
10
 
@@ -49,3 +50,24 @@ test('Should support a custom http server', async t => {
49
50
  })
50
51
  })
51
52
  })
53
+
54
+ test('Should not allow forceCloseConnection=idle if the server does not support closeIdleConnections', t => {
55
+ t.plan(1)
56
+
57
+ t.throws(
58
+ () => {
59
+ Fastify({
60
+ forceCloseConnections: 'idle',
61
+ serverFactory (handler, opts) {
62
+ return {
63
+ on () {
64
+
65
+ }
66
+ }
67
+ }
68
+ })
69
+ },
70
+ FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,
71
+ "Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
72
+ )
73
+ })
@@ -1060,3 +1060,149 @@ test('decorateRequest with dependencies (functions)', (t) => {
1060
1060
  t.ok(app.hasRequestDecorator('decorator2'))
1061
1061
  }
1062
1062
  })
1063
+
1064
+ test('chain of decorators on Request', async (t) => {
1065
+ const fastify = Fastify()
1066
+ fastify.register(fp(async function (fastify) {
1067
+ fastify.decorateRequest('foo', 'toto')
1068
+ fastify.decorateRequest('bar', () => 'tata')
1069
+ }, {
1070
+ name: 'first'
1071
+ }))
1072
+
1073
+ fastify.get('/foo', async function (request, reply) {
1074
+ return request.foo
1075
+ })
1076
+ fastify.get('/bar', function (request, reply) {
1077
+ return request.bar()
1078
+ })
1079
+ fastify.register(async function second (fastify) {
1080
+ fastify.get('/foo', async function (request, reply) {
1081
+ return request.foo
1082
+ })
1083
+ fastify.get('/bar', async function (request, reply) {
1084
+ return request.bar()
1085
+ })
1086
+ fastify.register(async function fourth (fastify) {
1087
+ fastify.get('/plugin3/foo', async function (request, reply) {
1088
+ return request.foo
1089
+ })
1090
+ fastify.get('/plugin3/bar', function (request, reply) {
1091
+ return request.bar()
1092
+ })
1093
+ })
1094
+ fastify.register(fp(async function (fastify) {
1095
+ fastify.decorateRequest('fooB', 'toto')
1096
+ fastify.decorateRequest('barB', () => 'tata')
1097
+ }, {
1098
+ name: 'third'
1099
+ }))
1100
+ },
1101
+ { prefix: '/plugin2', name: 'plugin2' }
1102
+ )
1103
+
1104
+ await fastify.ready()
1105
+
1106
+ {
1107
+ const response = await fastify.inject('/foo')
1108
+ t.equal(response.body, 'toto')
1109
+ }
1110
+
1111
+ {
1112
+ const response = await fastify.inject('/bar')
1113
+ t.equal(response.body, 'tata')
1114
+ }
1115
+
1116
+ {
1117
+ const response = await fastify.inject('/plugin2/foo')
1118
+ t.equal(response.body, 'toto')
1119
+ }
1120
+
1121
+ {
1122
+ const response = await fastify.inject('/plugin2/bar')
1123
+ t.equal(response.body, 'tata')
1124
+ }
1125
+
1126
+ {
1127
+ const response = await fastify.inject('/plugin2/plugin3/foo')
1128
+ t.equal(response.body, 'toto')
1129
+ }
1130
+
1131
+ {
1132
+ const response = await fastify.inject('/plugin2/plugin3/bar')
1133
+ t.equal(response.body, 'tata')
1134
+ }
1135
+ })
1136
+
1137
+ test('chain of decorators on Reply', async (t) => {
1138
+ const fastify = Fastify()
1139
+ fastify.register(fp(async function (fastify) {
1140
+ fastify.decorateReply('foo', 'toto')
1141
+ fastify.decorateReply('bar', () => 'tata')
1142
+ }, {
1143
+ name: 'first'
1144
+ }))
1145
+
1146
+ fastify.get('/foo', async function (request, reply) {
1147
+ return reply.foo
1148
+ })
1149
+ fastify.get('/bar', function (request, reply) {
1150
+ return reply.bar()
1151
+ })
1152
+ fastify.register(async function second (fastify) {
1153
+ fastify.get('/foo', async function (request, reply) {
1154
+ return reply.foo
1155
+ })
1156
+ fastify.get('/bar', async function (request, reply) {
1157
+ return reply.bar()
1158
+ })
1159
+ fastify.register(async function fourth (fastify) {
1160
+ fastify.get('/plugin3/foo', async function (request, reply) {
1161
+ return reply.foo
1162
+ })
1163
+ fastify.get('/plugin3/bar', function (request, reply) {
1164
+ return reply.bar()
1165
+ })
1166
+ })
1167
+ fastify.register(fp(async function (fastify) {
1168
+ fastify.decorateReply('fooB', 'toto')
1169
+ fastify.decorateReply('barB', () => 'tata')
1170
+ }, {
1171
+ name: 'third'
1172
+ }))
1173
+ },
1174
+ { prefix: '/plugin2', name: 'plugin2' }
1175
+ )
1176
+
1177
+ await fastify.ready()
1178
+
1179
+ {
1180
+ const response = await fastify.inject('/foo')
1181
+ t.equal(response.body, 'toto')
1182
+ }
1183
+
1184
+ {
1185
+ const response = await fastify.inject('/bar')
1186
+ t.equal(response.body, 'tata')
1187
+ }
1188
+
1189
+ {
1190
+ const response = await fastify.inject('/plugin2/foo')
1191
+ t.equal(response.body, 'toto')
1192
+ }
1193
+
1194
+ {
1195
+ const response = await fastify.inject('/plugin2/bar')
1196
+ t.equal(response.body, 'tata')
1197
+ }
1198
+
1199
+ {
1200
+ const response = await fastify.inject('/plugin2/plugin3/foo')
1201
+ t.equal(response.body, 'toto')
1202
+ }
1203
+
1204
+ {
1205
+ const response = await fastify.inject('/plugin2/plugin3/bar')
1206
+ t.equal(response.body, 'tata')
1207
+ }
1208
+ })
@@ -32,7 +32,6 @@ test('without options passed to Fastify, initialConfig should expose default val
32
32
  const fastifyDefaultOptions = {
33
33
  connectionTimeout: 0,
34
34
  keepAliveTimeout: 72000,
35
- forceCloseConnections: false,
36
35
  maxRequestsPerSocket: 0,
37
36
  requestTimeout: 0,
38
37
  bodyLimit: 1024 * 1024,
@@ -41,6 +40,7 @@ test('without options passed to Fastify, initialConfig should expose default val
41
40
  disableRequestLogging: false,
42
41
  jsonShorthand: true,
43
42
  ignoreTrailingSlash: false,
43
+ ignoreDuplicateSlashes: false,
44
44
  maxParamLength: 100,
45
45
  onProtoPoisoning: 'error',
46
46
  onConstructorPoisoning: 'error',
@@ -55,7 +55,7 @@ test('without options passed to Fastify, initialConfig should expose default val
55
55
  })
56
56
 
57
57
  test('Fastify.initialConfig should expose all options', t => {
58
- t.plan(19)
58
+ t.plan(20)
59
59
 
60
60
  const serverFactory = (handler, opts) => {
61
61
  const server = http.createServer((req, res) => {
@@ -88,6 +88,7 @@ test('Fastify.initialConfig should expose all options', t => {
88
88
  cert: global.context.cert
89
89
  },
90
90
  ignoreTrailingSlash: true,
91
+ ignoreDuplicateSlashes: true,
91
92
  maxParamLength: 200,
92
93
  connectionTimeout: 0,
93
94
  keepAliveTimeout: 72000,
@@ -115,6 +116,7 @@ test('Fastify.initialConfig should expose all options', t => {
115
116
  t.equal(fastify.initialConfig.http2, true)
116
117
  t.equal(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
117
118
  t.equal(fastify.initialConfig.ignoreTrailingSlash, true)
119
+ t.equal(fastify.initialConfig.ignoreDuplicateSlashes, true)
118
120
  t.equal(fastify.initialConfig.maxParamLength, 200)
119
121
  t.equal(fastify.initialConfig.connectionTimeout, 0)
120
122
  t.equal(fastify.initialConfig.keepAliveTimeout, 72000)
@@ -262,7 +264,6 @@ test('Should not have issues when passing stream options to Pino.js', t => {
262
264
  t.same(fastify.initialConfig, {
263
265
  connectionTimeout: 0,
264
266
  keepAliveTimeout: 72000,
265
- forceCloseConnections: false,
266
267
  maxRequestsPerSocket: 0,
267
268
  requestTimeout: 0,
268
269
  bodyLimit: 1024 * 1024,
@@ -271,6 +272,7 @@ test('Should not have issues when passing stream options to Pino.js', t => {
271
272
  disableRequestLogging: false,
272
273
  jsonShorthand: true,
273
274
  ignoreTrailingSlash: true,
275
+ ignoreDuplicateSlashes: false,
274
276
  maxParamLength: 100,
275
277
  onProtoPoisoning: 'error',
276
278
  onConstructorPoisoning: 'error',
@@ -127,9 +127,13 @@ fastify.listen({ port: 0 }, err => {
127
127
  url: 'http://localhost:' + fastify.server.address().port + '/wrong-object-for-schema'
128
128
  }, (err, response, body) => {
129
129
  t.error(err)
130
- t.equal(response.statusCode, 201)
130
+ t.equal(response.statusCode, 500)
131
131
  t.equal(response.headers['content-length'], '' + body.length)
132
- t.same(JSON.parse(body), {})
132
+ t.same(JSON.parse(body), {
133
+ statusCode: 500,
134
+ error: 'Internal Server Error',
135
+ message: 'The value "world" cannot be converted to a number.'
136
+ })
133
137
  })
134
138
  })
135
139