strong-tie/inbound-calls
0
1'use strict'2 3const t = require('tap')4const Fastify = require('..')5const test = t.test6 7const echoBody = (req, reply) => { reply.send(req.body) }8 9test('basic test', t => {10 t.plan(3)11 12 const fastify = Fastify()13 fastify.get('/', {14 schema: {15 response: {16 '2xx': {17 type: 'object',18 properties: {19 name: { type: 'string' },20 work: { type: 'string' }21 }22 }23 }24 }25 }, function (req, reply) {26 reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })27 })28 29 fastify.inject('/', (err, res) => {30 t.error(err)31 t.same(res.json(), { name: 'Foo', work: 'Bar' })32 t.equal(res.statusCode, 200)33 })34})35 36test('custom serializer options', t => {37 t.plan(3)38 39 const fastify = Fastify({40 serializerOpts: {41 rounding: 'ceil'42 }43 })44 fastify.get('/', {45 schema: {46 response: {47 '2xx': {48 type: 'integer'49 }50 }51 }52 }, function (req, reply) {53 reply.send(4.2)54 })55 56 fastify.inject('/', (err, res) => {57 t.error(err)58 t.equal(res.payload, '5', 'it must use the ceil rounding')59 t.equal(res.statusCode, 200)60 })61})62 63test('Different content types', t => {64 t.plan(46)65 66 const fastify = Fastify()67 fastify.addSchema({68 $id: 'test',69 type: 'object',70 properties: {71 name: { type: 'string' },72 age: { type: 'number' },73 verified: { type: 'boolean' }74 }75 })76 77 fastify.get('/', {78 schema: {79 response: {80 200: {81 content: {82 'application/json': {83 schema: {84 type: 'object',85 properties: {86 name: { type: 'string' },87 image: { type: 'string' },88 address: { type: 'string' }89 }90 }91 },92 'application/vnd.v1+json': {93 schema: {94 type: 'array',95 items: { $ref: 'test' }96 }97 }98 }99 },100 201: {101 content: {102 '*/*': {103 schema: { type: 'string' }104 }105 }106 },107 202: {108 content: {109 '*/*': {110 schema: { const: 'Processing exclusive content' }111 }112 }113 },114 '3xx': {115 content: {116 'application/vnd.v2+json': {117 schema: {118 type: 'object',119 properties: {120 fullName: { type: 'string' },121 phone: { type: 'string' }122 }123 }124 }125 }126 },127 '4xx': {128 content: {129 '*/*': {130 schema: {131 type: 'object',132 properties: {133 details: { type: 'string' }134 }135 }136 }137 }138 },139 default: {140 content: {141 'application/json': {142 schema: {143 type: 'object',144 properties: {145 details: { type: 'string' }146 }147 }148 },149 '*/*': {150 schema: {151 type: 'object',152 properties: {153 desc: { type: 'string' },154 details: { type: 'string' }155 }156 }157 }158 }159 }160 }161 }162 }, function (req, reply) {163 switch (req.headers.accept) {164 case 'application/json':165 reply.header('Content-Type', 'application/json')166 reply.send({ id: 1, name: 'Foo', image: 'profile picture', address: 'New Node' })167 break168 case 'application/vnd.v1+json':169 reply.header('Content-Type', 'application/vnd.v1+json')170 reply.send([{ id: 2, name: 'Boo', age: 18, verified: false }, { id: 3, name: 'Woo', age: 30, verified: true }])171 break172 case 'application/vnd.v2+json':173 reply.header('Content-Type', 'application/vnd.v2+json')174 reply.code(300)175 reply.send({ fullName: 'Jhon Smith', phone: '01090000000', authMethod: 'google' })176 break177 case 'application/vnd.v3+json':178 reply.header('Content-Type', 'application/vnd.v3+json')179 reply.code(300)180 reply.send({ firstName: 'New', lastName: 'Hoo', country: 'eg', city: 'node' })181 break182 case 'application/vnd.v4+json':183 reply.header('Content-Type', 'application/vnd.v4+json')184 reply.code(201)185 reply.send({ boxId: 1, content: 'Games' })186 break187 case 'application/vnd.v5+json':188 reply.header('Content-Type', 'application/vnd.v5+json')189 reply.code(202)190 reply.send({ content: 'interesting content' })191 break192 case 'application/vnd.v6+json':193 reply.header('Content-Type', 'application/vnd.v6+json')194 reply.code(400)195 reply.send({ desc: 'age is missing', details: 'validation error' })196 break197 case 'application/vnd.v7+json':198 reply.code(400)199 reply.send({ details: 'validation error' })200 break201 case 'application/vnd.v8+json':202 reply.header('Content-Type', 'application/vnd.v8+json')203 reply.code(500)204 reply.send({ desc: 'age is missing', details: 'validation error' })205 break206 case 'application/vnd.v9+json':207 reply.code(500)208 reply.send({ details: 'validation error' })209 break210 default:211 // to test if schema not found212 reply.header('Content-Type', 'application/vnd.v3+json')213 reply.code(200)214 reply.send([{ type: 'student', grade: 6 }, { type: 'student', grade: 9 }])215 }216 })217 218 fastify.get('/test', {219 serializerCompiler: ({ contentType }) => {220 t.equal(contentType, 'application/json')221 return data => JSON.stringify(data)222 },223 schema: {224 response: {225 200: {226 content: {227 'application/json': {228 schema: {229 type: 'object',230 properties: {231 name: { type: 'string' },232 image: { type: 'string' },233 address: { type: 'string' }234 }235 }236 }237 }238 },239 default: {240 content: {241 'application/json': {242 schema: {243 type: 'object',244 properties: {245 details: { type: 'string' }246 }247 }248 }249 }250 }251 }252 }253 }, function (req, reply) {254 switch (req.headers['code']) {255 case '200': {256 reply.header('Content-Type', 'application/json')257 reply.code(200).send({ age: 18, city: 'AU' })258 break259 }260 case '201': {261 reply.header('Content-Type', 'application/json')262 reply.code(201).send({ details: 'validation error' })263 break264 }265 default: {266 reply.header('Content-Type', 'application/vnd.v1+json')267 reply.code(201).send({ created: true })268 break269 }270 }271 })272 273 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/json' } }, (err, res) => {274 t.error(err)275 t.equal(res.payload, JSON.stringify({ name: 'Foo', image: 'profile picture', address: 'New Node' }))276 t.equal(res.statusCode, 200)277 })278 279 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v1+json' } }, (err, res) => {280 t.error(err)281 t.equal(res.payload, JSON.stringify([{ name: 'Boo', age: 18, verified: false }, { name: 'Woo', age: 30, verified: true }]))282 t.equal(res.statusCode, 200)283 })284 285 fastify.inject({ method: 'GET', url: '/' }, (err, res) => {286 t.error(err)287 t.equal(res.payload, JSON.stringify([{ type: 'student', grade: 6 }, { type: 'student', grade: 9 }]))288 t.equal(res.statusCode, 200)289 })290 291 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v2+json' } }, (err, res) => {292 t.error(err)293 t.equal(res.payload, JSON.stringify({ fullName: 'Jhon Smith', phone: '01090000000' }))294 t.equal(res.statusCode, 300)295 })296 297 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v3+json' } }, (err, res) => {298 t.error(err)299 t.equal(res.payload, JSON.stringify({ firstName: 'New', lastName: 'Hoo', country: 'eg', city: 'node' }))300 t.equal(res.statusCode, 300)301 })302 303 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v4+json' } }, (err, res) => {304 t.error(err)305 t.equal(res.payload, '"[object Object]"')306 t.equal(res.statusCode, 201)307 })308 309 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v5+json' } }, (err, res) => {310 t.error(err)311 t.equal(res.payload, '"Processing exclusive content"')312 t.equal(res.statusCode, 202)313 })314 315 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v6+json' } }, (err, res) => {316 t.error(err)317 t.equal(res.payload, JSON.stringify({ details: 'validation error' }))318 t.equal(res.statusCode, 400)319 })320 321 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v7+json' } }, (err, res) => {322 t.error(err)323 t.equal(res.payload, JSON.stringify({ details: 'validation error' }))324 t.equal(res.statusCode, 400)325 })326 327 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v8+json' } }, (err, res) => {328 t.error(err)329 t.equal(res.payload, JSON.stringify({ desc: 'age is missing', details: 'validation error' }))330 t.equal(res.statusCode, 500)331 })332 333 fastify.inject({ method: 'GET', url: '/', headers: { Accept: 'application/vnd.v9+json' } }, (err, res) => {334 t.error(err)335 t.equal(res.payload, JSON.stringify({ details: 'validation error' }))336 t.equal(res.statusCode, 500)337 })338 339 fastify.inject({ method: 'GET', url: '/test', headers: { Code: '200' } }, (err, res) => {340 t.error(err)341 t.equal(res.payload, JSON.stringify({ age: 18, city: 'AU' }))342 t.equal(res.statusCode, 200)343 })344 345 fastify.inject({ method: 'GET', url: '/test', headers: { Code: '201' } }, (err, res) => {346 t.error(err)347 t.equal(res.payload, JSON.stringify({ details: 'validation error' }))348 t.equal(res.statusCode, 201)349 })350 351 fastify.inject({ method: 'GET', url: '/test', headers: { Accept: 'application/vnd.v1+json' } }, (err, res) => {352 t.error(err)353 t.equal(res.payload, JSON.stringify({ created: true }))354 t.equal(res.statusCode, 201)355 })356})357 358test('Invalid multiple content schema, throw FST_ERR_SCH_CONTENT_MISSING_SCHEMA error', t => {359 t.plan(3)360 const fastify = Fastify()361 362 fastify.get('/testInvalid', {363 schema: {364 response: {365 200: {366 content: {367 'application/json': {368 schema: {369 type: 'object',370 properties: {371 fullName: { type: 'string' },372 phone: { type: 'string' }373 }374 },375 example: {376 fullName: 'John Doe',377 phone: '201090243795'378 }379 },380 type: 'string'381 }382 }383 }384 }385 }, function (req, reply) {386 reply.header('Content-Type', 'application/json')387 reply.send({ fullName: 'Any name', phone: '0109001010' })388 })389 390 fastify.ready((err) => {391 t.equal(err.message, "Schema is missing for the content type 'type'")392 t.equal(err.statusCode, 500)393 t.equal(err.code, 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA')394 })395})396 397test('Use the same schema id in different places', t => {398 t.plan(2)399 const fastify = Fastify()400 401 fastify.addSchema({402 $id: 'test',403 type: 'object',404 properties: {405 id: { type: 'number' }406 }407 })408 409 fastify.get('/:id', {410 handler (req, reply) {411 reply.send([{ id: 1 }, { id: 2 }, { what: 'is this' }])412 },413 schema: {414 response: {415 200: {416 type: 'array',417 items: { $ref: 'test' }418 }419 }420 }421 })422 423 fastify.inject({424 method: 'GET',425 url: '/123'426 }, (err, res) => {427 t.error(err)428 t.same(res.json(), [{ id: 1 }, { id: 2 }, {}])429 })430})431 432test('Use shared schema and $ref with $id in response ($ref to $id)', t => {433 t.plan(5)434 const fastify = Fastify()435 436 fastify.addSchema({437 $id: 'http://foo/test',438 type: 'object',439 properties: {440 id: { type: 'number' }441 }442 })443 444 const complexSchema = {445 $schema: 'http://json-schema.org/draft-07/schema#',446 $id: 'http://foo/user',447 type: 'object',448 definitions: {449 address: {450 $id: '#address',451 type: 'object',452 properties: {453 city: { type: 'string' }454 }455 }456 },457 properties: {458 test: { $ref: 'http://foo/test#' },459 address: { $ref: '#address' }460 },461 required: ['address', 'test']462 }463 464 fastify.post('/', {465 schema: {466 body: complexSchema,467 response: {468 200: complexSchema469 }470 },471 handler: (req, reply) => {472 req.body.removeThis = 'it should not be serialized'473 reply.send(req.body)474 }475 })476 477 const payload = {478 address: { city: 'New Node' },479 test: { id: Date.now() }480 }481 482 fastify.inject({483 method: 'POST',484 url: '/',485 payload486 }, (err, res) => {487 t.error(err)488 t.same(res.json(), payload)489 })490 491 fastify.inject({492 method: 'POST',493 url: '/',494 payload: { test: { id: Date.now() } }495 }, (err, res) => {496 t.error(err)497 t.equal(res.statusCode, 400)498 t.same(res.json(), {499 error: 'Bad Request',500 message: "body must have required property 'address'",501 statusCode: 400,502 code: 'FST_ERR_VALIDATION'503 })504 })505})506 507test('Shared schema should be pass to serializer and validator ($ref to shared schema /definitions)', t => {508 t.plan(5)509 const fastify = Fastify()510 511 fastify.addSchema({512 $id: 'http://example.com/asset.json',513 $schema: 'http://json-schema.org/draft-07/schema#',514 title: 'Physical Asset',515 description: 'A generic representation of a physical asset',516 type: 'object',517 required: [518 'id',519 'model',520 'location'521 ],522 properties: {523 id: {524 type: 'string',525 format: 'uuid'526 },527 model: {528 type: 'string'529 },530 location: { $ref: 'http://example.com/point.json#' }531 },532 definitions: {533 inner: {534 $id: '#innerId',535 type: 'string',536 format: 'email'537 }538 }539 })540 541 fastify.addSchema({542 $id: 'http://example.com/point.json',543 $schema: 'http://json-schema.org/draft-07/schema#',544 title: 'Longitude and Latitude Values',545 description: 'A geographical coordinate.',546 type: 'object',547 required: [548 'latitude',549 'longitude'550 ],551 properties: {552 email: { $ref: 'http://example.com/asset.json#/definitions/inner' },553 latitude: {554 type: 'number',555 minimum: -90,556 maximum: 90557 },558 longitude: {559 type: 'number',560 minimum: -180,561 maximum: 180562 },563 altitude: {564 type: 'number'565 }566 }567 })568 569 const schemaLocations = {570 $id: 'http://example.com/locations.json',571 $schema: 'http://json-schema.org/draft-07/schema#',572 title: 'List of Asset locations',573 type: 'array',574 items: { $ref: 'http://example.com/asset.json#' }575 }576 577 fastify.post('/', {578 schema: {579 body: schemaLocations,580 response: { 200: schemaLocations }581 }582 }, (req, reply) => {583 reply.send(locations.map(_ => Object.assign({ serializer: 'remove me' }, _)))584 })585 586 const locations = [587 { id: '550e8400-e29b-41d4-a716-446655440000', model: 'mod', location: { latitude: 10, longitude: 10, email: 'foo@bar.it' } },588 { id: '550e8400-e29b-41d4-a716-446655440000', model: 'mod', location: { latitude: 10, longitude: 10, email: 'foo@bar.it' } }589 ]590 fastify.inject({591 method: 'POST',592 url: '/',593 payload: locations594 }, (err, res) => {595 t.error(err)596 t.same(res.json(), locations)597 598 fastify.inject({599 method: 'POST',600 url: '/',601 payload: locations.map(_ => {602 _.location.email = 'not an email'603 return _604 })605 }, (err, res) => {606 t.error(err)607 t.equal(res.statusCode, 400)608 t.same(res.json(), {609 error: 'Bad Request',610 message: 'body/0/location/email must match format "email"',611 statusCode: 400,612 code: 'FST_ERR_VALIDATION'613 })614 })615 })616})617 618test('Custom setSerializerCompiler', t => {619 t.plan(7)620 const fastify = Fastify({ exposeHeadRoutes: false })621 622 const outSchema = {623 $id: 'test',624 type: 'object',625 whatever: 'need to be parsed by the custom serializer'626 }627 628 fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {629 t.equal(method, 'GET')630 t.equal(url, '/foo/:id')631 t.equal(httpStatus, '200')632 t.same(schema, outSchema)633 return data => JSON.stringify(data)634 })635 636 fastify.register((instance, opts, done) => {637 instance.get('/:id', {638 handler (req, reply) {639 reply.send({ id: 1 })640 },641 schema: {642 response: {643 200: outSchema644 }645 }646 })647 t.ok(instance.serializerCompiler, 'the serializer is set by the parent')648 done()649 }, { prefix: '/foo' })650 651 fastify.inject({652 method: 'GET',653 url: '/foo/123'654 }, (err, res) => {655 t.error(err)656 t.equal(res.payload, JSON.stringify({ id: 1 }))657 })658})659 660test('Custom setSerializerCompiler returns bad serialized output', t => {661 t.plan(4)662 const fastify = Fastify()663 664 const outSchema = {665 $id: 'test',666 type: 'object',667 whatever: 'need to be parsed by the custom serializer'668 }669 670 fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {671 return data => {672 t.pass('returning an invalid serialization')673 return { not: 'a string' }674 }675 })676 677 fastify.get('/:id', {678 handler (req, reply) { throw new Error('ops') },679 schema: {680 response: {681 500: outSchema682 }683 }684 })685 686 fastify.inject({687 method: 'GET',688 url: '/123'689 }, (err, res) => {690 t.error(err)691 t.equal(res.statusCode, 500)692 t.strictSame(res.json(), {693 code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',694 message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',695 statusCode: 500696 })697 })698})699 700test('Custom setSerializerCompiler with addSchema', t => {701 t.plan(6)702 const fastify = Fastify({ exposeHeadRoutes: false })703 704 const outSchema = {705 $id: 'test',706 type: 'object',707 whatever: 'need to be parsed by the custom serializer'708 }709 710 fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {711 t.equal(method, 'GET')712 t.equal(url, '/foo/:id')713 t.equal(httpStatus, '200')714 t.same(schema, outSchema)715 return _data => JSON.stringify({ id: 2 })716 })717 718 // provoke re-creation of serialization compiler in setupSerializer719 fastify.addSchema({ $id: 'dummy', type: 'object' })720 721 fastify.get('/foo/:id', {722 handler (_req, reply) {723 reply.send({ id: 1 })724 },725 schema: {726 response: {727 200: outSchema728 }729 }730 })731 732 fastify.inject({733 method: 'GET',734 url: '/foo/123'735 }, (err, res) => {736 t.error(err)737 t.equal(res.payload, JSON.stringify({ id: 2 }))738 })739})740 741test('Custom serializer per route', async t => {742 const fastify = Fastify()743 744 const outSchema = {745 $id: 'test',746 type: 'object',747 properties: {748 mean: { type: 'string' }749 }750 }751 752 fastify.get('/default', {753 handler (req, reply) { reply.send({ mean: 'default' }) },754 schema: { response: { 200: outSchema } }755 })756 757 let hit = 0758 fastify.register((instance, opts, done) => {759 instance.setSerializerCompiler(({ schema, method, url, httpStatus }) => {760 hit++761 return data => JSON.stringify({ mean: 'custom' })762 })763 instance.get('/custom', {764 handler (req, reply) { reply.send({}) },765 schema: { response: { 200: outSchema } }766 })767 instance.get('/route', {768 handler (req, reply) { reply.send({}) },769 serializerCompiler: ({ schema, method, url, httpPart }) => {770 hit++771 return data => JSON.stringify({ mean: 'route' })772 },773 schema: { response: { 200: outSchema } }774 })775 776 done()777 })778 779 let res = await fastify.inject('/default')780 t.equal(res.json().mean, 'default')781 782 res = await fastify.inject('/custom')783 t.equal(res.json().mean, 'custom')784 785 res = await fastify.inject('/route')786 t.equal(res.json().mean, 'route')787 788 t.equal(hit, 4, 'the custom and route serializer has been called')789})790 791test('Reply serializer win over serializer ', t => {792 t.plan(6)793 794 const fastify = Fastify()795 fastify.setReplySerializer(function (payload, statusCode) {796 t.same(payload, { name: 'Foo', work: 'Bar', nick: 'Boo' })797 return 'instance serializator'798 })799 800 fastify.get('/', {801 schema: {802 response: {803 '2xx': {804 type: 'object',805 properties: {806 name: { type: 'string' },807 work: { type: 'string' }808 }809 }810 }811 },812 serializerCompiler: ({ schema, method, url, httpPart }) => {813 t.ok(method, 'the custom compiler has been created')814 return () => {815 t.fail('the serializer must not be called when there is a reply serializer')816 return 'fail'817 }818 }819 }, function (req, reply) {820 reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })821 })822 823 fastify.inject('/', (err, res) => {824 t.error(err)825 t.same(res.payload, 'instance serializator')826 t.equal(res.statusCode, 200)827 })828})829 830test('Reply serializer win over serializer ', t => {831 t.plan(6)832 833 const fastify = Fastify()834 fastify.setReplySerializer(function (payload, statusCode) {835 t.same(payload, { name: 'Foo', work: 'Bar', nick: 'Boo' })836 return 'instance serializator'837 })838 839 fastify.get('/', {840 schema: {841 response: {842 '2xx': {843 type: 'object',844 properties: {845 name: { type: 'string' },846 work: { type: 'string' }847 }848 }849 }850 },851 serializerCompiler: ({ schema, method, url, httpPart }) => {852 t.ok(method, 'the custom compiler has been created')853 return () => {854 t.fail('the serializer must not be called when there is a reply serializer')855 return 'fail'856 }857 }858 }, function (req, reply) {859 reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })860 })861 862 fastify.inject('/', (err, res) => {863 t.error(err)864 t.same(res.payload, 'instance serializator')865 t.equal(res.statusCode, 200)866 })867})868 869test('The schema compiler recreate itself if needed', t => {870 t.plan(1)871 const fastify = Fastify()872 873 fastify.options('/', {874 schema: {875 response: { '2xx': { hello: { type: 'string' } } }876 }877 }, echoBody)878 879 fastify.register(function (fastify, options, done) {880 fastify.addSchema({881 $id: 'identifier',882 type: 'string',883 format: 'uuid'884 })885 886 fastify.get('/', {887 schema: {888 response: {889 '2xx': {890 foobarId: { $ref: 'identifier#' }891 }892 }893 }894 }, echoBody)895 896 done()897 })898 899 fastify.ready(err => { t.error(err) })900})901 902test('The schema changes the default error handler output', async t => {903 t.plan(4)904 const fastify = Fastify()905 906 fastify.get('/:code', {907 schema: {908 response: {909 '2xx': { hello: { type: 'string' } },910 501: {911 type: 'object',912 properties: {913 message: { type: 'string' }914 }915 },916 '5xx': {917 type: 'object',918 properties: {919 customId: { type: 'number' },920 error: { type: 'string' },921 message: { type: 'string' }922 }923 }924 }925 }926 }, (request, reply) => {927 if (request.params.code === '501') {928 return reply.code(501).send(new Error('501 message'))929 }930 const error = new Error('500 message')931 error.customId = 42932 reply.send(error)933 })934 935 let res = await fastify.inject('/501')936 t.equal(res.statusCode, 501)937 t.same(res.json(), { message: '501 message' })938 939 res = await fastify.inject('/500')940 t.equal(res.statusCode, 500)941 t.same(res.json(), { error: 'Internal Server Error', message: '500 message', customId: 42 })942})943 944test('do not crash if status code serializer errors', async t => {945 const fastify = Fastify()946 947 const requiresFoo = {948 type: 'object',949 properties: { foo: { type: 'string' } },950 required: ['foo']951 }952 953 const someUserErrorType2 = {954 type: 'object',955 properties: {956 customCode: { type: 'number' }957 },958 required: ['customCode']959 }960 961 fastify.get(962 '/',963 {964 schema: {965 query: requiresFoo,966 response: { 400: someUserErrorType2 }967 }968 },969 (request, reply) => {970 t.fail('handler, should not be called')971 }972 )973 974 const res = await fastify.inject({975 path: '/',976 query: {977 notfoo: true978 }979 })980 t.equal(res.statusCode, 500)981 t.same(res.json(), {982 statusCode: 500,983 code: 'FST_ERR_FAILED_ERROR_SERIALIZATION',984 message: 'Failed to serialize an error. Error: "customCode" is required!. ' +985 'Original error: querystring must have required property \'foo\''986 })987})988 989test('custom schema serializer error, empty message', async t => {990 t.plan(2)991 const fastify = Fastify()992 993 fastify.get('/:code', {994 schema: {995 response: {996 '2xx': { hello: { type: 'string' } },997 501: {998 type: 'object',999 properties: {1000 message: { type: 'string' }1001 }1002 }1003 }1004 }1005 }, (request, reply) => {1006 if (request.params.code === '501') {1007 return reply.code(501).send(new Error(''))1008 }1009 })1010 1011 const res = await fastify.inject('/501')1012 t.equal(res.statusCode, 501)1013 t.same(res.json(), { message: '' })1014})1015 1016test('error in custom schema serialize compiler, throw FST_ERR_SCH_SERIALIZATION_BUILD error', t => {1017 t.plan(3)1018 1019 const fastify = Fastify()1020 1021 fastify.get('/', {1022 schema: {1023 response: {1024 '2xx': {1025 type: 'object',1026 properties: {1027 some: { type: 'string' }1028 }1029 },1030 500: {1031 type: 'object',1032 properties: {1033 message: { type: 'string' }1034 }1035 }1036 }1037 },1038 serializerCompiler: () => {1039 throw new Error('CUSTOM_ERROR')1040 }1041 }, function (req, reply) {1042 reply.code(200).send({ some: 'thing' })1043 })1044 1045 fastify.ready((err) => {1046 t.equal(err.message, 'Failed building the serialization schema for GET: /, due to error CUSTOM_ERROR')1047 t.equal(err.statusCode, 500)1048 t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')1049 })1050})1051 1052test('Errors in serializer send to errorHandler', async t => {1053 let savedError1054 1055 const fastify = Fastify()1056 fastify.get('/', {1057 schema: {1058 response: {1059 200: {1060 type: 'object',1061 properties: {1062 name: { type: 'string' },1063 power: { type: 'string' }1064 },1065 required: ['name']1066 }1067 }1068 }1069 1070 }, function (req, reply) {1071 reply.code(200).send({ no: 'thing' })1072 })1073 fastify.setErrorHandler((error, request, reply) => {1074 savedError = error1075 reply.code(500).send(error)1076 })1077 1078 const res = await fastify.inject('/')1079 1080 t.equal(res.statusCode, 500)1081 1082 // t.same(savedError, new Error('"name" is required!'));1083 t.same(res.json(), {1084 statusCode: 500,1085 error: 'Internal Server Error',1086 message: '"name" is required!'1087 })1088 t.ok(savedError, 'error presents')1089 t.ok(savedError.serialization, 'Serialization sign presents')1090 t.end()1091})1092 1093test('capital X', t => {1094 t.plan(3)1095 1096 const fastify = Fastify()1097 fastify.get('/', {1098 schema: {1099 response: {1100 '2XX': {1101 type: 'object',1102 properties: {1103 name: { type: 'string' },1104 work: { type: 'string' }1105 }1106 }1107 }1108 }1109 }, function (req, reply) {1110 reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })1111 })1112 1113 fastify.inject('/', (err, res) => {1114 t.error(err)1115 t.same(res.json(), { name: 'Foo', work: 'Bar' })1116 t.equal(res.statusCode, 200)1117 })1118})1119 1120test('allow default as status code and used as last fallback', t => {1121 t.plan(3)1122 const fastify = Fastify()1123 1124 fastify.route({1125 url: '/',1126 method: 'GET',1127 schema: {1128 response: {1129 default: {1130 type: 'object',1131 properties: {1132 name: { type: 'string' },1133 work: { type: 'string' }1134 }1135 }1136 }1137 },1138 handler: (req, reply) => {1139 reply.code(200).send({ name: 'Foo', work: 'Bar', nick: 'Boo' })1140 }1141 })1142 1143 fastify.inject('/', (err, res) => {1144 t.error(err)1145 t.same(res.json(), { name: 'Foo', work: 'Bar' })1146 t.equal(res.statusCode, 200)1147 })1148})1149 