fastify 5.2.2 → 5.3.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 CHANGED
@@ -16,7 +16,6 @@ _Be the first!_
16
16
  - [Mercedes-Benz Group](https://github.com/mercedes-benz)
17
17
  - [Val Town, Inc.](https://opencollective.com/valtown)
18
18
  - [Handsontable - JavaScript Data Grid](https://handsontable.com/docs/react-data-grid/?utm_source=Fastify_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024)
19
- - [Jspreadsheet](https://jspreadsheet.com/)
20
19
  - [Lokalise - A Localization and Translation Software Tool](https://lokalise.com/?utm_source=Fastify_GH&utm_medium=sponsorship)
21
20
 
22
21
  ## Tier 2
@@ -460,6 +460,8 @@ middlewares into Fastify plugins
460
460
  Lightweight cache plugin
461
461
  - [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
462
462
  A simple plugin for Fastify to list all available routes.
463
+ - [`fastify-lm`](https://github.com/galiprandi/fastify-lm#readme)
464
+ Use OpenAI, Claude, Google, Deepseek, and others LMs with one Fastify plugin.
463
465
  - [`fastify-loader`](https://github.com/TheNoim/fastify-loader) Load routes from
464
466
  a directory and inject the Fastify instance in each file.
465
467
  - [`fastify-log-controller`](https://github.com/Eomm/fastify-log-controller/)
@@ -365,3 +365,202 @@ Will define the `foo` property on the Fastify instance:
365
365
  ```js
366
366
  console.log(fastify.foo) // 'a getter'
367
367
  ```
368
+
369
+ ### `getDecorator<T>` API
370
+
371
+ Fastify's `getDecorator<T>` API retrieves an existing decorator from the
372
+ Fastify instance, `Request`, or `Reply`. If the decorator is not defined, an
373
+ `FST_ERR_DEC_UNDECLARED` error is thrown.
374
+
375
+ #### Use cases
376
+
377
+ **Early Plugin Dependency Validation**
378
+
379
+ `getDecorator<T>` on Fastify instance verifies that required decorators are
380
+ available at registration time.
381
+
382
+ For example:
383
+
384
+ ```js
385
+ fastify.register(async function (fastify) {
386
+ const usersRepository = fastify.getDecorator('usersRepository')
387
+
388
+ fastify.get('/users', async function (request, reply) {
389
+ // We are sure `usersRepository` exists at runtime
390
+ return usersRepository.findAll()
391
+ })
392
+ })
393
+ ```
394
+
395
+ **Handling Missing Decorators**
396
+
397
+ Directly accessing a decorator may lead to unexpected behavior if it is not declared:
398
+
399
+ ```ts
400
+ const user = request.user;
401
+ if (user && user.isAdmin) {
402
+ // Execute admin tasks.
403
+ }
404
+ ```
405
+
406
+ If `request.user` doesn't exist, then `user` will be set to `undefined`.
407
+ This makes it unclear whether the user is unauthenticated or the decorator is missing.
408
+
409
+ Using `getDecorator` enforces runtime safety:
410
+
411
+ ```ts
412
+ // If the decorator is missing, an explicit `FST_ERR_DEC_UNDECLARED`
413
+ // error is thrown immediately.
414
+ const user = request.getDecorator('user');
415
+ if (user && user.isAdmin) {
416
+ // Execute admin tasks.
417
+ }
418
+ ```
419
+
420
+ **Alternative to Module Augmentation**
421
+
422
+ Decorators are typically typed via module augmentation:
423
+
424
+ ```ts
425
+ declare module 'fastify' {
426
+ interface FastifyInstance {
427
+ usersRepository: IUsersRepository
428
+ }
429
+ interface FastifyRequest {
430
+ session: ISession
431
+ }
432
+ interface FastifyReply {
433
+ sendSuccess: SendSuccessFn
434
+ }
435
+ }
436
+ ```
437
+
438
+ This approach modifies the Fastify instance globally, which may lead to
439
+ conflicts and inconsistent behavior in multi-server setups or with plugin
440
+ encapsulation.
441
+
442
+ Using `getDecorator<T>` allows to limit types scope:
443
+
444
+ ```ts
445
+ serverOne.register(async function (fastify) {
446
+ const usersRepository = fastify.getDecorator<PostgreUsersRepository>(
447
+ 'usersRepository'
448
+ )
449
+
450
+ fastify.decorateRequest('session', null)
451
+ fastify.addHook('onRequest', async (req, reply) => {
452
+ // Yes, the request object has a setDecorator method.
453
+ // More information will be provided soon.
454
+ req.setDecorator('session', { user: 'Jean' })
455
+ })
456
+
457
+ fastify.get('/me', (request, reply) => {
458
+ const session = request.getDecorator<ISession>('session')
459
+ reply.send(session)
460
+ })
461
+ })
462
+
463
+ serverTwo.register(async function (fastify) {
464
+ const usersRepository = fastify.getDecorator<SqlLiteUsersRepository>(
465
+ 'usersRepository'
466
+ )
467
+
468
+ fastify.decorateReply('sendSuccess', function (data) {
469
+ return this.send({ success: true })
470
+ })
471
+
472
+ fastify.get('/success', async (request, reply) => {
473
+ const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
474
+ await sendSuccess()
475
+ })
476
+ })
477
+ ```
478
+
479
+ #### Bound functions inference
480
+
481
+ To save time, it's common to infer function types instead of
482
+ writing them manually:
483
+
484
+ ```ts
485
+ function sendSuccess (this: FastifyReply) {
486
+ return this.send({ success: true })
487
+ }
488
+
489
+ export type SendSuccess = typeof sendSuccess
490
+ ```
491
+
492
+ However, `getDecorator` returns functions with the `this`
493
+ context already **bound**, meaning the `this` parameter disappears
494
+ from the function signature.
495
+
496
+ To correctly type it, you should use `OmitThisParameter` utility:
497
+
498
+ ```ts
499
+ function sendSuccess (this: FastifyReply) {
500
+ return this.send({ success: true })
501
+ }
502
+
503
+ type BoundSendSuccess = OmitThisParameter<typeof sendSuccess>
504
+
505
+ fastify.decorateReply('sendSuccess', sendSuccess)
506
+ fastify.get('/success', async (request, reply) => {
507
+ const sendSuccess = reply.getDecorator<BoundSendSuccess>('sendSuccess')
508
+ await sendSuccess()
509
+ })
510
+ ```
511
+
512
+ ### `Request.setDecorator<T>` Method
513
+
514
+ The `setDecorator<T>` method provides a safe and convenient way to
515
+ update the value of a `Request` decorator.
516
+ If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error
517
+ is thrown.
518
+
519
+ #### Use Cases
520
+
521
+ **Runtime Safety**
522
+
523
+ A typical way to set a `Request` decorator looks like this:
524
+
525
+ ```ts
526
+ fastify.decorateRequest('user', '')
527
+ fastify.addHook('preHandler', async (req, reply) => {
528
+ req.user = 'Bob Dylan'
529
+ })
530
+ ```
531
+
532
+ However, there is no guarantee that the decorator actually exists
533
+ unless you manually check beforehand.
534
+ Additionally, typos are common, e.g. `account`, `acount`, or `accout`.
535
+
536
+ By using `setDecorator`, you are always sure that the decorator exists:
537
+
538
+ ```ts
539
+ fastify.decorateRequest('user', '')
540
+ fastify.addHook('preHandler', async (req, reply) => {
541
+ // Throws FST_ERR_DEC_UNDECLARED if the decorator does not exist
542
+ req.setDecorator('user-with-typo', 'Bob Dylan')
543
+ })
544
+ ```
545
+
546
+ ---
547
+
548
+ **Type Safety**
549
+
550
+ If the `FastifyRequest` interface does not declare the decorator, you
551
+ would typically need to use type assertions:
552
+
553
+ ```ts
554
+ fastify.addHook('preHandler', async (req, reply) => {
555
+ (req as typeof req & { user: string }).user = 'Bob Dylan'
556
+ })
557
+ ```
558
+
559
+ The `setDecorator<T>` method eliminates the need for explicit type
560
+ assertions while allowing type safety:
561
+
562
+ ```ts
563
+ fastify.addHook('preHandler', async (req, reply) => {
564
+ req.setDecorator<string>('user', 'Bob Dylan')
565
+ })
566
+ ```
@@ -36,6 +36,7 @@
36
36
  - [FST_ERR_DEC_MISSING_DEPENDENCY](#fst_err_dec_missing_dependency)
37
37
  - [FST_ERR_DEC_AFTER_START](#fst_err_dec_after_start)
38
38
  - [FST_ERR_DEC_REFERENCE_TYPE](#fst_err_dec_reference_type)
39
+ - [FST_ERR_DEC_UNDECLARED](#fst_err_dec_undeclared)
39
40
  - [FST_ERR_HOOK_INVALID_TYPE](#fst_err_hook_invalid_type)
40
41
  - [FST_ERR_HOOK_INVALID_HANDLER](#fst_err_hook_invalid_handler)
41
42
  - [FST_ERR_HOOK_INVALID_ASYNC_HANDLER](#fst_err_hook_invalid_async_handler)
@@ -306,6 +307,7 @@ Below is a table with all the error codes used by Fastify.
306
307
  | <a id="fst_err_dec_missing_dependency">FST_ERR_DEC_MISSING_DEPENDENCY</a> | The decorator cannot be registered due to a missing dependency. | Register the missing dependency. | [#1168](https://github.com/fastify/fastify/pull/1168) |
307
308
  | <a id="fst_err_dec_after_start">FST_ERR_DEC_AFTER_START</a> | The decorator cannot be added after start. | Add the decorator before starting the server. | [#2128](https://github.com/fastify/fastify/pull/2128) |
308
309
  | <a id="fst_err_dec_reference_type">FST_ERR_DEC_REFERENCE_TYPE</a> | The decorator cannot be a reference type. | Define the decorator with a getter/setter interface or an empty decorator with a hook. | [#5462](https://github.com/fastify/fastify/pull/5462) |
310
+ | <a id="fst_err_dec_undeclared">FST_ERR_DEC_UNDECLARED</a> | An attempt was made to access a decorator that has not been declared. | Declare the decorator before using it. | [#](https://github.com/fastify/fastify/pull/)
309
311
  | <a id="fst_err_hook_invalid_type">FST_ERR_HOOK_INVALID_TYPE</a> | The hook name must be a string. | Use a string for the hook name. | [#1168](https://github.com/fastify/fastify/pull/1168) |
310
312
  | <a id="fst_err_hook_invalid_handler">FST_ERR_HOOK_INVALID_HANDLER</a> | The hook callback must be a function. | Use a function for the hook callback. | [#1168](https://github.com/fastify/fastify/pull/1168) |
311
313
  | <a id="fst_err_hook_invalid_async_handler">FST_ERR_HOOK_INVALID_ASYNC_HANDLER</a> | Async function has too many arguments. Async hooks should not use the `done` argument. | Remove the `done` argument from the async hook. | [#4367](https://github.com/fastify/fastify/pull/4367) |
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '5.2.2'
3
+ const VERSION = '5.3.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -343,6 +343,7 @@ function fastify (options) {
343
343
  decorateRequest: decorator.decorateRequest,
344
344
  hasRequestDecorator: decorator.existRequest,
345
345
  hasReplyDecorator: decorator.existReply,
346
+ getDecorator: decorator.getInstanceDecorator,
346
347
  addHttpMethod,
347
348
  // fake http injection
348
349
  inject,
@@ -639,7 +640,7 @@ function fastify (options) {
639
640
  resolveReady(fastify)
640
641
  fastify[kState].booting = false
641
642
  fastify[kState].ready = true
642
- fastify[kState].promise = null
643
+ fastify[kState].readyPromise = null
643
644
  }
644
645
  }
645
646
 
package/lib/decorate.js CHANGED
@@ -4,7 +4,7 @@ const {
4
4
  kReply,
5
5
  kRequest,
6
6
  kState,
7
- kHasBeenDecorated
7
+ kHasBeenDecorated,
8
8
  } = require('./symbols.js')
9
9
 
10
10
  const {
@@ -12,7 +12,8 @@ const {
12
12
  FST_ERR_DEC_MISSING_DEPENDENCY,
13
13
  FST_ERR_DEC_AFTER_START,
14
14
  FST_ERR_DEC_REFERENCE_TYPE,
15
- FST_ERR_DEC_DEPENDENCY_INVALID_TYPE
15
+ FST_ERR_DEC_DEPENDENCY_INVALID_TYPE,
16
+ FST_ERR_DEC_UNDECLARED,
16
17
  } = require('./errors')
17
18
 
18
19
  function decorate (instance, name, fn, dependencies) {
@@ -32,6 +33,18 @@ function decorate (instance, name, fn, dependencies) {
32
33
  }
33
34
  }
34
35
 
36
+ function getInstanceDecorator (name) {
37
+ if (!checkExistence(this, name)) {
38
+ throw new FST_ERR_DEC_UNDECLARED(name, 'instance')
39
+ }
40
+
41
+ if (typeof this[name] === 'function') {
42
+ return this[name].bind(this)
43
+ }
44
+
45
+ return this[name]
46
+ }
47
+
35
48
  function decorateConstructor (konstructor, name, fn, dependencies) {
36
49
  const instance = konstructor.prototype
37
50
  if (Object.hasOwn(instance, name) || hasKey(konstructor, name)) {
@@ -133,5 +146,7 @@ module.exports = {
133
146
  existReply: checkReplyExistence,
134
147
  dependencies: checkDependencies,
135
148
  decorateReply,
136
- decorateRequest
149
+ decorateRequest,
150
+ getInstanceDecorator,
151
+ hasKey
137
152
  }
package/lib/errors.js CHANGED
@@ -149,6 +149,10 @@ const codes = {
149
149
  'FST_ERR_DEC_REFERENCE_TYPE',
150
150
  "The decorator '%s' of type '%s' is a reference type. Use the { getter, setter } interface instead."
151
151
  ),
152
+ FST_ERR_DEC_UNDECLARED: createError(
153
+ 'FST_ERR_DEC_UNDECLARED',
154
+ "No decorator '%s' has been declared on %s."
155
+ ),
152
156
 
153
157
  /**
154
158
  * hooks
package/lib/reply.js CHANGED
@@ -22,7 +22,7 @@ const {
22
22
  kReplyCacheSerializeFns,
23
23
  kSchemaController,
24
24
  kOptions,
25
- kRouteContext
25
+ kRouteContext,
26
26
  } = require('./symbols.js')
27
27
  const {
28
28
  onSendHookRunner,
@@ -52,8 +52,10 @@ const {
52
52
  FST_ERR_BAD_TRAILER_NAME,
53
53
  FST_ERR_BAD_TRAILER_VALUE,
54
54
  FST_ERR_MISSING_SERIALIZATION_FN,
55
- FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN
55
+ FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN,
56
+ FST_ERR_DEC_UNDECLARED,
56
57
  } = require('./errors')
58
+ const decorators = require('./decorate')
57
59
 
58
60
  const toString = Object.prototype.toString
59
61
 
@@ -475,6 +477,19 @@ Reply.prototype.then = function (fulfilled, rejected) {
475
477
  })
476
478
  }
477
479
 
480
+ Reply.prototype.getDecorator = function (name) {
481
+ if (!decorators.hasKey(this, name) && !decorators.exist(this, name)) {
482
+ throw new FST_ERR_DEC_UNDECLARED(name, 'reply')
483
+ }
484
+
485
+ const decorator = this[name]
486
+ if (typeof decorator === 'function') {
487
+ return decorator.bind(this)
488
+ }
489
+
490
+ return decorator
491
+ }
492
+
478
493
  function preSerializationHook (reply, payload) {
479
494
  if (reply[kRouteContext].preSerialization !== null) {
480
495
  preSerializationHookRunner(
package/lib/request.js CHANGED
@@ -11,9 +11,10 @@ const {
11
11
  kOptions,
12
12
  kRequestCacheValidateFns,
13
13
  kRouteContext,
14
- kRequestOriginalUrl
14
+ kRequestOriginalUrl,
15
15
  } = require('./symbols')
16
- const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
16
+ const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION, FST_ERR_DEC_UNDECLARED } = require('./errors')
17
+ const decorators = require('./decorate')
17
18
 
18
19
  const HTTP_PART_SYMBOL_MAP = {
19
20
  body: kSchemaBody,
@@ -141,6 +142,12 @@ function buildRequestWithTrustProxy (R, trustProxy) {
141
142
  return _Request
142
143
  }
143
144
 
145
+ function assertsRequestDecoration (request, name) {
146
+ if (!decorators.hasKey(request, name) && !decorators.exist(request, name)) {
147
+ throw new FST_ERR_DEC_UNDECLARED(name, 'request')
148
+ }
149
+ }
150
+
144
151
  Object.defineProperties(Request.prototype, {
145
152
  server: {
146
153
  get () {
@@ -343,6 +350,25 @@ Object.defineProperties(Request.prototype, {
343
350
 
344
351
  return validate(input)
345
352
  }
353
+ },
354
+ getDecorator: {
355
+ value: function (name) {
356
+ assertsRequestDecoration(this, name)
357
+
358
+ const decorator = this[name]
359
+ if (typeof decorator === 'function') {
360
+ return decorator.bind(this)
361
+ }
362
+
363
+ return decorator
364
+ }
365
+ },
366
+ setDecorator: {
367
+ value: function (name, value) {
368
+ assertsRequestDecoration(this, name)
369
+
370
+ this[name] = value
371
+ }
346
372
  }
347
373
  })
348
374
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "5.2.2",
3
+ "version": "5.3.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -209,9 +209,9 @@
209
209
  "find-my-way": "^9.0.0",
210
210
  "light-my-request": "^6.0.0",
211
211
  "pino": "^9.0.0",
212
- "process-warning": "^4.0.0",
212
+ "process-warning": "^5.0.0",
213
213
  "rfdc": "^1.3.1",
214
- "secure-json-parse": "^3.0.1",
214
+ "secure-json-parse": "^4.0.0",
215
215
  "semver": "^7.6.0",
216
216
  "toad-cache": "^3.7.0"
217
217
  },
@@ -32,6 +32,5 @@ test('ensure the current error serializer is latest', { skip: !isPrepublish }, a
32
32
  const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
33
33
 
34
34
  // line break should not be a problem depends on system
35
- // t.assert.strictEqual(unifyLineBreak(current), unifyLineBreak(code))
36
- t.assert.ok(current)
35
+ t.assert.strictEqual(unifyLineBreak(current), unifyLineBreak(code))
37
36
  })
@@ -1,19 +1,18 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
3
+ const { test } = require('node:test')
5
4
  const sget = require('simple-get').concat
6
5
  const Fastify = require('../fastify')
7
6
  const jsonParser = require('fast-json-body')
8
7
  const { getServerUrl } = require('./helper')
8
+ const { waitForCb } = require('./toolkit')
9
9
 
10
10
  process.removeAllListeners('warning')
11
11
 
12
- test('should prefer string content types over RegExp ones', t => {
12
+ test('should prefer string content types over RegExp ones', (t, testDone) => {
13
13
  t.plan(7)
14
14
  const fastify = Fastify()
15
- t.teardown(fastify.close.bind(fastify))
16
-
15
+ t.after(() => { fastify.close() })
17
16
  fastify.post('/', (req, reply) => {
18
17
  reply.send(req.body)
19
18
  })
@@ -33,7 +32,8 @@ test('should prefer string content types over RegExp ones', t => {
33
32
  })
34
33
 
35
34
  fastify.listen({ port: 0 }, err => {
36
- t.error(err)
35
+ t.assert.ifError(err)
36
+ const completion = waitForCb({ steps: 2 })
37
37
 
38
38
  sget({
39
39
  method: 'POST',
@@ -43,9 +43,10 @@ test('should prefer string content types over RegExp ones', t => {
43
43
  'Content-Type': 'application/json'
44
44
  }
45
45
  }, (err, response, body) => {
46
- t.error(err)
47
- t.equal(response.statusCode, 200)
48
- t.same(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
46
+ t.assert.ifError(err)
47
+ t.assert.strictEqual(response.statusCode, 200)
48
+ t.assert.equal(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
49
+ completion.stepIn()
49
50
  })
50
51
 
51
52
  sget({
@@ -56,18 +57,21 @@ test('should prefer string content types over RegExp ones', t => {
56
57
  'Content-Type': 'application/javascript'
57
58
  }
58
59
  }, (err, response, body) => {
59
- t.error(err)
60
- t.equal(response.statusCode, 200)
61
- t.same(body.toString(), 'javascript')
60
+ t.assert.ifError(err)
61
+ t.assert.strictEqual(response.statusCode, 200)
62
+ t.assert.equal(body.toString(), 'javascript')
63
+ completion.stepIn()
62
64
  })
65
+
66
+ completion.patience.then(testDone)
63
67
  })
64
68
  })
65
69
 
66
- test('removeContentTypeParser should support arrays of content types to remove', t => {
70
+ test('removeContentTypeParser should support arrays of content types to remove', (t, testDone) => {
67
71
  t.plan(8)
68
72
 
69
73
  const fastify = Fastify()
70
- t.teardown(fastify.close.bind(fastify))
74
+ t.after(() => fastify.close())
71
75
 
72
76
  fastify.addContentTypeParser('application/xml', function (req, payload, done) {
73
77
  payload.on('data', () => {})
@@ -90,7 +94,8 @@ test('removeContentTypeParser should support arrays of content types to remove',
90
94
  })
91
95
 
92
96
  fastify.listen({ port: 0 }, err => {
93
- t.error(err)
97
+ t.assert.ifError(err)
98
+ const completion = waitForCb({ steps: 3 })
94
99
 
95
100
  sget({
96
101
  method: 'POST',
@@ -100,9 +105,10 @@ test('removeContentTypeParser should support arrays of content types to remove',
100
105
  'Content-Type': 'application/xml'
101
106
  }
102
107
  }, (err, response, body) => {
103
- t.error(err)
104
- t.equal(response.statusCode, 200)
105
- t.same(body.toString(), 'xml')
108
+ t.assert.ifError(err)
109
+ t.assert.strictEqual(response.statusCode, 200)
110
+ t.assert.equal(body.toString(), 'xml')
111
+ completion.stepIn()
106
112
  })
107
113
 
108
114
  sget({
@@ -113,8 +119,9 @@ test('removeContentTypeParser should support arrays of content types to remove',
113
119
  'Content-Type': 'image/png'
114
120
  }
115
121
  }, (err, response, body) => {
116
- t.error(err)
117
- t.equal(response.statusCode, 415)
122
+ t.assert.ifError(err)
123
+ t.assert.strictEqual(response.statusCode, 415)
124
+ completion.stepIn()
118
125
  })
119
126
 
120
127
  sget({
@@ -125,16 +132,19 @@ test('removeContentTypeParser should support arrays of content types to remove',
125
132
  'Content-Type': 'application/json'
126
133
  }
127
134
  }, (err, response, body) => {
128
- t.error(err)
129
- t.equal(response.statusCode, 415)
135
+ t.assert.ifError(err)
136
+ t.assert.strictEqual(response.statusCode, 415)
137
+ completion.stepIn()
130
138
  })
139
+ completion.patience.then(testDone)
131
140
  })
132
141
  })
133
142
 
134
- test('removeContentTypeParser should support encapsulation', t => {
143
+ test('removeContentTypeParser should support encapsulation', (t, testDone) => {
135
144
  t.plan(6)
136
145
 
137
146
  const fastify = Fastify()
147
+ t.after(() => fastify.close())
138
148
 
139
149
  fastify.addContentTypeParser('application/xml', function (req, payload, done) {
140
150
  payload.on('data', () => {})
@@ -158,7 +168,8 @@ test('removeContentTypeParser should support encapsulation', t => {
158
168
  })
159
169
 
160
170
  fastify.listen({ port: 0 }, err => {
161
- t.error(err)
171
+ t.assert.ifError(err)
172
+ const completion = waitForCb({ steps: 2 })
162
173
 
163
174
  sget({
164
175
  method: 'POST',
@@ -168,8 +179,9 @@ test('removeContentTypeParser should support encapsulation', t => {
168
179
  'Content-Type': 'application/xml'
169
180
  }
170
181
  }, (err, response, body) => {
171
- t.error(err)
172
- t.equal(response.statusCode, 415)
182
+ t.assert.ifError(err)
183
+ t.assert.strictEqual(response.statusCode, 415)
184
+ completion.stepIn()
173
185
  })
174
186
 
175
187
  sget({
@@ -180,18 +192,20 @@ test('removeContentTypeParser should support encapsulation', t => {
180
192
  'Content-Type': 'application/xml'
181
193
  }
182
194
  }, (err, response, body) => {
183
- t.error(err)
184
- t.equal(response.statusCode, 200)
185
- t.same(body.toString(), 'xml')
186
- fastify.close()
195
+ t.assert.ifError(err)
196
+ t.assert.strictEqual(response.statusCode, 200)
197
+ t.assert.equal(body.toString(), 'xml')
198
+ completion.stepIn()
187
199
  })
200
+ completion.patience.then(testDone)
188
201
  })
189
202
  })
190
203
 
191
- test('removeAllContentTypeParsers should support encapsulation', t => {
204
+ test('removeAllContentTypeParsers should support encapsulation', (t, testDone) => {
192
205
  t.plan(6)
193
206
 
194
207
  const fastify = Fastify()
208
+ t.after(() => fastify.close())
195
209
 
196
210
  fastify.post('/', (req, reply) => {
197
211
  reply.send(req.body)
@@ -208,7 +222,8 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
208
222
  })
209
223
 
210
224
  fastify.listen({ port: 0 }, err => {
211
- t.error(err)
225
+ t.assert.ifError(err)
226
+ const completion = waitForCb({ steps: 2 })
212
227
 
213
228
  sget({
214
229
  method: 'POST',
@@ -218,8 +233,9 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
218
233
  'Content-Type': 'application/json'
219
234
  }
220
235
  }, (err, response, body) => {
221
- t.error(err)
222
- t.equal(response.statusCode, 415)
236
+ t.assert.ifError(err)
237
+ t.assert.strictEqual(response.statusCode, 415)
238
+ completion.stepIn()
223
239
  })
224
240
 
225
241
  sget({
@@ -230,10 +246,11 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
230
246
  'Content-Type': 'application/json'
231
247
  }
232
248
  }, (err, response, body) => {
233
- t.error(err)
234
- t.equal(response.statusCode, 200)
235
- t.same(JSON.parse(body.toString()).test, 1)
236
- fastify.close()
249
+ t.assert.ifError(err)
250
+ t.assert.strictEqual(response.statusCode, 200)
251
+ t.assert.equal(JSON.parse(body.toString()).test, 1)
252
+ completion.stepIn()
237
253
  })
254
+ completion.patience.then(testDone)
238
255
  })
239
256
  })