strong-tie/inbound-calls
0
1'use strict'2 3const { test } = require('tap')4const Fastify = require('..')5const fp = require('fastify-plugin')6const deepClone = require('rfdc')({ circles: true, proto: false })7const Ajv = require('ajv')8const { kSchemaController } = require('../lib/symbols.js')9const { FSTWRN001 } = require('../lib/warnings')10 11const echoParams = (req, reply) => { reply.send(req.params) }12const echoBody = (req, reply) => { reply.send(req.body) }13 14;['addSchema', 'getSchema', 'getSchemas', 'setValidatorCompiler', 'setSerializerCompiler'].forEach(f => {15 test(`Should expose ${f} function`, t => {16 t.plan(1)17 const fastify = Fastify()18 t.equal(typeof fastify[f], 'function')19 })20})21 22;['setValidatorCompiler', 'setSerializerCompiler'].forEach(f => {23 test(`cannot call ${f} after binding`, t => {24 t.plan(2)25 const fastify = Fastify()26 t.teardown(fastify.close.bind(fastify))27 fastify.listen({ port: 0 }, err => {28 t.error(err)29 try {30 fastify[f](() => { })31 t.fail()32 } catch (e) {33 t.pass()34 }35 })36 })37})38 39test('The schemas should be added to an internal storage', t => {40 t.plan(1)41 const fastify = Fastify()42 const schema = { $id: 'id', my: 'schema' }43 fastify.addSchema(schema)44 t.same(fastify[kSchemaController].schemaBucket.store, { id: schema })45})46 47test('The schemas should be accessible via getSchemas', t => {48 t.plan(1)49 const fastify = Fastify()50 51 const schemas = {52 id: { $id: 'id', my: 'schema' },53 abc: { $id: 'abc', my: 'schema' },54 bcd: { $id: 'bcd', my: 'schema', properties: { a: 'a', b: 1 } }55 }56 57 Object.values(schemas).forEach(schema => { fastify.addSchema(schema) })58 t.same(fastify.getSchemas(), schemas)59})60 61test('The schema should be accessible by id via getSchema', t => {62 t.plan(5)63 const fastify = Fastify()64 65 const schemas = [66 { $id: 'id', my: 'schema' },67 { $id: 'abc', my: 'schema' },68 { $id: 'bcd', my: 'schema', properties: { a: 'a', b: 1 } }69 ]70 schemas.forEach(schema => { fastify.addSchema(schema) })71 t.same(fastify.getSchema('abc'), schemas[1])72 t.same(fastify.getSchema('id'), schemas[0])73 t.same(fastify.getSchema('foo'), undefined)74 75 fastify.register((instance, opts, done) => {76 const pluginSchema = { $id: 'cde', my: 'schema' }77 instance.addSchema(pluginSchema)78 t.same(instance.getSchema('cde'), pluginSchema)79 done()80 })81 82 fastify.ready(err => t.error(err))83})84 85test('Get validatorCompiler after setValidatorCompiler', t => {86 t.plan(2)87 const myCompiler = () => { }88 const fastify = Fastify()89 fastify.setValidatorCompiler(myCompiler)90 const sc = fastify.validatorCompiler91 t.ok(Object.is(myCompiler, sc))92 fastify.ready(err => t.error(err))93})94 95test('Get serializerCompiler after setSerializerCompiler', t => {96 t.plan(2)97 const myCompiler = () => { }98 const fastify = Fastify()99 fastify.setSerializerCompiler(myCompiler)100 const sc = fastify.serializerCompiler101 t.ok(Object.is(myCompiler, sc))102 fastify.ready(err => t.error(err))103})104 105test('Get compilers is empty when settle on routes', t => {106 t.plan(3)107 108 const fastify = Fastify()109 110 fastify.post('/', {111 schema: {112 body: { type: 'object', properties: { hello: { type: 'string' } } },113 response: {114 '2xx': {115 type: 'object',116 properties: {117 foo: { type: 'array', items: { type: 'string' } }118 }119 }120 }121 },122 validatorCompiler: ({ schema, method, url, httpPart }) => {},123 serializerCompiler: ({ schema, method, url, httpPart }) => {}124 }, function (req, reply) {125 reply.send('ok')126 })127 128 fastify.inject({129 method: 'POST',130 payload: {},131 url: '/'132 }, (err, res) => {133 t.error(err)134 t.equal(fastify.validatorCompiler, undefined)135 t.equal(fastify.serializerCompiler, undefined)136 })137})138 139test('Should throw if the $id property is missing', t => {140 t.plan(1)141 const fastify = Fastify()142 try {143 fastify.addSchema({ type: 'string' })144 t.fail()145 } catch (err) {146 t.equal(err.code, 'FST_ERR_SCH_MISSING_ID')147 }148})149 150test('Cannot add multiple times the same id', t => {151 t.plan(2)152 const fastify = Fastify()153 154 fastify.addSchema({ $id: 'id' })155 try {156 fastify.addSchema({ $id: 'id' })157 } catch (err) {158 t.equal(err.code, 'FST_ERR_SCH_ALREADY_PRESENT')159 t.equal(err.message, 'Schema with id \'id\' already declared!')160 }161})162 163test('Cannot add schema for query and querystring', t => {164 t.plan(2)165 const fastify = Fastify()166 167 fastify.get('/', {168 handler: () => {},169 schema: {170 query: {171 type: 'object',172 properties: {173 foo: { type: 'string' }174 }175 },176 querystring: {177 type: 'object',178 properties: {179 foo: { type: 'string' }180 }181 }182 }183 })184 185 fastify.ready(err => {186 t.equal(err.code, 'FST_ERR_SCH_DUPLICATE')187 t.equal(err.message, 'Schema with \'querystring\' already present!')188 })189})190 191test('Should throw of the schema does not exists in input', t => {192 t.plan(2)193 const fastify = Fastify()194 195 fastify.get('/:id', {196 handler: echoParams,197 schema: {198 params: {199 type: 'object',200 properties: {201 name: { $ref: '#notExist' }202 }203 }204 }205 })206 207 fastify.ready(err => {208 t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')209 t.equal(err.message, "Failed building the validation schema for GET: /:id, due to error can't resolve reference #notExist from id #")210 })211})212 213test('Should throw if schema is missing for content type', t => {214 t.plan(2)215 216 const fastify = Fastify()217 fastify.post('/', {218 handler: echoBody,219 schema: {220 body: {221 content: {222 'application/json': {}223 }224 }225 }226 })227 228 fastify.ready(err => {229 t.equal(err.code, 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA')230 t.equal(err.message, "Schema is missing for the content type 'application/json'")231 })232})233 234test('Should throw of the schema does not exists in output', t => {235 t.plan(2)236 const fastify = Fastify()237 238 fastify.get('/:id', {239 handler: echoParams,240 schema: {241 response: {242 '2xx': {243 type: 'object',244 properties: {245 name: { $ref: '#notExist' }246 }247 }248 }249 }250 })251 252 fastify.ready(err => {253 t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')254 t.match(err.message, /^Failed building the serialization schema for GET: \/:id, due to error Cannot find reference.*/) // error from fast-json-stringify255 })256})257 258test('Should not change the input schemas', t => {259 t.plan(4)260 261 const theSchema = {262 $id: 'helloSchema',263 type: 'object',264 definitions: {265 hello: { type: 'string' }266 }267 }268 269 const fastify = Fastify()270 fastify.post('/', {271 handler: echoBody,272 schema: {273 body: {274 type: 'object',275 additionalProperties: false,276 properties: {277 name: { $ref: 'helloSchema#/definitions/hello' }278 }279 },280 response: {281 '2xx': {282 type: 'object',283 properties: {284 name: { $ref: 'helloSchema#/definitions/hello' }285 }286 }287 }288 }289 })290 fastify.addSchema(theSchema)291 292 fastify.inject({293 url: '/',294 method: 'POST',295 payload: { name: 'Foo', surname: 'Bar' }296 }, (err, res) => {297 t.error(err)298 t.same(res.json(), { name: 'Foo' })299 t.ok(theSchema.$id, 'the $id is not removed')300 t.same(fastify.getSchema('helloSchema'), theSchema)301 })302})303 304test('Should emit warning if the schema headers is undefined', t => {305 t.plan(4)306 const fastify = Fastify()307 308 process.on('warning', onWarning)309 function onWarning (warning) {310 t.equal(warning.name, 'FastifyWarning')311 t.equal(warning.code, FSTWRN001.code)312 }313 314 t.teardown(() => {315 process.removeListener('warning', onWarning)316 FSTWRN001.emitted = false317 })318 319 fastify.post('/:id', {320 handler: echoParams,321 schema: {322 headers: undefined323 }324 })325 326 fastify.inject({327 method: 'POST',328 url: '/123'329 }, (error, res) => {330 t.error(error)331 t.equal(res.statusCode, 200)332 })333})334 335test('Should emit warning if the schema body is undefined', t => {336 t.plan(4)337 const fastify = Fastify()338 339 process.on('warning', onWarning)340 function onWarning (warning) {341 t.equal(warning.name, 'FastifyWarning')342 t.equal(warning.code, FSTWRN001.code)343 }344 345 t.teardown(() => {346 process.removeListener('warning', onWarning)347 FSTWRN001.emitted = false348 })349 350 fastify.post('/:id', {351 handler: echoParams,352 schema: {353 body: undefined354 }355 })356 357 fastify.inject({358 method: 'POST',359 url: '/123'360 }, (error, res) => {361 t.error(error)362 t.equal(res.statusCode, 200)363 })364})365 366test('Should emit warning if the schema query is undefined', t => {367 t.plan(4)368 const fastify = Fastify()369 370 process.on('warning', onWarning)371 function onWarning (warning) {372 t.equal(warning.name, 'FastifyWarning')373 t.equal(warning.code, FSTWRN001.code)374 }375 376 t.teardown(() => {377 process.removeListener('warning', onWarning)378 FSTWRN001.emitted = false379 })380 381 fastify.post('/:id', {382 handler: echoParams,383 schema: {384 querystring: undefined385 }386 })387 388 fastify.inject({389 method: 'POST',390 url: '/123'391 }, (error, res) => {392 t.error(error)393 t.equal(res.statusCode, 200)394 })395})396 397test('Should emit warning if the schema params is undefined', t => {398 t.plan(4)399 const fastify = Fastify()400 401 process.on('warning', onWarning)402 function onWarning (warning) {403 t.equal(warning.name, 'FastifyWarning')404 t.equal(warning.code, FSTWRN001.code)405 }406 407 t.teardown(() => {408 process.removeListener('warning', onWarning)409 FSTWRN001.emitted = false410 })411 412 fastify.post('/:id', {413 handler: echoParams,414 schema: {415 params: undefined416 }417 })418 419 fastify.inject({420 method: 'POST',421 url: '/123'422 }, (error, res) => {423 t.error(error)424 t.equal(res.statusCode, 200)425 })426})427 428test('Should emit a warning for every route with undefined schema', t => {429 t.plan(16)430 const fastify = Fastify()431 432 let runs = 0433 const expectedWarningEmitted = [0, 1, 2, 3]434 // It emits 4 warnings:435 // - 2 - GET and HEAD for /undefinedParams/:id436 // - 2 - GET and HEAD for /undefinedBody/:id437 // => 3 x 4 assertions = 12 assertions438 function onWarning (warning) {439 t.equal(warning.name, 'FastifyWarning')440 t.equal(warning.code, FSTWRN001.code)441 t.equal(runs++, expectedWarningEmitted.shift())442 }443 444 process.on('warning', onWarning)445 t.teardown(() => {446 process.removeListener('warning', onWarning)447 FSTWRN001.emitted = false448 })449 450 fastify.get('/undefinedParams/:id', {451 handler: echoParams,452 schema: {453 params: undefined454 }455 })456 457 fastify.get('/undefinedBody/:id', {458 handler: echoParams,459 schema: {460 body: undefined461 }462 })463 464 fastify.inject({465 method: 'GET',466 url: '/undefinedParams/123'467 }, (error, res) => {468 t.error(error)469 t.equal(res.statusCode, 200)470 })471 472 fastify.inject({473 method: 'GET',474 url: '/undefinedBody/123'475 }, (error, res) => {476 t.error(error)477 t.equal(res.statusCode, 200)478 })479})480 481test('First level $ref', t => {482 t.plan(2)483 const fastify = Fastify()484 485 fastify.addSchema({486 $id: 'test',487 type: 'object',488 properties: {489 id: { type: 'number' }490 }491 })492 493 fastify.get('/:id', {494 handler: (req, reply) => {495 reply.send({ id: req.params.id * 2, ignore: 'it' })496 },497 schema: {498 params: { $ref: 'test#' },499 response: {500 200: { $ref: 'test#' }501 }502 }503 })504 505 fastify.inject({506 method: 'GET',507 url: '/123'508 }, (err, res) => {509 t.error(err)510 t.same(res.json(), { id: 246 })511 })512})513 514test('Customize validator compiler in instance and route', t => {515 t.plan(28)516 const fastify = Fastify({ exposeHeadRoutes: false })517 518 fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {519 t.equal(method, 'POST') // run 4 times520 t.equal(url, '/:id') // run 4 times521 switch (httpPart) {522 case 'body':523 t.pass('body evaluated')524 return body => {525 t.same(body, { foo: ['bar', 'BAR'] })526 return true527 }528 case 'params':529 t.pass('params evaluated')530 return params => {531 t.same(params, { id: 1234 })532 return true533 }534 case 'querystring':535 t.pass('querystring evaluated')536 return query => {537 t.same(query, { lang: 'en' })538 return true539 }540 case 'headers':541 t.pass('headers evaluated')542 return headers => {543 t.match(headers, { x: 'hello' })544 return true545 }546 case '2xx':547 t.fail('the validator doesn\'t process the response')548 break549 default:550 t.fail(`unknown httpPart ${httpPart}`)551 }552 })553 554 fastify.post('/:id', {555 handler: echoBody,556 schema: {557 query: {558 type: 'object',559 properties: {560 lang: { type: 'string', enum: ['it', 'en'] }561 }562 },563 headers: {564 type: 'object',565 properties: {566 x: { type: 'string' }567 }568 },569 params: {570 type: 'object',571 properties: {572 id: { type: 'number' }573 }574 },575 body: {576 type: 'object',577 properties: {578 foo: { type: 'array' }579 }580 },581 response: {582 '2xx': {583 type: 'object',584 properties: {585 foo: { type: 'array', items: { type: 'string' } }586 }587 }588 }589 }590 })591 592 fastify.get('/wow/:id', {593 handler: echoParams,594 validatorCompiler: ({ schema, method, url, httpPart }) => {595 t.equal(method, 'GET') // run 3 times (params, headers, query)596 t.equal(url, '/wow/:id') // run 4 times597 return () => { return true } // ignore the validation598 },599 schema: {600 query: {601 type: 'object',602 properties: {603 lang: { type: 'string', enum: ['it', 'en'] }604 }605 },606 headers: {607 type: 'object',608 properties: {609 x: { type: 'string' }610 }611 },612 params: {613 type: 'object',614 properties: {615 id: { type: 'number' }616 }617 },618 response: {619 '2xx': {620 type: 'object',621 properties: {622 foo: { type: 'array', items: { type: 'string' } }623 }624 }625 }626 }627 })628 629 fastify.inject({630 url: '/1234',631 method: 'POST',632 headers: { x: 'hello' },633 query: { lang: 'en' },634 payload: { foo: ['bar', 'BAR'] }635 }, (err, res) => {636 t.error(err)637 t.equal(res.statusCode, 200)638 t.same(res.json(), { foo: ['bar', 'BAR'] })639 })640 641 fastify.inject({642 url: '/wow/should-be-a-num',643 method: 'GET',644 headers: { x: 'hello' },645 query: { lang: 'jp' } // not in the enum646 }, (err, res) => {647 t.error(err)648 t.equal(res.statusCode, 200) // the validation is always true649 t.same(res.json(), {})650 })651})652 653test('Use the same schema across multiple routes', t => {654 t.plan(4)655 const fastify = Fastify()656 657 fastify.addSchema({658 $id: 'test',659 type: 'object',660 properties: {661 id: { type: 'number' }662 }663 })664 665 fastify.get('/first/:id', {666 schema: {667 params: {668 type: 'object',669 properties: {670 id: { $ref: 'test#/properties/id' }671 }672 }673 },674 handler: (req, reply) => {675 reply.send(typeof req.params.id)676 }677 })678 679 fastify.get('/second/:id', {680 schema: {681 params: {682 type: 'object',683 properties: {684 id: { $ref: 'test#/properties/id' }685 }686 }687 },688 handler: (req, reply) => {689 reply.send(typeof req.params.id)690 }691 })692 693 fastify.inject({694 method: 'GET',695 url: '/first/123'696 }, (err, res) => {697 t.error(err)698 t.equal(res.payload, 'number')699 })700 701 fastify.inject({702 method: 'GET',703 url: '/second/123'704 }, (err, res) => {705 t.error(err)706 t.equal(res.payload, 'number')707 })708})709 710test('Encapsulation should intervene', t => {711 t.plan(2)712 const fastify = Fastify()713 714 fastify.register((instance, opts, done) => {715 instance.addSchema({716 $id: 'encapsulation',717 type: 'object',718 properties: {719 id: { type: 'number' }720 }721 })722 done()723 })724 725 fastify.register((instance, opts, done) => {726 instance.get('/:id', {727 handler: echoParams,728 schema: {729 params: {730 type: 'object',731 properties: {732 id: { $ref: 'encapsulation#/properties/id' }733 }734 }735 }736 })737 done()738 })739 740 fastify.ready(err => {741 t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')742 t.equal(err.message, "Failed building the validation schema for GET: /:id, due to error can't resolve reference encapsulation#/properties/id from id #")743 })744})745 746test('Encapsulation isolation', t => {747 t.plan(1)748 const fastify = Fastify()749 750 fastify.register((instance, opts, done) => {751 instance.addSchema({ $id: 'id' })752 done()753 })754 755 fastify.register((instance, opts, done) => {756 instance.addSchema({ $id: 'id' })757 done()758 })759 760 fastify.ready(err => t.error(err))761})762 763test('Add schema after register', t => {764 t.plan(5)765 766 const fastify = Fastify()767 fastify.register((instance, opts, done) => {768 instance.get('/:id', {769 handler: echoParams,770 schema: {771 params: { $ref: 'test#' }772 }773 })774 775 // add it to the parent instance776 fastify.addSchema({777 $id: 'test',778 type: 'object',779 properties: {780 id: { type: 'number' }781 }782 })783 784 try {785 instance.addSchema({ $id: 'test' })786 } catch (err) {787 t.equal(err.code, 'FST_ERR_SCH_ALREADY_PRESENT')788 t.equal(err.message, 'Schema with id \'test\' already declared!')789 }790 done()791 })792 793 fastify.inject({794 method: 'GET',795 url: '/4242'796 }, (err, res) => {797 t.error(err)798 t.equal(res.statusCode, 200)799 t.same(res.json(), { id: 4242 })800 })801})802 803test('Encapsulation isolation for getSchemas', t => {804 t.plan(5)805 const fastify = Fastify()806 807 let pluginDeepOneSide808 let pluginDeepOne809 let pluginDeepTwo810 811 const schemas = {812 z: { $id: 'z', my: 'schema' },813 a: { $id: 'a', my: 'schema' },814 b: { $id: 'b', my: 'schema' },815 c: { $id: 'c', my: 'schema', properties: { a: 'a', b: 1 } }816 }817 818 fastify.addSchema(schemas.z)819 820 fastify.register((instance, opts, done) => {821 instance.addSchema(schemas.a)822 pluginDeepOneSide = instance823 done()824 })825 826 fastify.register((instance, opts, done) => {827 instance.addSchema(schemas.b)828 instance.register((subinstance, opts, done) => {829 subinstance.addSchema(schemas.c)830 pluginDeepTwo = subinstance831 done()832 })833 pluginDeepOne = instance834 done()835 })836 837 fastify.ready(err => {838 t.error(err)839 t.same(fastify.getSchemas(), { z: schemas.z })840 t.same(pluginDeepOneSide.getSchemas(), { z: schemas.z, a: schemas.a })841 t.same(pluginDeepOne.getSchemas(), { z: schemas.z, b: schemas.b })842 t.same(pluginDeepTwo.getSchemas(), { z: schemas.z, b: schemas.b, c: schemas.c })843 })844})845 846test('Use the same schema id in different places', t => {847 t.plan(1)848 const fastify = Fastify()849 850 fastify.addSchema({851 $id: 'test',852 type: 'object',853 properties: {854 id: { type: 'number' }855 }856 })857 858 fastify.get('/:id', {859 handler: echoParams,860 schema: {861 response: {862 200: {863 type: 'array',864 items: { $ref: 'test#/properties/id' }865 }866 }867 }868 })869 870 fastify.post('/:id', {871 handler: echoBody,872 schema: {873 body: {874 type: 'object',875 properties: {876 id: { $ref: 'test#/properties/id' }877 }878 },879 response: {880 200: {881 type: 'object',882 properties: {883 id: { $ref: 'test#/properties/id' }884 }885 }886 }887 }888 })889 890 fastify.ready(err => t.error(err))891})892 893test('Get schema anyway should not add `properties` if allOf is present', t => {894 t.plan(1)895 const fastify = Fastify()896 897 fastify.addSchema({898 $id: 'first',899 type: 'object',900 properties: {901 first: { type: 'number' }902 }903 })904 905 fastify.addSchema({906 $id: 'second',907 type: 'object',908 allOf: [909 {910 type: 'object',911 properties: {912 second: { type: 'number' }913 }914 },915 fastify.getSchema('first')916 ]917 })918 919 fastify.get('/', {920 handler: () => {},921 schema: {922 querystring: fastify.getSchema('second'),923 response: { 200: fastify.getSchema('second') }924 }925 })926 927 fastify.ready(err => t.error(err))928})929 930test('Get schema anyway should not add `properties` if oneOf is present', t => {931 t.plan(1)932 const fastify = Fastify()933 934 fastify.addSchema({935 $id: 'first',936 type: 'object',937 properties: {938 first: { type: 'number' }939 }940 })941 942 fastify.addSchema({943 $id: 'second',944 type: 'object',945 oneOf: [946 {947 type: 'object',948 properties: {949 second: { type: 'number' }950 }951 },952 fastify.getSchema('first')953 ]954 })955 956 fastify.get('/', {957 handler: () => {},958 schema: {959 querystring: fastify.getSchema('second'),960 response: { 200: fastify.getSchema('second') }961 }962 })963 964 fastify.ready(err => t.error(err))965})966 967test('Get schema anyway should not add `properties` if anyOf is present', t => {968 t.plan(1)969 const fastify = Fastify()970 971 fastify.addSchema({972 $id: 'first',973 type: 'object',974 properties: {975 first: { type: 'number' }976 }977 })978 979 fastify.addSchema({980 $id: 'second',981 type: 'object',982 anyOf: [983 {984 type: 'object',985 properties: {986 second: { type: 'number' }987 }988 },989 fastify.getSchema('first')990 ]991 })992 993 fastify.get('/', {994 handler: () => {},995 schema: {996 querystring: fastify.getSchema('second'),997 response: { 200: fastify.getSchema('second') }998 }999 })1000 1001 fastify.ready(err => t.error(err))1002})1003 1004test('Shared schema should be ignored in string enum', t => {1005 t.plan(2)1006 const fastify = Fastify()1007 1008 fastify.get('/:lang', {1009 handler: echoParams,1010 schema: {1011 params: {1012 type: 'object',1013 properties: {1014 lang: {1015 type: 'string',1016 enum: ['Javascript', 'C++', 'C#']1017 }1018 }1019 }1020 }1021 })1022 1023 fastify.inject('/C%23', (err, res) => {1024 t.error(err)1025 t.same(res.json(), { lang: 'C#' })1026 })1027})1028 1029test('Shared schema should NOT be ignored in != string enum', t => {1030 t.plan(2)1031 const fastify = Fastify()1032 1033 fastify.addSchema({1034 $id: 'C',1035 type: 'object',1036 properties: {1037 lang: {1038 type: 'string',1039 enum: ['Javascript', 'C++', 'C#']1040 }1041 }1042 })1043 1044 fastify.post('/:lang', {1045 handler: echoBody,1046 schema: {1047 body: fastify.getSchema('C')1048 }1049 })1050 1051 fastify.inject({1052 url: '/',1053 method: 'POST',1054 payload: { lang: 'C#' }1055 }, (err, res) => {1056 t.error(err)1057 t.same(res.json(), { lang: 'C#' })1058 })1059})1060 1061test('Case insensitive header validation', t => {1062 t.plan(2)1063 const fastify = Fastify()1064 fastify.get('/', {1065 handler: (req, reply) => {1066 reply.code(200).send(req.headers.foobar)1067 },1068 schema: {1069 headers: {1070 type: 'object',1071 required: ['FooBar'],1072 properties: {1073 FooBar: { type: 'string' }1074 }1075 }1076 }1077 })1078 fastify.inject({1079 url: '/',1080 method: 'GET',1081 headers: {1082 FooBar: 'Baz'1083 }1084 }, (err, res) => {1085 t.error(err)1086 t.equal(res.payload, 'Baz')1087 })1088})1089 1090test('Not evaluate json-schema $schema keyword', t => {1091 t.plan(2)1092 const fastify = Fastify()1093 fastify.post('/', {1094 handler: echoBody,1095 schema: {1096 body: {1097 $schema: 'http://json-schema.org/draft-07/schema#',1098 type: 'object',1099 additionalProperties: false,1100 properties: {1101 hello: {1102 type: 'string'1103 }1104 }1105 }1106 }1107 })1108 fastify.inject({1109 url: '/',1110 method: 'POST',1111 body: { hello: 'world', foo: 'bar' }1112 }, (err, res) => {1113 t.error(err)1114 t.same(res.json(), { hello: 'world' })1115 })1116})1117 1118test('Validation context in validation result', t => {1119 t.plan(5)1120 const fastify = Fastify()1121 // custom error handler to expose validation context in response, so we can test it later1122 fastify.setErrorHandler((err, request, reply) => {1123 t.equal(err instanceof Error, true)1124 t.ok(err.validation, 'detailed errors')1125 t.equal(err.validationContext, 'body')1126 reply.code(400).send()1127 })1128 fastify.post('/', {1129 handler: echoParams,1130 schema: {1131 body: {1132 type: 'object',1133 required: ['hello'],1134 properties: {1135 hello: { type: 'string' }1136 }1137 }1138 }1139 })1140 fastify.inject({1141 method: 'POST',1142 url: '/',1143 payload: {} // body lacks required field, will fail validation1144 }, (err, res) => {1145 t.error(err)1146 t.equal(res.statusCode, 400)1147 })1148})1149 1150test('The schema build should not modify the input', t => {1151 t.plan(3)1152 const fastify = Fastify()1153 1154 const first = {1155 $id: 'first',1156 type: 'object',1157 properties: {1158 first: {1159 type: 'number'1160 }1161 }1162 }1163 1164 fastify.addSchema(first)1165 1166 fastify.addSchema({1167 $id: 'second',1168 type: 'object',1169 allOf: [1170 {1171 type: 'object',1172 properties: {1173 second: {1174 type: 'number'1175 }1176 }1177 },1178 { $ref: 'first#' }1179 ]1180 })1181 1182 fastify.post('/', {1183 schema: {1184 description: 'get',1185 body: { $ref: 'second#' },1186 response: {1187 200: { $ref: 'second#' }1188 }1189 },1190 handler: (request, reply) => {1191 reply.send({ hello: 'world' })1192 }1193 })1194 1195 fastify.patch('/', {1196 schema: {1197 description: 'patch',1198 body: { $ref: 'first#' },1199 response: {1200 200: { $ref: 'first#' }