You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							824 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
	
	
							824 lines
						
					
					
						
							17 KiB
						
					
					
				| 'use strict'
 | |
| 
 | |
| const { test } = require('tap')
 | |
| const Joi = require('@hapi/joi')
 | |
| const AJV = require('ajv')
 | |
| const S = require('fluent-json-schema')
 | |
| const Fastify = require('..')
 | |
| const ajvMergePatch = require('ajv-merge-patch')
 | |
| const ajvErrors = require('ajv-errors')
 | |
| 
 | |
| const buildValidatorAJV8 = require('@fastify/ajv-compiler-8')
 | |
| 
 | |
| test('Ajv8 usage instead of the bundle one', t => {
 | |
|   t.plan(2)
 | |
| 
 | |
|   t.test('use new ajv8 option', t => {
 | |
|     t.plan(2)
 | |
|     const fastify = Fastify({
 | |
|       ajv: {
 | |
|         customOptions: { strictRequired: true }
 | |
|       },
 | |
|       schemaController: {
 | |
|         compilersFactory: {
 | |
|           buildValidator: buildValidatorAJV8()
 | |
|         }
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     fastify.post('/', {
 | |
|       schema: {
 | |
|         body: {
 | |
|           type: 'object',
 | |
|           required: ['missing'],
 | |
|           properties: {
 | |
|             foo: {
 | |
|               type: 'string'
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|       handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|     })
 | |
| 
 | |
|     fastify.ready(err => {
 | |
|       t.ok(err)
 | |
|       t.match(err.message, 'strictRequired', 'the new ajv8 option trigger a startup error')
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   t.test('use new ajv8 option within a response schema', t => {
 | |
|     t.plan(2)
 | |
|     const fastify = Fastify({
 | |
|       schemaController: {
 | |
|         compilersFactory: {
 | |
|           buildValidator: buildValidatorAJV8()
 | |
|         }
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     fastify.post('/', {
 | |
|       schema: {
 | |
|         body: {
 | |
|           type: 'object',
 | |
|           required: ['missing'],
 | |
|           properties: {
 | |
|             foo: {
 | |
|               type: 'string'
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         response: {
 | |
|           '2xx': {
 | |
|             type: 'object',
 | |
|             properties: {
 | |
|               ok: {
 | |
|                 type: 'integer'
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|       handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|     })
 | |
| 
 | |
|     fastify.ready(err => {
 | |
|       t.error(err)
 | |
|       t.pass('startup successful')
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('Ajv8 usage with plugins', t => {
 | |
|   t.plan(2)
 | |
| 
 | |
|   t.test('use new ajv8 option', t => {
 | |
|     t.plan(3)
 | |
|     const fastify = Fastify({
 | |
|       ajv: {
 | |
|         customOptions: { validateFormats: true },
 | |
|         plugins: [require('ajv-formats')]
 | |
|       },
 | |
|       schemaController: {
 | |
|         compilersFactory: {
 | |
|           buildValidator: buildValidatorAJV8()
 | |
|         }
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     callIt(fastify, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 400)
 | |
|       t.equal(res.json().message, 'body must match format "date"')
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   t.test('use new ajv8 option - avoid check', t => {
 | |
|     t.plan(2)
 | |
|     const fastify = Fastify({
 | |
|       ajv: {
 | |
|         customOptions: { validateFormats: false }
 | |
|       },
 | |
|       schemaController: {
 | |
|         compilersFactory: {
 | |
|           buildValidator: buildValidatorAJV8()
 | |
|         }
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     callIt(fastify, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 200)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   function callIt (fastify, cb) {
 | |
|     fastify.post('/', {
 | |
|       schema: {
 | |
|         body: {
 | |
|           type: 'object',
 | |
|           properties: {
 | |
|             foo: {
 | |
|               type: 'string',
 | |
|               format: 'date'
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|       handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|     })
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'POST',
 | |
|       url: '/',
 | |
|       payload: { foo: '99' }
 | |
|     }, cb)
 | |
|   }
 | |
| })
 | |
| 
 | |
| test('Ajv plugins array parameter', t => {
 | |
|   t.plan(3)
 | |
|   const fastify = Fastify({
 | |
|     ajv: {
 | |
|       customOptions: {
 | |
|         jsonPointers: true,
 | |
|         allErrors: true
 | |
|       },
 | |
|       plugins: [
 | |
|         [ajvErrors, { singleError: '@@@@' }]
 | |
|       ]
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.post('/', {
 | |
|     schema: {
 | |
|       body: {
 | |
|         type: 'object',
 | |
|         properties: {
 | |
|           foo: {
 | |
|             type: 'number',
 | |
|             minimum: 2,
 | |
|             maximum: 10,
 | |
|             multipleOf: 2,
 | |
|             errorMessage: {
 | |
|               type: 'should be number',
 | |
|               minimum: 'should be >= 2',
 | |
|               maximum: 'should be <= 10',
 | |
|               multipleOf: 'should be multipleOf 2'
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|   })
 | |
| 
 | |
|   fastify.inject({
 | |
|     method: 'POST',
 | |
|     url: '/',
 | |
|     payload: { foo: 99 }
 | |
|   }, (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 400)
 | |
|     t.equal(res.json().message, 'body/foo should be <= 10@@@@should be multipleOf 2')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('Should handle root $merge keywords in header', t => {
 | |
|   t.plan(5)
 | |
|   const fastify = Fastify({
 | |
|     ajv: {
 | |
|       plugins: [
 | |
|         ajvMergePatch
 | |
|       ]
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.route({
 | |
|     method: 'GET',
 | |
|     url: '/',
 | |
|     schema: {
 | |
|       headers: {
 | |
|         $merge: {
 | |
|           source: {
 | |
|             type: 'object',
 | |
|             properties: {
 | |
|               q: { type: 'string' }
 | |
|             }
 | |
|           },
 | |
|           with: { required: ['q'] }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|   })
 | |
| 
 | |
|   fastify.ready(err => {
 | |
|     t.error(err)
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'GET',
 | |
|       url: '/'
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 400)
 | |
|     })
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'GET',
 | |
|       url: '/',
 | |
|       headers: { q: 'foo' }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 200)
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('Should handle root $patch keywords in header', t => {
 | |
|   t.plan(5)
 | |
|   const fastify = Fastify({
 | |
|     ajv: {
 | |
|       plugins: [
 | |
|         ajvMergePatch
 | |
|       ]
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.route({
 | |
|     method: 'GET',
 | |
|     url: '/',
 | |
|     schema: {
 | |
|       headers: {
 | |
|         $patch: {
 | |
|           source: {
 | |
|             type: 'object',
 | |
|             properties: {
 | |
|               q: { type: 'string' }
 | |
|             }
 | |
|           },
 | |
|           with: [
 | |
|             {
 | |
|               op: 'add',
 | |
|               path: '/properties/q',
 | |
|               value: { type: 'number' }
 | |
|             }
 | |
|           ]
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|   })
 | |
| 
 | |
|   fastify.ready(err => {
 | |
|     t.error(err)
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'GET',
 | |
|       url: '/',
 | |
|       headers: {
 | |
|         q: 'foo'
 | |
|       }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 400)
 | |
|     })
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'GET',
 | |
|       url: '/',
 | |
|       headers: { q: 10 }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 200)
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('Should handle $merge keywords in body', t => {
 | |
|   t.plan(5)
 | |
|   const fastify = Fastify({
 | |
|     ajv: {
 | |
|       plugins: [ajvMergePatch]
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.post('/', {
 | |
|     schema: {
 | |
|       body: {
 | |
|         $merge: {
 | |
|           source: {
 | |
|             type: 'object',
 | |
|             properties: {
 | |
|               q: {
 | |
|                 type: 'string'
 | |
|               }
 | |
|             }
 | |
|           },
 | |
|           with: {
 | |
|             required: ['q']
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|   })
 | |
| 
 | |
|   fastify.ready(err => {
 | |
|     t.error(err)
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'POST',
 | |
|       url: '/'
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 400)
 | |
|     })
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'POST',
 | |
|       url: '/',
 | |
|       payload: { q: 'foo' }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 200)
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('Should handle $patch keywords in body', t => {
 | |
|   t.plan(5)
 | |
|   const fastify = Fastify({
 | |
|     ajv: {
 | |
|       plugins: [ajvMergePatch]
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.post('/', {
 | |
|     schema: {
 | |
|       body: {
 | |
|         $patch: {
 | |
|           source: {
 | |
|             type: 'object',
 | |
|             properties: {
 | |
|               q: {
 | |
|                 type: 'string'
 | |
|               }
 | |
|             }
 | |
|           },
 | |
|           with: [
 | |
|             {
 | |
|               op: 'add',
 | |
|               path: '/properties/q',
 | |
|               value: { type: 'number' }
 | |
|             }
 | |
|           ]
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     handler (req, reply) { reply.send({ ok: 1 }) }
 | |
|   })
 | |
| 
 | |
|   fastify.ready(err => {
 | |
|     t.error(err)
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'POST',
 | |
|       url: '/',
 | |
|       payload: { q: 'foo' }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 400)
 | |
|     })
 | |
| 
 | |
|     fastify.inject({
 | |
|       method: 'POST',
 | |
|       url: '/',
 | |
|       payload: { q: 10 }
 | |
|     }, (err, res) => {
 | |
|       t.error(err)
 | |
|       t.equal(res.statusCode, 200)
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test("serializer read validator's schemas", t => {
 | |
|   t.plan(4)
 | |
|   const ajvInstance = new AJV()
 | |
| 
 | |
|   const baseSchema = {
 | |
|     $id: 'http://example.com/schemas/base',
 | |
|     definitions: {
 | |
|       hello: { type: 'string' }
 | |
|     },
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: '#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const refSchema = {
 | |
|     $id: 'http://example.com/schemas/ref',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: 'http://example.com/schemas/base#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ajvInstance.addSchema(baseSchema)
 | |
|   ajvInstance.addSchema(refSchema)
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     schemaController: {
 | |
|       bucket: function factory (storeInit) {
 | |
|         t.notOk(storeInit, 'is always empty because fastify.addSchema is not called')
 | |
|         return {
 | |
|           getSchemas () {
 | |
|             return {
 | |
|               [baseSchema.$id]: ajvInstance.getSchema(baseSchema.$id).schema,
 | |
|               [refSchema.$id]: ajvInstance.getSchema(refSchema.$id).schema
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.setValidatorCompiler(function ({ schema }) {
 | |
|     return ajvInstance.compile(schema)
 | |
|   })
 | |
| 
 | |
|   fastify.get('/', {
 | |
|     schema: {
 | |
|       response: {
 | |
|         '2xx': ajvInstance.getSchema('http://example.com/schemas/ref').schema
 | |
|       }
 | |
|     },
 | |
|     handler (req, res) { res.send({ hello: 'world', evict: 'this' }) }
 | |
|   })
 | |
| 
 | |
|   fastify.inject('/', (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 200)
 | |
|     t.same(res.json(), { hello: 'world' })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('setSchemaController in a plugin', t => {
 | |
|   t.plan(5)
 | |
|   const baseSchema = {
 | |
|     $id: 'urn:schema:base',
 | |
|     definitions: {
 | |
|       hello: { type: 'string' }
 | |
|     },
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: '#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const refSchema = {
 | |
|     $id: 'urn:schema:ref',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: 'urn:schema:base#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const ajvInstance = new AJV()
 | |
|   ajvInstance.addSchema(baseSchema)
 | |
|   ajvInstance.addSchema(refSchema)
 | |
| 
 | |
|   const fastify = Fastify()
 | |
|   fastify.register(schemaPlugin)
 | |
|   fastify.get('/', {
 | |
|     schema: {
 | |
|       query: ajvInstance.getSchema('urn:schema:ref').schema,
 | |
|       response: {
 | |
|         '2xx': ajvInstance.getSchema('urn:schema:ref').schema
 | |
|       }
 | |
|     },
 | |
|     handler (req, res) {
 | |
|       res.send({ hello: 'world', evict: 'this' })
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.inject('/', (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 200)
 | |
|     t.same(res.json(), { hello: 'world' })
 | |
|   })
 | |
| 
 | |
|   async function schemaPlugin (server) {
 | |
|     server.setSchemaController({
 | |
|       bucket () {
 | |
|         t.pass('the bucket is created')
 | |
|         return {
 | |
|           addSchema (source) {
 | |
|             ajvInstance.addSchema(source)
 | |
|           },
 | |
|           getSchema (id) {
 | |
|             return ajvInstance.getSchema(id).schema
 | |
|           },
 | |
|           getSchemas () {
 | |
|             return {
 | |
|               'urn:schema:base': baseSchema,
 | |
|               'urn:schema:ref': refSchema
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     })
 | |
|     server.setValidatorCompiler(function ({ schema }) {
 | |
|       t.pass('the querystring schema is compiled')
 | |
|       return ajvInstance.compile(schema)
 | |
|     })
 | |
|   }
 | |
|   schemaPlugin[Symbol.for('skip-override')] = true
 | |
| })
 | |
| 
 | |
| test('side effect on schema let the server crash', async t => {
 | |
|   const firstSchema = {
 | |
|     $id: 'example1',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       name: {
 | |
|         type: 'string'
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const reusedSchema = {
 | |
|     $id: 'example2',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       name: {
 | |
|         oneOf: [
 | |
|           {
 | |
|             $ref: 'example1'
 | |
|           }
 | |
|         ]
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const fastify = Fastify()
 | |
|   fastify.addSchema(firstSchema)
 | |
| 
 | |
|   fastify.post('/a', {
 | |
|     handler: async () => 'OK',
 | |
|     schema: {
 | |
|       body: reusedSchema,
 | |
|       response: { 200: reusedSchema }
 | |
|     }
 | |
|   })
 | |
|   fastify.post('/b', {
 | |
|     handler: async () => 'OK',
 | |
|     schema: {
 | |
|       body: reusedSchema,
 | |
|       response: { 200: reusedSchema }
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   await fastify.ready()
 | |
| })
 | |
| 
 | |
| test('only response schema trigger AJV pollution', async t => {
 | |
|   const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
 | |
|   const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
 | |
| 
 | |
|   const fastify = Fastify()
 | |
|   fastify.addSchema(ListSchema)
 | |
|   fastify.addSchema(ShowSchema)
 | |
| 
 | |
|   const routeResponseSchemas = {
 | |
|     schema: { response: { 200: S.ref('ListSchema#') } }
 | |
|   }
 | |
| 
 | |
|   fastify.register(
 | |
|     async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
 | |
|     { prefix: '/prefix1' }
 | |
|   )
 | |
|   fastify.register(
 | |
|     async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
 | |
|     { prefix: '/prefix2' }
 | |
|   )
 | |
| 
 | |
|   await fastify.ready()
 | |
| })
 | |
| 
 | |
| test('only response schema trigger AJV pollution #2', async t => {
 | |
|   const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
 | |
|   const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
 | |
| 
 | |
|   const fastify = Fastify()
 | |
|   fastify.addSchema(ListSchema)
 | |
|   fastify.addSchema(ShowSchema)
 | |
| 
 | |
|   const routeResponseSchemas = {
 | |
|     schema: {
 | |
|       params: S.ref('ListSchema#'),
 | |
|       response: { 200: S.ref('ListSchema#') }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   fastify.register(
 | |
|     async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
 | |
|     { prefix: '/prefix1' }
 | |
|   )
 | |
|   fastify.register(
 | |
|     async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
 | |
|     { prefix: '/prefix2' }
 | |
|   )
 | |
| 
 | |
|   await fastify.ready()
 | |
| })
 | |
| 
 | |
| test('setSchemaController in a plugin with head routes', t => {
 | |
|   t.plan(6)
 | |
|   const baseSchema = {
 | |
|     $id: 'urn:schema:base',
 | |
|     definitions: {
 | |
|       hello: { type: 'string' }
 | |
|     },
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: '#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const refSchema = {
 | |
|     $id: 'urn:schema:ref',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: 'urn:schema:base#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const ajvInstance = new AJV()
 | |
|   ajvInstance.addSchema(baseSchema)
 | |
|   ajvInstance.addSchema(refSchema)
 | |
| 
 | |
|   const fastify = Fastify({ exposeHeadRoutes: true })
 | |
|   fastify.register(schemaPlugin)
 | |
|   fastify.get('/', {
 | |
|     schema: {
 | |
|       query: ajvInstance.getSchema('urn:schema:ref').schema,
 | |
|       response: {
 | |
|         '2xx': ajvInstance.getSchema('urn:schema:ref').schema
 | |
|       }
 | |
|     },
 | |
|     handler (req, res) {
 | |
|       res.send({ hello: 'world', evict: 'this' })
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.inject('/', (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 200)
 | |
|     t.same(res.json(), { hello: 'world' })
 | |
|   })
 | |
| 
 | |
|   async function schemaPlugin (server) {
 | |
|     server.setSchemaController({
 | |
|       bucket () {
 | |
|         t.pass('the bucket is created')
 | |
|         return {
 | |
|           addSchema (source) {
 | |
|             ajvInstance.addSchema(source)
 | |
|           },
 | |
|           getSchema (id) {
 | |
|             return ajvInstance.getSchema(id).schema
 | |
|           },
 | |
|           getSchemas () {
 | |
|             return {
 | |
|               'urn:schema:base': baseSchema,
 | |
|               'urn:schema:ref': refSchema
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     })
 | |
|     server.setValidatorCompiler(function ({ schema }) {
 | |
|       if (schema.$id) {
 | |
|         const stored = ajvInstance.getSchema(schema.$id)
 | |
|         if (stored) {
 | |
|           t.pass('the schema is reused')
 | |
|           return stored
 | |
|         }
 | |
|       }
 | |
|       t.pass('the schema is compiled')
 | |
| 
 | |
|       return ajvInstance.compile(schema)
 | |
|     })
 | |
|   }
 | |
|   schemaPlugin[Symbol.for('skip-override')] = true
 | |
| })
 | |
| 
 | |
| test('multiple refs with the same ids', t => {
 | |
|   t.plan(3)
 | |
|   const baseSchema = {
 | |
|     $id: 'urn:schema:base',
 | |
|     definitions: {
 | |
|       hello: { type: 'string' }
 | |
|     },
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: '#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const refSchema = {
 | |
|     $id: 'urn:schema:ref',
 | |
|     type: 'object',
 | |
|     properties: {
 | |
|       hello: { $ref: 'urn:schema:base#/definitions/hello' }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const fastify = Fastify()
 | |
| 
 | |
|   fastify.addSchema(baseSchema)
 | |
|   fastify.addSchema(refSchema)
 | |
|   fastify.get('/', {
 | |
|     schema: {
 | |
|       query: refSchema,
 | |
|       response: {
 | |
|         '2xx': refSchema
 | |
|       }
 | |
|     },
 | |
|     handler (req, res) {
 | |
|       res.send({ hello: 'world', evict: 'this' })
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.head('/', {
 | |
|     schema: {
 | |
|       query: refSchema,
 | |
|       response: {
 | |
|         '2xx': refSchema
 | |
|       }
 | |
|     },
 | |
|     handler (req, res) {
 | |
|       res.send({ hello: 'world', evict: 'this' })
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.inject('/', (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 200)
 | |
|     t.same(res.json(), { hello: 'world' })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('JOI validation overwrite request headers', t => {
 | |
|   t.plan(3)
 | |
|   const schemaValidator = ({ schema }) => data => {
 | |
|     const validationResult = schema.validate(data)
 | |
|     return validationResult
 | |
|   }
 | |
| 
 | |
|   const fastify = Fastify()
 | |
|   fastify.setValidatorCompiler(schemaValidator)
 | |
| 
 | |
|   fastify.get('/', {
 | |
|     schema: {
 | |
|       headers: Joi.object({
 | |
|         'user-agent': Joi.string().required(),
 | |
|         host: Joi.string().required()
 | |
|       })
 | |
|     }
 | |
|   }, (request, reply) => {
 | |
|     reply.send(request.headers)
 | |
|   })
 | |
| 
 | |
|   fastify.inject('/', (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 200)
 | |
|     t.same(res.json(), {
 | |
|       'user-agent': 'lightMyRequest',
 | |
|       host: 'localhost:80'
 | |
|     })
 | |
|   })
 | |
| })
 |