strong-tie/inbound-calls
0
1'use strict'2 3const { test } = require('tap')4const Joi = require('joi')5const yup = require('yup')6const AJV = require('ajv')7const S = require('fluent-json-schema')8const Fastify = require('..')9const ajvMergePatch = require('ajv-merge-patch')10const ajvErrors = require('ajv-errors')11 12test('Ajv plugins array parameter', t => {13 t.plan(3)14 const fastify = Fastify({15 ajv: {16 customOptions: {17 allErrors: true18 },19 plugins: [20 [ajvErrors, { singleError: '@@@@' }]21 ]22 }23 })24 25 fastify.post('/', {26 schema: {27 body: {28 type: 'object',29 properties: {30 foo: {31 type: 'number',32 minimum: 2,33 maximum: 10,34 multipleOf: 2,35 errorMessage: {36 type: 'should be number',37 minimum: 'should be >= 2',38 maximum: 'should be <= 10',39 multipleOf: 'should be multipleOf 2'40 }41 }42 }43 }44 },45 handler (req, reply) { reply.send({ ok: 1 }) }46 })47 48 fastify.inject({49 method: 'POST',50 url: '/',51 payload: { foo: 99 }52 }, (err, res) => {53 t.error(err)54 t.equal(res.statusCode, 400)55 t.equal(res.json().message, 'body/foo should be <= 10@@@@should be multipleOf 2')56 })57})58 59test('Should handle root $merge keywords in header', t => {60 t.plan(5)61 const fastify = Fastify({62 ajv: {63 plugins: [64 ajvMergePatch65 ]66 }67 })68 69 fastify.route({70 method: 'GET',71 url: '/',72 schema: {73 headers: {74 $merge: {75 source: {76 type: 'object',77 properties: {78 q: { type: 'string' }79 }80 },81 with: { required: ['q'] }82 }83 }84 },85 handler (req, reply) { reply.send({ ok: 1 }) }86 })87 88 fastify.ready(err => {89 t.error(err)90 91 fastify.inject({92 method: 'GET',93 url: '/'94 }, (err, res) => {95 t.error(err)96 t.equal(res.statusCode, 400)97 })98 99 fastify.inject({100 method: 'GET',101 url: '/',102 headers: { q: 'foo' }103 }, (err, res) => {104 t.error(err)105 t.equal(res.statusCode, 200)106 })107 })108})109 110test('Should handle root $patch keywords in header', t => {111 t.plan(5)112 const fastify = Fastify({113 ajv: {114 plugins: [115 ajvMergePatch116 ]117 }118 })119 120 fastify.route({121 method: 'GET',122 url: '/',123 schema: {124 headers: {125 $patch: {126 source: {127 type: 'object',128 properties: {129 q: { type: 'string' }130 }131 },132 with: [133 {134 op: 'add',135 path: '/properties/q',136 value: { type: 'number' }137 }138 ]139 }140 }141 },142 handler (req, reply) { reply.send({ ok: 1 }) }143 })144 145 fastify.ready(err => {146 t.error(err)147 148 fastify.inject({149 method: 'GET',150 url: '/',151 headers: {152 q: 'foo'153 }154 }, (err, res) => {155 t.error(err)156 t.equal(res.statusCode, 400)157 })158 159 fastify.inject({160 method: 'GET',161 url: '/',162 headers: { q: 10 }163 }, (err, res) => {164 t.error(err)165 t.equal(res.statusCode, 200)166 })167 })168})169 170test('Should handle $merge keywords in body', t => {171 t.plan(5)172 const fastify = Fastify({173 ajv: {174 plugins: [ajvMergePatch]175 }176 })177 178 fastify.post('/', {179 schema: {180 body: {181 $merge: {182 source: {183 type: 'object',184 properties: {185 q: {186 type: 'string'187 }188 }189 },190 with: {191 required: ['q']192 }193 }194 }195 },196 handler (req, reply) { reply.send({ ok: 1 }) }197 })198 199 fastify.ready(err => {200 t.error(err)201 202 fastify.inject({203 method: 'POST',204 url: '/'205 }, (err, res) => {206 t.error(err)207 t.equal(res.statusCode, 400)208 })209 210 fastify.inject({211 method: 'POST',212 url: '/',213 payload: { q: 'foo' }214 }, (err, res) => {215 t.error(err)216 t.equal(res.statusCode, 200)217 })218 })219})220 221test('Should handle $patch keywords in body', t => {222 t.plan(5)223 const fastify = Fastify({224 ajv: {225 plugins: [ajvMergePatch]226 }227 })228 229 fastify.post('/', {230 schema: {231 body: {232 $patch: {233 source: {234 type: 'object',235 properties: {236 q: {237 type: 'string'238 }239 }240 },241 with: [242 {243 op: 'add',244 path: '/properties/q',245 value: { type: 'number' }246 }247 ]248 }249 }250 },251 handler (req, reply) { reply.send({ ok: 1 }) }252 })253 254 fastify.ready(err => {255 t.error(err)256 257 fastify.inject({258 method: 'POST',259 url: '/',260 payload: { q: 'foo' }261 }, (err, res) => {262 t.error(err)263 t.equal(res.statusCode, 400)264 })265 266 fastify.inject({267 method: 'POST',268 url: '/',269 payload: { q: 10 }270 }, (err, res) => {271 t.error(err)272 t.equal(res.statusCode, 200)273 })274 })275})276 277test("serializer read validator's schemas", t => {278 t.plan(4)279 const ajvInstance = new AJV()280 281 const baseSchema = {282 $id: 'http://example.com/schemas/base',283 definitions: {284 hello: { type: 'string' }285 },286 type: 'object',287 properties: {288 hello: { $ref: '#/definitions/hello' }289 }290 }291 292 const refSchema = {293 $id: 'http://example.com/schemas/ref',294 type: 'object',295 properties: {296 hello: { $ref: 'http://example.com/schemas/base#/definitions/hello' }297 }298 }299 300 ajvInstance.addSchema(baseSchema)301 ajvInstance.addSchema(refSchema)302 303 const fastify = Fastify({304 schemaController: {305 bucket: function factory (storeInit) {306 t.notOk(storeInit, 'is always empty because fastify.addSchema is not called')307 return {308 getSchemas () {309 return {310 [baseSchema.$id]: ajvInstance.getSchema(baseSchema.$id).schema,311 [refSchema.$id]: ajvInstance.getSchema(refSchema.$id).schema312 }313 }314 }315 }316 }317 })318 319 fastify.setValidatorCompiler(function ({ schema }) {320 return ajvInstance.compile(schema)321 })322 323 fastify.get('/', {324 schema: {325 response: {326 '2xx': ajvInstance.getSchema('http://example.com/schemas/ref').schema327 }328 },329 handler (req, res) { res.send({ hello: 'world', evict: 'this' }) }330 })331 332 fastify.inject('/', (err, res) => {333 t.error(err)334 t.equal(res.statusCode, 200)335 t.same(res.json(), { hello: 'world' })336 })337})338 339test('setSchemaController in a plugin', t => {340 t.plan(5)341 const baseSchema = {342 $id: 'urn:schema:base',343 definitions: {344 hello: { type: 'string' }345 },346 type: 'object',347 properties: {348 hello: { $ref: '#/definitions/hello' }349 }350 }351 352 const refSchema = {353 $id: 'urn:schema:ref',354 type: 'object',355 properties: {356 hello: { $ref: 'urn:schema:base#/definitions/hello' }357 }358 }359 360 const ajvInstance = new AJV()361 ajvInstance.addSchema(baseSchema)362 ajvInstance.addSchema(refSchema)363 364 const fastify = Fastify({ exposeHeadRoutes: false })365 fastify.register(schemaPlugin)366 fastify.get('/', {367 schema: {368 query: ajvInstance.getSchema('urn:schema:ref').schema,369 response: {370 '2xx': ajvInstance.getSchema('urn:schema:ref').schema371 }372 },373 handler (req, res) {374 res.send({ hello: 'world', evict: 'this' })375 }376 })377 378 fastify.inject('/', (err, res) => {379 t.error(err)380 t.equal(res.statusCode, 200)381 t.same(res.json(), { hello: 'world' })382 })383 384 async function schemaPlugin (server) {385 server.setSchemaController({386 bucket () {387 t.pass('the bucket is created')388 return {389 addSchema (source) {390 ajvInstance.addSchema(source)391 },392 getSchema (id) {393 return ajvInstance.getSchema(id).schema394 },395 getSchemas () {396 return {397 'urn:schema:base': baseSchema,398 'urn:schema:ref': refSchema399 }400 }401 }402 }403 })404 server.setValidatorCompiler(function ({ schema }) {405 t.pass('the querystring schema is compiled')406 return ajvInstance.compile(schema)407 })408 }409 schemaPlugin[Symbol.for('skip-override')] = true410})411 412test('side effect on schema let the server crash', async t => {413 const firstSchema = {414 $id: 'example1',415 type: 'object',416 properties: {417 name: {418 type: 'string'419 }420 }421 }422 423 const reusedSchema = {424 $id: 'example2',425 type: 'object',426 properties: {427 name: {428 oneOf: [429 {430 $ref: 'example1'431 }432 ]433 }434 }435 }436 437 const fastify = Fastify()438 fastify.addSchema(firstSchema)439 440 fastify.post('/a', {441 handler: async () => 'OK',442 schema: {443 body: reusedSchema,444 response: { 200: reusedSchema }445 }446 })447 fastify.post('/b', {448 handler: async () => 'OK',449 schema: {450 body: reusedSchema,451 response: { 200: reusedSchema }452 }453 })454 455 await fastify.ready()456})457 458test('only response schema trigger AJV pollution', async t => {459 const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())460 const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))461 462 const fastify = Fastify()463 fastify.addSchema(ListSchema)464 fastify.addSchema(ShowSchema)465 466 const routeResponseSchemas = {467 schema: { response: { 200: S.ref('ListSchema#') } }468 }469 470 fastify.register(471 async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },472 { prefix: '/prefix1' }473 )474 fastify.register(475 async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },476 { prefix: '/prefix2' }477 )478 479 await fastify.ready()480})481 482test('only response schema trigger AJV pollution #2', async t => {483 const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())484 const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))485 486 const fastify = Fastify()487 fastify.addSchema(ListSchema)488 fastify.addSchema(ShowSchema)489 490 const routeResponseSchemas = {491 schema: {492 params: S.ref('ListSchema#'),493 response: { 200: S.ref('ListSchema#') }494 }495 }496 497 fastify.register(498 async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },499 { prefix: '/prefix1' }500 )501 fastify.register(502 async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },503 { prefix: '/prefix2' }504 )505 506 await fastify.ready()507})508 509test('setSchemaController in a plugin with head routes', t => {510 t.plan(6)511 const baseSchema = {512 $id: 'urn:schema:base',513 definitions: {514 hello: { type: 'string' }515 },516 type: 'object',517 properties: {518 hello: { $ref: '#/definitions/hello' }519 }520 }521 522 const refSchema = {523 $id: 'urn:schema:ref',524 type: 'object',525 properties: {526 hello: { $ref: 'urn:schema:base#/definitions/hello' }527 }528 }529 530 const ajvInstance = new AJV()531 ajvInstance.addSchema(baseSchema)532 ajvInstance.addSchema(refSchema)533 534 const fastify = Fastify({ exposeHeadRoutes: true })535 fastify.register(schemaPlugin)536 fastify.get('/', {537 schema: {538 query: ajvInstance.getSchema('urn:schema:ref').schema,539 response: {540 '2xx': ajvInstance.getSchema('urn:schema:ref').schema541 }542 },543 handler (req, res) {544 res.send({ hello: 'world', evict: 'this' })545 }546 })547 548 fastify.inject('/', (err, res) => {549 t.error(err)550 t.equal(res.statusCode, 200)551 t.same(res.json(), { hello: 'world' })552 })553 554 async function schemaPlugin (server) {555 server.setSchemaController({556 bucket () {557 t.pass('the bucket is created')558 return {559 addSchema (source) {560 ajvInstance.addSchema(source)561 },562 getSchema (id) {563 return ajvInstance.getSchema(id).schema564 },565 getSchemas () {566 return {567 'urn:schema:base': baseSchema,568 'urn:schema:ref': refSchema569 }570 }571 }572 }573 })574 server.setValidatorCompiler(function ({ schema }) {575 if (schema.$id) {576 const stored = ajvInstance.getSchema(schema.$id)577 if (stored) {578 t.pass('the schema is reused')579 return stored580 }581 }582 t.pass('the schema is compiled')583 584 return ajvInstance.compile(schema)585 })586 }587 schemaPlugin[Symbol.for('skip-override')] = true588})589 590test('multiple refs with the same ids', t => {591 t.plan(3)592 const baseSchema = {593 $id: 'urn:schema:base',594 definitions: {595 hello: { type: 'string' }596 },597 type: 'object',598 properties: {599 hello: { $ref: '#/definitions/hello' }600 }601 }602 603 const refSchema = {604 $id: 'urn:schema:ref',605 type: 'object',606 properties: {607 hello: { $ref: 'urn:schema:base#/definitions/hello' }608 }609 }610 611 const fastify = Fastify()612 613 fastify.addSchema(baseSchema)614 fastify.addSchema(refSchema)615 616 fastify.head('/', {617 schema: {618 query: refSchema,619 response: {620 '2xx': refSchema621 }622 },623 handler (req, res) {624 res.send({ hello: 'world', evict: 'this' })625 }626 })627 628 fastify.get('/', {629 schema: {630 query: refSchema,631 response: {632 '2xx': refSchema633 }634 },635 handler (req, res) {636 res.send({ hello: 'world', evict: 'this' })637 }638 })639 640 fastify.inject('/', (err, res) => {641 t.error(err)642 t.equal(res.statusCode, 200)643 t.same(res.json(), { hello: 'world' })644 })645})646 647test('JOI validation overwrite request headers', t => {648 t.plan(3)649 const schemaValidator = ({ schema }) => data => {650 const validationResult = schema.validate(data)651 return validationResult652 }653 654 const fastify = Fastify()655 fastify.setValidatorCompiler(schemaValidator)656 657 fastify.get('/', {658 schema: {659 headers: Joi.object({660 'user-agent': Joi.string().required(),661 host: Joi.string().required()662 })663 }664 }, (request, reply) => {665 reply.send(request.headers)666 })667 668 fastify.inject('/', (err, res) => {669 t.error(err)670 t.equal(res.statusCode, 200)671 t.same(res.json(), {672 'user-agent': 'lightMyRequest',673 host: 'localhost:80'674 })675 })676})677 678test('Custom schema object should not trigger FST_ERR_SCH_DUPLICATE', async t => {679 const fastify = Fastify()680 const handler = () => { }681 682 fastify.get('/the/url', {683 schema: {684 query: yup.object({685 foo: yup.string()686 })687 },688 validatorCompiler: ({ schema, method, url, httpPart }) => {689 return function (data) {690 // with option strict = false, yup `validateSync` function returns the coerced value if validation was successful, or throws if validation failed691 try {692 const result = schema.validateSync(data, {})693 return { value: result }694 } catch (e) {695 return { error: e }696 }697 }698 },699 handler700 })701 702 await fastify.ready()703 t.pass('fastify is ready')704})705 706test('The default schema compilers should not be called when overwritten by the user', async t => {707 const Fastify = t.mockRequire('../', {708 '@fastify/ajv-compiler': () => {709 t.fail('The default validator compiler should not be called')710 },711 '@fastify/fast-json-stringify-compiler': () => {712 t.fail('The default serializer compiler should not be called')713 }714 })715 716 const fastify = Fastify({717 schemaController: {718 compilersFactory: {719 buildValidator: function factory () {720 t.pass('The custom validator compiler should be called')721 return function validatorCompiler () {722 return () => { return true }723 }724 },725 buildSerializer: function factory () {726 t.pass('The custom serializer compiler should be called')727 return function serializerCompiler () {728 return () => { return true }729 }730 }731 }732 }733 })734 735 fastify.get('/',736 {737 schema: {738 query: { foo: { type: 'string' } },739 response: {740 200: { type: 'object' }741 }742 }743 }, () => {})744 745 await fastify.ready()746})747 748test('Supports async JOI validation', t => {749 t.plan(7)750 751 const schemaValidator = ({ schema }) => async data => {752 const validationResult = await schema.validateAsync(data)753 return validationResult754 }755 756 const fastify = Fastify({757 exposeHeadRoutes: false758 })759 fastify.setValidatorCompiler(schemaValidator)760 761 fastify.get('/', {762 schema: {763 headers: Joi.object({764 'user-agent': Joi.string().external(async (val) => {765 if (val !== 'lightMyRequest') {766 throw new Error('Invalid user-agent')767 }768 769 t.equal(val, 'lightMyRequest')770 return val771 }),772 host: Joi.string().required()773 })774 }775 }, (request, reply) => {776 reply.send(request.headers)777 })778 779 fastify.inject('/', (err, res) => {780 t.error(err)781 t.equal(res.statusCode, 200)782 t.same(res.json(), {783 'user-agent': 'lightMyRequest',784 host: 'localhost:80'785 })786 })787 788 fastify.inject({789 url: '/',790 headers: {791 'user-agent': 'invalid'792 }793 }, (err, res) => {794 t.error(err)795 t.equal(res.statusCode, 400)796 t.same(res.json(), {797 statusCode: 400,798 code: 'FST_ERR_VALIDATION',799 error: 'Bad Request',800 message: 'Invalid user-agent (user-agent)'801 })802 })803})804 805test('Supports async AJV validation', t => {806 t.plan(12)807 808 const fastify = Fastify({809 exposeHeadRoutes: false,810 ajv: {811 customOptions: {812 allErrors: true,813 keywords: [814 {815 keyword: 'idExists',816 async: true,817 type: 'number',818 validate: checkIdExists819 }820 ]821 },822 plugins: [823 [ajvErrors, { singleError: '@@@@' }]824 ]825 }826 })827 828 async function checkIdExists (schema, data) {829 const res = await Promise.resolve(data)830 switch (res) {831 case 42:832 return true833 834 case 500:835 throw new Error('custom error')836 837 default:838 return false839 }840 }841 842 const schema = {843 $async: true,844 type: 'object',845 properties: {846 userId: {847 type: 'integer',848 idExists: { table: 'users' }849 },850 postId: {851 type: 'integer',852 idExists: { table: 'posts' }853 }854 }855 }856 857 fastify.post('/', {858 schema: {859 body: schema860 },861 handler (req, reply) { reply.send(req.body) }862 })863 864 fastify.inject({865 method: 'POST',866 url: '/',867 payload: { userId: 99 }868 }, (err, res) => {869 t.error(err)870 t.equal(res.statusCode, 400)871 t.same(res.json(), {872 statusCode: 400,873 code: 'FST_ERR_VALIDATION',874 error: 'Bad Request',875 message: 'validation failed'876 })877 })878 879 fastify.inject({880 method: 'POST',881 url: '/',882 payload: { userId: 500 }883 }, (err, res) => {884 t.error(err)885 t.equal(res.statusCode, 400)886 t.same(res.json(), {887 statusCode: 400,888 code: 'FST_ERR_VALIDATION',889 error: 'Bad Request',890 message: 'custom error'891 })892 })893 894 fastify.inject({895 method: 'POST',896 url: '/',897 payload: { userId: 42 }898 }, (err, res) => {899 t.error(err)900 t.equal(res.statusCode, 200)901 t.same(res.json(), { userId: 42 })902 })903 904 fastify.inject({905 method: 'POST',906 url: '/',907 payload: { userId: 42, postId: 19 }908 }, (err, res) => {909 t.error(err)910 t.equal(res.statusCode, 400)911 t.same(res.json(), {912 statusCode: 400,913 code: 'FST_ERR_VALIDATION',914 error: 'Bad Request',915 message: 'validation failed'916 })917 })918})919 920test('Check all the async AJV validation paths', t => {921 const fastify = Fastify({922 exposeHeadRoutes: false,923 ajv: {924 customOptions: {925 allErrors: true,926 keywords: [927 {928 keyword: 'idExists',929 async: true,930 type: 'number',931 validate: checkIdExists932 }933 ]934 }935 }936 })937 938 async function checkIdExists (schema, data) {939 const res = await Promise.resolve(data)940 switch (res) {941 case 200:942 return true943 944 default:945 return false946 }947 }948 949 const schema = {950 $async: true,951 type: 'object',952 properties: {953 id: {954 type: 'integer',955 idExists: { table: 'posts' }956 }957 }958 }959 960 fastify.post('/:id', {961 schema: {962 params: schema,963 body: schema,964 query: schema,965 headers: schema966 },967 handler (req, reply) { reply.send(req.body) }968 })969 970 const testCases = [971 {972 params: 400,973 body: 200,974 querystring: 200,975 headers: 200,976 response: 400977 },978 {979 params: 200,980 body: 400,981 querystring: 200,982 headers: 200,983 response: 400984 },985 {986 params: 200,987 body: 200,988 querystring: 400,989 headers: 200,990 response: 400991 },992 {993 params: 200,994 body: 200,995 querystring: 200,996 headers: 400,997 response: 400998 },999 {1000 params: 200,1001 body: 200,1002 querystring: 200,1003 headers: 200,1004 response: 2001005 }1006 ]1007 t.plan(testCases.length * 2)1008 testCases.forEach(validate)1009 1010 function validate ({1011 params,1012 body,1013 querystring,1014 headers,1015 response1016 }) {1017 fastify.inject({1018 method: 'POST',1019 url: `/${params}`,1020 headers: { id: headers },1021 query: { id: querystring },1022 payload: { id: body }1023 }, (err, res) => {1024 t.error(err)1025 t.equal(res.statusCode, response)1026 })1027 }1028})1029 1030test('Check mixed sync and async AJV validations', t => {1031 const fastify = Fastify({1032 exposeHeadRoutes: false,1033 ajv: {1034 customOptions: {1035 allErrors: true,1036 keywords: [1037 {1038 keyword: 'idExists',1039 async: true,1040 type: 'number',1041 validate: checkIdExists1042 }1043 ]1044 }1045 }1046 })1047 1048 async function checkIdExists (schema, data) {1049 const res = await Promise.resolve(data)1050 switch (res) {1051 case 200:1052 return true1053 1054 default:1055 return false1056 }1057 }1058 1059 const schemaSync = {1060 type: 'object',1061 properties: {1062 id: { type: 'integer' }1063 }1064 }1065 1066 const schemaAsync = {1067 $async: true,1068 type: 'object',1069 properties: {1070 id: {1071 type: 'integer',1072 idExists: { table: 'posts' }1073 }1074 }1075 }1076 1077 fastify.post('/queryAsync/:id', {1078 schema: {1079 params: schemaSync,1080 body: schemaSync,1081 query: schemaAsync,1082 headers: schemaSync1083 },1084 handler (req, reply) { reply.send(req.body) }1085 })1086 1087 fastify.post('/paramsAsync/:id', {1088 schema: {1089 params: schemaAsync,1090 body: schemaSync1091 },1092 handler (req, reply) { reply.send(req.body) }1093 })1094 1095 fastify.post('/bodyAsync/:id', {1096 schema: {1097 params: schemaAsync,1098 body: schemaAsync,1099 query: schemaSync1100 },1101 handler (req, reply) { reply.send(req.body) }1102 })1103 1104 fastify.post('/headersSync/:id', {1105 schema: {1106 params: schemaSync,1107 body: schemaSync,1108 query: schemaAsync,1109 headers: schemaSync1110 },1111 handler (req, reply) { reply.send(req.body) }1112 })1113 1114 fastify.post('/noHeader/:id', {1115 schema: {1116 params: schemaSync,1117 body: schemaSync,1118 query: schemaAsync1119 },1120 handler (req, reply) { reply.send(req.body) }1121 })1122 1123 fastify.post('/noBody/:id', {1124 schema: {1125 params: schemaSync,1126 query: schemaAsync,1127 headers: schemaSync1128 },1129 handler (req, reply) { reply.send(req.body) }1130 })1131 1132 const testCases = [1133 {1134 url: '/queryAsync',1135 params: 200,1136 body: 200,1137 querystring: 200,1138 headers: 'not a number sync',1139 response: 4001140 },1141 {1142 url: '/paramsAsync',1143 params: 200,1144 body: 'not a number sync',1145 querystring: 200,1146 headers: 200,1147 response: 4001148 },1149 {1150 url: '/bodyAsync',1151 params: 200,1152 body: 200,1153 querystring: 'not a number sync',1154 headers: 200,1155 response: 4001156 },1157 {1158 url: '/headersSync',1159 params: 200,1160 body: 200,1161 querystring: 200,1162 headers: 'not a number sync',1163 response: 4001164 },1165 {1166 url: '/noHeader',1167 params: 200,1168 body: 200,1169 querystring: 200,1170 headers: 'not a number sync, but not validated',1171 response: 2001172 },1173 {1174 url: '/noBody',1175 params: 200,1176 body: 'not a number sync, but not validated',1177 querystring: 200,1178 headers: 'not a number sync',1179 response: 4001180 }1181 ]1182 t.plan(testCases.length * 2)1183 testCases.forEach(validate)1184 1185 function validate ({1186 url,1187 params,1188 body,1189 querystring,1190 headers,1191 response1192 }) {1193 fastify.inject({1194 method: 'POST',1195 url: `${url}/${params || ''}`,1196 headers: { id: headers },1197 query: { id: querystring },1198 payload: { id: body }1199 }, (err, res) => {1200 t.error(err)