fastify 4.3.0 → 4.5.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.
Files changed (45) hide show
  1. package/build/build-validation.js +1 -1
  2. package/docs/Guides/Ecosystem.md +23 -0
  3. package/docs/Reference/Hooks.md +4 -1
  4. package/docs/Reference/Logging.md +1 -1
  5. package/docs/Reference/Reply.md +1 -0
  6. package/docs/Reference/Routes.md +6 -3
  7. package/docs/Reference/Server.md +20 -7
  8. package/docs/Reference/Validation-and-Serialization.md +1 -1
  9. package/fastify.d.ts +1 -1
  10. package/fastify.js +4 -4
  11. package/lib/configValidator.js +68 -19
  12. package/lib/context.js +1 -0
  13. package/lib/handleRequest.js +2 -2
  14. package/lib/httpMethods.js +22 -0
  15. package/lib/reqIdGenFactory.js +13 -2
  16. package/lib/route.js +2 -4
  17. package/lib/server.js +1 -0
  18. package/package.json +3 -9
  19. package/test/copy.test.js +41 -0
  20. package/test/hooks-async.test.js +43 -0
  21. package/test/internals/all.test.js +8 -2
  22. package/test/internals/reply.test.js +4 -1
  23. package/test/lock.test.js +73 -0
  24. package/test/logger.test.js +108 -0
  25. package/test/mkcol.test.js +38 -0
  26. package/test/move.test.js +45 -0
  27. package/test/propfind.test.js +108 -0
  28. package/test/proppatch.test.js +78 -0
  29. package/test/schema-examples.test.js +54 -0
  30. package/test/search.test.js +100 -0
  31. package/test/trace.test.js +21 -0
  32. package/test/types/fastify.test-d.ts +1 -0
  33. package/test/types/instance.test-d.ts +1 -1
  34. package/test/types/logger.test-d.ts +4 -5
  35. package/test/types/reply.test-d.ts +44 -3
  36. package/test/types/request.test-d.ts +9 -28
  37. package/test/types/type-provider.test-d.ts +3 -1
  38. package/test/unlock.test.js +41 -0
  39. package/test/upgrade.test.js +53 -0
  40. package/types/instance.d.ts +1 -1
  41. package/types/logger.d.ts +7 -4
  42. package/types/reply.d.ts +7 -6
  43. package/types/request.d.ts +2 -2
  44. package/types/type-provider.d.ts +1 -14
  45. package/types/utils.d.ts +2 -1
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('can be created - copy', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'COPY',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ reply.code(204).send()
16
+ }
17
+ })
18
+ t.pass()
19
+ } catch (e) {
20
+ t.fail()
21
+ }
22
+ })
23
+
24
+ fastify.listen({ port: 0 }, err => {
25
+ t.error(err)
26
+ t.teardown(() => { fastify.close() })
27
+
28
+ test('request - copy', t => {
29
+ t.plan(2)
30
+ sget({
31
+ url: `http://localhost:${fastify.server.address().port}/test.txt`,
32
+ method: 'COPY',
33
+ headers: {
34
+ Destination: '/test2.txt'
35
+ }
36
+ }, (err, response, body) => {
37
+ t.error(err)
38
+ t.equal(response.statusCode, 204)
39
+ })
40
+ })
41
+ })
@@ -710,3 +710,46 @@ test('preSerializationEnd should handle errors if the serialize method throws',
710
710
 
711
711
  t.end()
712
712
  })
713
+
714
+ t.test('nested hooks to do not crash on 404', t => {
715
+ t.plan(2)
716
+ const fastify = Fastify()
717
+
718
+ fastify.get('/hello', (req, reply) => {
719
+ reply.send({ hello: 'world' })
720
+ })
721
+
722
+ fastify.register(async function (fastify) {
723
+ fastify.get('/something', (req, reply) => {
724
+ reply.callNotFound()
725
+ })
726
+
727
+ fastify.setNotFoundHandler(async (request, reply) => {
728
+ reply.statusCode = 404
729
+ return { status: 'nested-not-found' }
730
+ })
731
+
732
+ fastify.setErrorHandler(async (error, request, reply) => {
733
+ reply.statusCode = 500
734
+ return { status: 'nested-error', error }
735
+ })
736
+ }, { prefix: '/nested' })
737
+
738
+ fastify.setNotFoundHandler(async (request, reply) => {
739
+ reply.statusCode = 404
740
+ return { status: 'not-found' }
741
+ })
742
+
743
+ fastify.setErrorHandler(async (error, request, reply) => {
744
+ reply.statusCode = 500
745
+ return { status: 'error', error }
746
+ })
747
+
748
+ fastify.inject({
749
+ method: 'GET',
750
+ url: '/nested/something'
751
+ }, (err, res) => {
752
+ t.error(err)
753
+ t.equal(res.statusCode, 404)
754
+ })
755
+ })
@@ -3,9 +3,15 @@
3
3
  const t = require('tap')
4
4
  const test = t.test
5
5
  const Fastify = require('../..')
6
- const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
6
+ const { supportedMethods } = require('../../lib/httpMethods')
7
7
 
8
8
  test('fastify.all should add all the methods to the same url', t => {
9
+ const requirePayload = [
10
+ 'POST',
11
+ 'PUT',
12
+ 'PATCH'
13
+ ]
14
+
9
15
  t.plan(supportedMethods.length * 2)
10
16
 
11
17
  const fastify = Fastify()
@@ -22,7 +28,7 @@ test('fastify.all should add all the methods to the same url', t => {
22
28
  method
23
29
  }
24
30
 
25
- if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
31
+ if (requirePayload.includes(method)) {
26
32
  options.payload = { hello: 'world' }
27
33
  }
28
34
 
@@ -1044,7 +1044,7 @@ test('reply.hasHeader returns correct values', t => {
1044
1044
  })
1045
1045
 
1046
1046
  test('reply.getHeader returns correct values', t => {
1047
- t.plan(4)
1047
+ t.plan(5)
1048
1048
 
1049
1049
  const fastify = require('../../')()
1050
1050
 
@@ -1055,6 +1055,9 @@ test('reply.getHeader returns correct values', t => {
1055
1055
  reply.header('x-foo', 'bar')
1056
1056
  t.strictSame(reply.getHeader('x-foo'), 'bar')
1057
1057
 
1058
+ reply.header('x-foo', 42)
1059
+ t.strictSame(reply.getHeader('x-foo'), 42)
1060
+
1058
1061
  reply.header('set-cookie', 'one')
1059
1062
  reply.header('set-cookie', 'two')
1060
1063
  t.strictSame(reply.getHeader('set-cookie'), ['one', 'two'])
@@ -0,0 +1,73 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('can be created - lock', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'LOCK',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ reply
16
+ .code(200)
17
+ .send(`<?xml version="1.0" encoding="utf-8" ?>
18
+ <D:prop xmlns:D="DAV:">
19
+ <D:lockdiscovery>
20
+ <D:activelock>
21
+ <D:locktype>
22
+ <D:write/>
23
+ </D:locktype>
24
+ <D:lockscope>
25
+ <D:exclusive/>
26
+ </D:lockscope>
27
+ <D:depth>infinity</D:depth>
28
+ <D:owner>
29
+ <D:href>http://example.org/~ejw/contact.html</D:href>
30
+ </D:owner>
31
+ <D:timeout>Second-604800</D:timeout>
32
+ <D:locktoken>
33
+ <D:href>urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4</:href>
34
+ </D:locktoken>
35
+ <D:lockroot>
36
+ <D:href>http://example.com/workspace/webdav/proposal.oc</D:href>
37
+ </D:lockroot>
38
+ </D:activelock>
39
+ </D:lockdiscovery>
40
+ </D:prop>`
41
+ )
42
+ }
43
+ })
44
+ t.pass()
45
+ } catch (e) {
46
+ t.fail()
47
+ }
48
+ })
49
+
50
+ fastify.listen({ port: 0 }, err => {
51
+ t.error(err)
52
+ t.teardown(() => { fastify.close() })
53
+
54
+ test('request - lock', t => {
55
+ t.plan(3)
56
+ sget({
57
+ url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
58
+ body: `<?xml version="1.0" encoding="utf-8" ?>
59
+ <D:lockinfo xmlns:D='DAV:'>
60
+ <D:lockscope> <D:exclusive/> </D:lockscope>
61
+ <D:locktype> <D:write/> </D:locktype>
62
+ <D:owner>
63
+ <D:href>http://example.org/~ejw/contact.html</D:href>
64
+ </D:owner>
65
+ </D:lockinfo> `,
66
+ method: 'LOCK'
67
+ }, (err, response, body) => {
68
+ t.error(err)
69
+ t.equal(response.statusCode, 200)
70
+ t.equal(response.headers['content-length'], '' + body.length)
71
+ })
72
+ })
73
+ })
@@ -330,6 +330,51 @@ test('The request id header key can be customized', t => {
330
330
  })
331
331
  })
332
332
 
333
+ test('The request id header key can be ignored', t => {
334
+ t.plan(9)
335
+ const REQUEST_ID = 'ignore-me'
336
+
337
+ const stream = split(JSON.parse)
338
+ const fastify = Fastify({
339
+ logger: { stream, level: 'info' },
340
+ requestIdHeader: false
341
+ })
342
+ t.teardown(() => fastify.close())
343
+
344
+ fastify.get('/', (req, reply) => {
345
+ t.equal(req.id, 'req-1')
346
+ req.log.info('some log message')
347
+ reply.send({ id: req.id })
348
+ })
349
+
350
+ fastify.inject({
351
+ method: 'GET',
352
+ url: '/',
353
+ headers: {
354
+ 'request-id': REQUEST_ID
355
+ }
356
+ }, (err, res) => {
357
+ t.error(err)
358
+ const payload = JSON.parse(res.payload)
359
+ t.equal(payload.id, 'req-1')
360
+
361
+ stream.once('data', line => {
362
+ t.equal(line.reqId, 'req-1')
363
+ t.equal(line.msg, 'incoming request', 'message is set')
364
+
365
+ stream.once('data', line => {
366
+ t.equal(line.reqId, 'req-1')
367
+ t.equal(line.msg, 'some log message', 'message is set')
368
+
369
+ stream.once('data', line => {
370
+ t.equal(line.reqId, 'req-1')
371
+ t.equal(line.msg, 'request completed', 'message is set')
372
+ })
373
+ })
374
+ })
375
+ })
376
+ })
377
+
333
378
  test('The request id header key can be customized along with a custom id generator', t => {
334
379
  t.plan(12)
335
380
  const REQUEST_ID = '42'
@@ -393,6 +438,69 @@ test('The request id header key can be customized along with a custom id generat
393
438
  })
394
439
  })
395
440
 
441
+ test('The request id header key can be ignored along with a custom id generator', t => {
442
+ t.plan(12)
443
+ const REQUEST_ID = 'ignore-me'
444
+
445
+ const stream = split(JSON.parse)
446
+ const fastify = Fastify({
447
+ logger: { stream, level: 'info' },
448
+ requestIdHeader: false,
449
+ genReqId (req) {
450
+ return 'foo'
451
+ }
452
+ })
453
+ t.teardown(() => fastify.close())
454
+
455
+ fastify.get('/one', (req, reply) => {
456
+ t.equal(req.id, 'foo')
457
+ req.log.info('some log message')
458
+ reply.send({ id: req.id })
459
+ })
460
+
461
+ fastify.get('/two', (req, reply) => {
462
+ t.equal(req.id, 'foo')
463
+ req.log.info('some log message 2')
464
+ reply.send({ id: req.id })
465
+ })
466
+
467
+ const matches = [
468
+ { reqId: 'foo', msg: /incoming request/ },
469
+ { reqId: 'foo', msg: /some log message/ },
470
+ { reqId: 'foo', msg: /request completed/ },
471
+ { reqId: 'foo', msg: /incoming request/ },
472
+ { reqId: 'foo', msg: /some log message 2/ },
473
+ { reqId: 'foo', msg: /request completed/ }
474
+ ]
475
+
476
+ let i = 0
477
+ stream.on('data', line => {
478
+ t.match(line, matches[i])
479
+ i += 1
480
+ })
481
+
482
+ fastify.inject({
483
+ method: 'GET',
484
+ url: '/one',
485
+ headers: {
486
+ 'request-id': REQUEST_ID
487
+ }
488
+ }, (err, res) => {
489
+ t.error(err)
490
+ const payload = JSON.parse(res.payload)
491
+ t.equal(payload.id, 'foo')
492
+ })
493
+
494
+ fastify.inject({
495
+ method: 'GET',
496
+ url: '/two'
497
+ }, (err, res) => {
498
+ t.error(err)
499
+ const payload = JSON.parse(res.payload)
500
+ t.equal(payload.id, 'foo')
501
+ })
502
+ })
503
+
396
504
  test('The request id log label can be changed', t => {
397
505
  t.plan(6)
398
506
  const REQUEST_ID = '42'
@@ -0,0 +1,38 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('can be created - mkcol', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'MKCOL',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ reply.code(201).send()
16
+ }
17
+ })
18
+ t.pass()
19
+ } catch (e) {
20
+ t.fail()
21
+ }
22
+ })
23
+
24
+ fastify.listen({ port: 0 }, err => {
25
+ t.error(err)
26
+ t.teardown(() => { fastify.close() })
27
+
28
+ test('request - mkcol', t => {
29
+ t.plan(2)
30
+ sget({
31
+ url: `http://localhost:${fastify.server.address().port}/test/`,
32
+ method: 'MKCOL'
33
+ }, (err, response, body) => {
34
+ t.error(err)
35
+ t.equal(response.statusCode, 201)
36
+ })
37
+ })
38
+ })
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('shorthand - move', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'MOVE',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ const destination = req.headers.destination
16
+ reply.code(201)
17
+ .header('location', destination)
18
+ .send()
19
+ }
20
+ })
21
+ t.pass()
22
+ } catch (e) {
23
+ t.fail()
24
+ }
25
+ })
26
+
27
+ fastify.listen({ port: 0 }, err => {
28
+ t.error(err)
29
+ t.teardown(() => { fastify.close() })
30
+
31
+ test('request - move', t => {
32
+ t.plan(3)
33
+ sget({
34
+ url: `http://localhost:${fastify.server.address().port}/test.txt`,
35
+ method: 'MOVE',
36
+ headers: {
37
+ Destination: '/test2.txt'
38
+ }
39
+ }, (err, response, body) => {
40
+ t.error(err)
41
+ t.equal(response.statusCode, 201)
42
+ t.equal(response.headers.location, '/test2.txt')
43
+ })
44
+ })
45
+ })
@@ -0,0 +1,108 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('can be created - propfind', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'PROPFIND',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ return reply.code(207)
16
+ .send(`<?xml version="1.0" encoding="utf-8"?>
17
+ <D:multistatus xmlns:D="DAV:">
18
+ <D:response xmlns:lp1="DAV:">
19
+ <D:href>/</D:href>
20
+ <D:propstat>
21
+ <D:prop>
22
+ <lp1:resourcetype>
23
+ <D:collection/>
24
+ </lp1:resourcetype>
25
+ <lp1:creationdate>2022-04-13T12:35:30Z</lp1:creationdate>
26
+ <lp1:getlastmodified>Wed, 13 Apr 2022 12:35:30 GMT</lp1:getlastmodified>
27
+ <lp1:getetag>"e0-5dc8869b53ef1"</lp1:getetag>
28
+ <D:supportedlock>
29
+ <D:lockentry>
30
+ <D:lockscope>
31
+ <D:exclusive/>
32
+ </D:lockscope>
33
+ <D:locktype>
34
+ <D:write/>
35
+ </D:locktype>
36
+ </D:lockentry>
37
+ <D:lockentry>
38
+ <D:lockscope>
39
+ <D:shared/>
40
+ </D:lockscope>
41
+ <D:locktype>
42
+ <D:write/>
43
+ </D:locktype>
44
+ </D:lockentry>
45
+ </D:supportedlock>
46
+ <D:lockdiscovery/>
47
+ <D:getcontenttype>httpd/unix-directory</D:getcontenttype>
48
+ </D:prop>
49
+ <D:status>HTTP/1.1 200 OK</D:status>
50
+ </D:propstat>
51
+ </D:response>
52
+ </D:multistatus>`
53
+ )
54
+ }
55
+ })
56
+ t.pass()
57
+ } catch (e) {
58
+ t.fail()
59
+ }
60
+ })
61
+
62
+ fastify.listen({ port: 0 }, err => {
63
+ t.error(err)
64
+ t.teardown(() => { fastify.close() })
65
+
66
+ test('request - propfind', t => {
67
+ t.plan(3)
68
+ sget({
69
+ url: `http://localhost:${fastify.server.address().port}/`,
70
+ method: 'PROPFIND'
71
+ }, (err, response, body) => {
72
+ t.error(err)
73
+ t.equal(response.statusCode, 207)
74
+ t.equal(response.headers['content-length'], '' + body.length)
75
+ })
76
+ })
77
+
78
+ test('request with other path - propfind', t => {
79
+ t.plan(3)
80
+ sget({
81
+ url: `http://localhost:${fastify.server.address().port}/test`,
82
+ method: 'PROPFIND'
83
+ }, (err, response, body) => {
84
+ t.error(err)
85
+ t.equal(response.statusCode, 207)
86
+ t.equal(response.headers['content-length'], '' + body.length)
87
+ })
88
+ })
89
+
90
+ test('request with body - propfind', t => {
91
+ t.plan(3)
92
+ sget({
93
+ url: `http://localhost:${fastify.server.address().port}/test`,
94
+ body: `<?xml version="1.0" encoding="utf-8" ?>
95
+ <D:propfind xmlns:D="DAV:">
96
+ <D:prop xmlns:R="http://ns.example.com/boxschema/">
97
+ <R:bigbox/> <R:author/> <R:DingALing/> <R:Random/>
98
+ </D:prop>
99
+ </D:propfind>
100
+ `,
101
+ method: 'PROPFIND'
102
+ }, (err, response, body) => {
103
+ t.error(err)
104
+ t.equal(response.statusCode, 207)
105
+ t.equal(response.headers['content-length'], '' + body.length)
106
+ })
107
+ })
108
+ })
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const sget = require('simple-get').concat
6
+ const fastify = require('..')()
7
+
8
+ test('shorthand - proppatch', t => {
9
+ t.plan(1)
10
+ try {
11
+ fastify.route({
12
+ method: 'PROPPATCH',
13
+ url: '*',
14
+ handler: function (req, reply) {
15
+ reply
16
+ .code(207)
17
+ .send(`<?xml version="1.0" encoding="utf-8" ?>
18
+ <D:multistatus xmlns:D="DAV:"
19
+ xmlns:Z="http://ns.example.com/standards/z39.50/">
20
+ <D:response>
21
+ <D:href>http://www.example.com/bar.html</D:href>
22
+ <D:propstat>
23
+ <D:prop>
24
+ <Z:Authors/>
25
+ </D:prop>
26
+ <D:status>HTTP/1.1 424 Failed Dependency</D:status>
27
+ </D:propstat>
28
+ <D:propstat>
29
+ <D:prop>
30
+ <Z:Copyright-Owner/>
31
+ </D:prop>
32
+ <D:status>HTTP/1.1 409 Conflict</D:status>
33
+ </D:propstat>
34
+ <D:responsedescription> Copyright Owner cannot be deleted or altered.</D:responsedescription>
35
+ </D:response>
36
+ </D:multistatus>`
37
+ )
38
+ }
39
+ })
40
+ t.pass()
41
+ } catch (e) {
42
+ t.fail()
43
+ }
44
+ })
45
+
46
+ fastify.listen({ port: 0 }, err => {
47
+ t.error(err)
48
+ t.teardown(() => { fastify.close() })
49
+
50
+ test('request - proppatch', t => {
51
+ t.plan(3)
52
+ sget({
53
+ url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
54
+ body: `<?xml version="1.0" encoding="utf-8" ?>
55
+ <D:propertyupdate xmlns:D="DAV:"
56
+ xmlns:Z="http://ns.example.com/standards/z39.50/">
57
+ <D:set>
58
+ <D:prop>
59
+ <Z:Authors>
60
+ <Z:Author>Jim Whitehead</Z:Author>
61
+ <Z:Author>Roy Fielding</Z:Author>
62
+ </Z:Authors>
63
+ </D:prop>
64
+ </D:set>
65
+ <D:remove>
66
+ <D:prop>
67
+ <Z:Copyright-Owner/>
68
+ </D:prop>
69
+ </D:remove>
70
+ </D:propertyupdate>`,
71
+ method: 'PROPPATCH'
72
+ }, (err, response, body) => {
73
+ t.error(err)
74
+ t.equal(response.statusCode, 207)
75
+ t.equal(response.headers['content-length'], '' + body.length)
76
+ })
77
+ })
78
+ })
@@ -509,6 +509,60 @@ test('should return custom error messages with ajv-errors', t => {
509
509
  })
510
510
  })
511
511
 
512
+ test('should be able to handle formats of ajv-formats when added by plugins option', t => {
513
+ t.plan(3)
514
+
515
+ const fastify = Fastify({
516
+ ajv: {
517
+ plugins: [
518
+ require('ajv-formats')
519
+ ]
520
+ }
521
+ })
522
+
523
+ const schema = {
524
+ body: {
525
+ type: 'object',
526
+ properties: {
527
+ id: { type: 'string', format: 'uuid' },
528
+ email: { type: 'string', format: 'email' }
529
+ },
530
+ required: ['id', 'email']
531
+ }
532
+ }
533
+
534
+ fastify.post('/', { schema }, function (req, reply) {
535
+ reply.code(200).send(req.body.id)
536
+ })
537
+
538
+ fastify.inject({
539
+ method: 'POST',
540
+ payload: {
541
+ id: '254381a5-888c-4b41-8116-e3b1a54980bd',
542
+ email: 'info@fastify.io'
543
+ },
544
+ url: '/'
545
+ }, (_err, res) => {
546
+ t.equal(res.body, '254381a5-888c-4b41-8116-e3b1a54980bd')
547
+ t.equal(res.statusCode, 200)
548
+ })
549
+
550
+ fastify.inject({
551
+ method: 'POST',
552
+ payload: {
553
+ id: 'invalid',
554
+ email: 'info@fastify.io'
555
+ },
556
+ url: '/'
557
+ }, (_err, res) => {
558
+ t.same(JSON.parse(res.payload), {
559
+ statusCode: 400,
560
+ error: 'Bad Request',
561
+ message: 'body/id must match format "uuid"'
562
+ })
563
+ })
564
+ })
565
+
512
566
  test('should return localized error messages with ajv-i18n', t => {
513
567
  t.plan(3)
514
568