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.
		
		
		
		
		
			
		
			
				
					
					
						
							949 lines
						
					
					
						
							18 KiB
						
					
					
				
			
		
		
	
	
							949 lines
						
					
					
						
							18 KiB
						
					
					
				'use strict'
 | 
						|
 | 
						|
const { test } = require('tap')
 | 
						|
const Fastify = require('..')
 | 
						|
 | 
						|
const AJV = require('ajv')
 | 
						|
 | 
						|
const customSchemaCompilers = {
 | 
						|
  body: new AJV({
 | 
						|
    coerceTypes: false
 | 
						|
  }),
 | 
						|
  params: new AJV({
 | 
						|
    coerceTypes: true
 | 
						|
  }),
 | 
						|
  querystring: new AJV({
 | 
						|
    coerceTypes: true
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
const customValidatorCompiler = req => {
 | 
						|
  if (!req.httpPart) {
 | 
						|
    throw new Error('Missing httpPart')
 | 
						|
  }
 | 
						|
 | 
						|
  const compiler = customSchemaCompilers[req.httpPart]
 | 
						|
 | 
						|
  if (!compiler) {
 | 
						|
    throw new Error(`Missing compiler for ${req.httpPart}`)
 | 
						|
  }
 | 
						|
 | 
						|
  return compiler.compile(req.schema)
 | 
						|
}
 | 
						|
 | 
						|
const schemaA = {
 | 
						|
  $id: 'urn:schema:foo',
 | 
						|
  type: 'object',
 | 
						|
  definitions: {
 | 
						|
    foo: { type: 'integer' }
 | 
						|
  },
 | 
						|
  properties: {
 | 
						|
    foo: { $ref: '#/definitions/foo' }
 | 
						|
  }
 | 
						|
}
 | 
						|
const schemaBRefToA = {
 | 
						|
  $id: 'urn:schema:response',
 | 
						|
  type: 'object',
 | 
						|
  required: ['foo'],
 | 
						|
  properties: {
 | 
						|
    foo: { $ref: 'urn:schema:foo#/definitions/foo' }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const schemaCRefToB = {
 | 
						|
  $id: 'urn:schema:request',
 | 
						|
  type: 'object',
 | 
						|
  required: ['foo'],
 | 
						|
  properties: {
 | 
						|
    foo: { $ref: 'urn:schema:response#/properties/foo' }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const schemaArtist = {
 | 
						|
  type: 'object',
 | 
						|
  properties: {
 | 
						|
    name: { type: 'string' },
 | 
						|
    work: { type: 'string' }
 | 
						|
  },
 | 
						|
  required: ['name', 'work']
 | 
						|
}
 | 
						|
 | 
						|
test('Basic validation test', t => {
 | 
						|
  t.plan(6)
 | 
						|
 | 
						|
  const fastify = Fastify()
 | 
						|
  fastify.post('/', {
 | 
						|
    schema: {
 | 
						|
      body: schemaArtist
 | 
						|
    }
 | 
						|
  }, function (req, reply) {
 | 
						|
    reply.code(200).send(req.body.name)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    payload: {
 | 
						|
      name: 'michelangelo',
 | 
						|
      work: 'sculptor, painter, architect and poet'
 | 
						|
    },
 | 
						|
    url: '/'
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.same(res.payload, 'michelangelo')
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    payload: { name: 'michelangelo' },
 | 
						|
    url: '/'
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.same(res.json(), { statusCode: 400, error: 'Bad Request', message: "body should have required property 'work'" })
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('External AJV instance', t => {
 | 
						|
  t.plan(4)
 | 
						|
 | 
						|
  const fastify = Fastify()
 | 
						|
  const ajv = new AJV()
 | 
						|
  ajv.addSchema(schemaA)
 | 
						|
  ajv.addSchema(schemaBRefToA)
 | 
						|
 | 
						|
  // the user must provide the schemas to fastify also
 | 
						|
  fastify.addSchema(schemaA)
 | 
						|
  fastify.addSchema(schemaBRefToA)
 | 
						|
 | 
						|
  fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
 | 
						|
    return ajv.compile(schema)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send({ foo: 1 }) },
 | 
						|
    schema: {
 | 
						|
      body: schemaCRefToB,
 | 
						|
      response: {
 | 
						|
        '2xx': ajv.getSchema('urn:schema:response').schema
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { foo: 42 }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { foo: 'not a number' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Encapsulation', t => {
 | 
						|
  t.plan(19)
 | 
						|
 | 
						|
  const fastify = Fastify()
 | 
						|
  const ajv = new AJV()
 | 
						|
  ajv.addSchema(schemaA)
 | 
						|
  ajv.addSchema(schemaBRefToA)
 | 
						|
 | 
						|
  // the user must provide the schemas to fastify also
 | 
						|
  fastify.addSchema(schemaA)
 | 
						|
  fastify.addSchema(schemaBRefToA)
 | 
						|
 | 
						|
  fastify.register((instance, opts, done) => {
 | 
						|
    const validator = ({ schema, method, url, httpPart }) => {
 | 
						|
      return ajv.compile(schema)
 | 
						|
    }
 | 
						|
    instance.setValidatorCompiler(validator)
 | 
						|
    instance.post('/one', {
 | 
						|
      handler (req, reply) { reply.send({ foo: 'one' }) },
 | 
						|
      schema: {
 | 
						|
        body: ajv.getSchema('urn:schema:response').schema
 | 
						|
      }
 | 
						|
    })
 | 
						|
 | 
						|
    instance.register((instance, opts, done) => {
 | 
						|
      instance.post('/two', {
 | 
						|
        handler (req, reply) {
 | 
						|
          t.same(instance.validatorCompiler, validator)
 | 
						|
          reply.send({ foo: 'two' })
 | 
						|
        },
 | 
						|
        schema: {
 | 
						|
          body: ajv.getSchema('urn:schema:response').schema
 | 
						|
        }
 | 
						|
      })
 | 
						|
 | 
						|
      const anotherValidator = ({ schema, method, url, httpPart }) => {
 | 
						|
        return () => { return true } // always valid
 | 
						|
      }
 | 
						|
      instance.post('/three', {
 | 
						|
        validatorCompiler: anotherValidator,
 | 
						|
        handler (req, reply) {
 | 
						|
          t.same(instance.validatorCompiler, validator, 'the route validator does not change the instance one')
 | 
						|
          reply.send({ foo: 'three' })
 | 
						|
        },
 | 
						|
        schema: {
 | 
						|
          body: ajv.getSchema('urn:schema:response').schema
 | 
						|
        }
 | 
						|
      })
 | 
						|
      done()
 | 
						|
    })
 | 
						|
    done()
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.register((instance, opts, done) => {
 | 
						|
    instance.post('/clean', function (req, reply) {
 | 
						|
      t.equal(instance.validatorCompiler, undefined)
 | 
						|
      reply.send({ foo: 'bar' })
 | 
						|
    })
 | 
						|
    done()
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/one',
 | 
						|
    payload: { foo: 1 }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { foo: 'one' })
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/one',
 | 
						|
    payload: { wrongFoo: 'bar' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/two',
 | 
						|
    payload: { foo: 2 }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { foo: 'two' })
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/two',
 | 
						|
    payload: { wrongFoo: 'bar' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/three',
 | 
						|
    payload: { wrongFoo: 'but works' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { foo: 'three' })
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/clean',
 | 
						|
    payload: { wrongFoo: 'bar' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { foo: 'bar' })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Triple $ref with a simple $id', t => {
 | 
						|
  t.plan(6)
 | 
						|
 | 
						|
  const fastify = Fastify()
 | 
						|
  const ajv = new AJV()
 | 
						|
  ajv.addSchema(schemaA)
 | 
						|
  ajv.addSchema(schemaBRefToA)
 | 
						|
  ajv.addSchema(schemaCRefToB)
 | 
						|
 | 
						|
  // the user must provide the schemas to fastify also
 | 
						|
  fastify.addSchema(schemaA)
 | 
						|
  fastify.addSchema(schemaBRefToA)
 | 
						|
  fastify.addSchema(schemaCRefToB)
 | 
						|
 | 
						|
  fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
 | 
						|
    return ajv.compile(schema)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send({ foo: 105, bar: 'foo' }) },
 | 
						|
    schema: {
 | 
						|
      body: ajv.getSchema('urn:schema:request').schema,
 | 
						|
      response: {
 | 
						|
        '2xx': ajv.getSchema('urn:schema:response').schema
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { foo: 43 }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { foo: 105 })
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { fool: 'bar' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.same(res.json().message, "body should have required property 'foo'")
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Extending schema', t => {
 | 
						|
  t.plan(4)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'address.id',
 | 
						|
    type: 'object',
 | 
						|
    definitions: {
 | 
						|
      address: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          city: { type: 'string' },
 | 
						|
          state: { type: 'string' }
 | 
						|
        },
 | 
						|
        required: ['city', 'state']
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send('works') },
 | 
						|
    schema: {
 | 
						|
      body: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          billingAddress: { $ref: 'address.id#/definitions/address' },
 | 
						|
          shippingAddress: {
 | 
						|
            allOf: [
 | 
						|
              { $ref: 'address.id#/definitions/address' },
 | 
						|
              {
 | 
						|
                properties: { type: { enum: ['residential', 'business'] } },
 | 
						|
                required: ['type']
 | 
						|
              }
 | 
						|
            ]
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      shippingAddress: {
 | 
						|
        city: 'Forlì',
 | 
						|
        state: 'FC'
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      shippingAddress: {
 | 
						|
        city: 'Forlì',
 | 
						|
        state: 'FC',
 | 
						|
        type: 'business'
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Should work with nested ids', t => {
 | 
						|
  t.plan(6)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      id: { type: 'number' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'greetings',
 | 
						|
    type: 'string'
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/:id', {
 | 
						|
    handler (req, reply) { reply.send(typeof req.params.id) },
 | 
						|
    schema: {
 | 
						|
      params: { $ref: 'test#' },
 | 
						|
      body: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          hello: { $ref: 'greetings#' }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/123',
 | 
						|
    payload: {
 | 
						|
      hello: 'world'
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.equal(res.payload, 'number')
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/abc',
 | 
						|
    payload: {
 | 
						|
      hello: 'world'
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.equal(res.json().message, 'params.id should be number')
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Use the same schema across multiple routes', t => {
 | 
						|
  t.plan(8)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      id: { type: 'number' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.get('/first/:id', {
 | 
						|
    handler (req, reply) { reply.send(typeof req.params.id) },
 | 
						|
    schema: {
 | 
						|
      params: { $ref: 'test#' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.get('/second/:id', {
 | 
						|
    handler (req, reply) { reply.send(typeof req.params.id) },
 | 
						|
    schema: {
 | 
						|
      params: { $ref: 'test#' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  ;[
 | 
						|
    '/first/123',
 | 
						|
    '/second/123'
 | 
						|
  ].forEach(url => {
 | 
						|
    fastify.inject({
 | 
						|
      url,
 | 
						|
      method: 'GET'
 | 
						|
    }, (err, res) => {
 | 
						|
      t.error(err)
 | 
						|
      t.equal(res.payload, 'number')
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  ;[
 | 
						|
    '/first/abc',
 | 
						|
    '/second/abc'
 | 
						|
  ].forEach(url => {
 | 
						|
    fastify.inject({
 | 
						|
      url,
 | 
						|
      method: 'GET'
 | 
						|
    }, (err, res) => {
 | 
						|
      t.error(err)
 | 
						|
      t.equal(res.statusCode, 400)
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('JSON Schema validation keywords', t => {
 | 
						|
  t.plan(6)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      ip: {
 | 
						|
        type: 'string',
 | 
						|
        format: 'ipv4'
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.get('/:ip', {
 | 
						|
    handler (req, reply) { reply.send(typeof req.params.ip) },
 | 
						|
    schema: {
 | 
						|
      params: { $ref: 'test#' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'GET',
 | 
						|
    url: '/127.0.0.1'
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.equal(res.payload, 'string')
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'GET',
 | 
						|
    url: '/localhost'
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.same(res.json(), {
 | 
						|
      statusCode: 400,
 | 
						|
      error: 'Bad Request',
 | 
						|
      message: 'params.ip should match format "ipv4"'
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Nested id calls', t => {
 | 
						|
  t.plan(6)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      ip: {
 | 
						|
        type: 'string',
 | 
						|
        format: 'ipv4'
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'hello',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      host: { $ref: 'test#' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send(typeof req.body.host.ip) },
 | 
						|
    schema: {
 | 
						|
      body: { $ref: 'hello#' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { host: { ip: '127.0.0.1' } }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.equal(res.payload, 'string')
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { host: { ip: 'localhost' } }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.same(res.json(), {
 | 
						|
      error: 'Bad Request',
 | 
						|
      message: 'body.host.ip should match format "ipv4"',
 | 
						|
      statusCode: 400
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Use the same schema id in different places', t => {
 | 
						|
  t.plan(2)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      id: { type: 'number' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send({ id: req.body.id / 2 }) },
 | 
						|
    schema: {
 | 
						|
      body: { $ref: 'test#' },
 | 
						|
      response: {
 | 
						|
        200: { $ref: 'test#' }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { id: 42 }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.same(res.json(), { id: 21 })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Use shared schema and $ref with $id ($ref to $id)', t => {
 | 
						|
  t.plan(5)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'http://foo/test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      id: { type: 'number' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const body = {
 | 
						|
    $id: 'http://foo/user',
 | 
						|
    $schema: 'http://json-schema.org/draft-07/schema#',
 | 
						|
    type: 'object',
 | 
						|
    definitions: {
 | 
						|
      address: {
 | 
						|
        $id: '#address',
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          city: { type: 'string' }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    required: ['address'],
 | 
						|
    properties: {
 | 
						|
      test: { $ref: 'http://foo/test#' }, // to external
 | 
						|
      address: { $ref: '#address' } // to local
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    handler (req, reply) { reply.send(req.body.test) },
 | 
						|
    schema: {
 | 
						|
      body,
 | 
						|
      response: {
 | 
						|
        200: { $ref: 'http://foo/test#' }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const id = Date.now()
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      address: { city: 'New Node' },
 | 
						|
      test: { id }
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.same(res.json(), { id })
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { test: { id } }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.same(res.json(), {
 | 
						|
      error: 'Bad Request',
 | 
						|
      message: "body should have required property 'address'",
 | 
						|
      statusCode: 400
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Use items with $ref', t => {
 | 
						|
  t.plan(4)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'http://example.com/ref-to-external-validator.json',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      hello: { type: 'string' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const body = {
 | 
						|
    type: 'array',
 | 
						|
    items: { $ref: 'http://example.com/ref-to-external-validator.json#' },
 | 
						|
    default: []
 | 
						|
  }
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    schema: { body },
 | 
						|
    handler: (_, r) => { r.send('ok') }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: [{ hello: 'world' }]
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.payload, 'ok')
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: { hello: 'world' }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Use $ref to /definitions', t => {
 | 
						|
  t.plan(6)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.addSchema({
 | 
						|
    $id: 'test',
 | 
						|
    type: 'object',
 | 
						|
    properties: {
 | 
						|
      id: { type: 'number' }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const body = {
 | 
						|
    type: 'object',
 | 
						|
    definitions: {
 | 
						|
      address: {
 | 
						|
        $id: '#otherId',
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          city: { type: 'string' }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    properties: {
 | 
						|
      test: { $ref: 'test#' },
 | 
						|
      address: { $ref: '#/definitions/address' }
 | 
						|
    },
 | 
						|
    required: ['address', 'test']
 | 
						|
  }
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    schema: {
 | 
						|
      body,
 | 
						|
      response: {
 | 
						|
        200: body
 | 
						|
      }
 | 
						|
    },
 | 
						|
    handler: (req, reply) => {
 | 
						|
      req.body.removeThis = 'it should not be serialized'
 | 
						|
      reply.send(req.body)
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const payload = {
 | 
						|
    address: { city: 'New Node' },
 | 
						|
    test: { id: Date.now() }
 | 
						|
  }
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), payload)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      address: { city: 'New Node' },
 | 
						|
      test: { id: 'wrong' }
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
    t.same(res.json(), {
 | 
						|
      error: 'Bad Request',
 | 
						|
      message: 'body.test.id should be number',
 | 
						|
      statusCode: 400
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Custom AJV settings - pt1', t => {
 | 
						|
  t.plan(4)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    schema: {
 | 
						|
      body: { num: { type: 'integer' } }
 | 
						|
    },
 | 
						|
    handler: (req, reply) => {
 | 
						|
      t.equal(req.body.num, 12)
 | 
						|
      reply.send(req.body)
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      num: '12'
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 200)
 | 
						|
    t.same(res.json(), { num: 12 })
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Custom AJV settings - pt2', t => {
 | 
						|
  t.plan(2)
 | 
						|
  const fastify = Fastify({
 | 
						|
    ajv: {
 | 
						|
      customOptions: {
 | 
						|
        coerceTypes: false
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.post('/', {
 | 
						|
    schema: {
 | 
						|
      body: { num: { type: 'integer' } }
 | 
						|
    },
 | 
						|
    handler: (req, reply) => {
 | 
						|
      t.fail('the handler is not called because the "12" is not coerced to number')
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/',
 | 
						|
    payload: {
 | 
						|
      num: '12'
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Custom AJV settings on different parameters - pt1', t => {
 | 
						|
  t.plan(2)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.setValidatorCompiler(customValidatorCompiler)
 | 
						|
 | 
						|
  fastify.post('/api/:id', {
 | 
						|
    schema: {
 | 
						|
      querystring: { id: { type: 'integer' } },
 | 
						|
      body: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          num: { type: 'number' }
 | 
						|
        },
 | 
						|
        required: ['num']
 | 
						|
      }
 | 
						|
    },
 | 
						|
    handler: (req, reply) => {
 | 
						|
      t.fail('the handler is not called because the "12" is not coerced to number')
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/api/42',
 | 
						|
    payload: {
 | 
						|
      num: '12'
 | 
						|
    }
 | 
						|
  }, (err, res) => {
 | 
						|
    t.error(err)
 | 
						|
    t.equal(res.statusCode, 400)
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
test('Custom AJV settings on different parameters - pt2', t => {
 | 
						|
  t.plan(4)
 | 
						|
  const fastify = Fastify()
 | 
						|
 | 
						|
  fastify.setValidatorCompiler(customValidatorCompiler)
 | 
						|
 | 
						|
  fastify.post('/api/:id', {
 | 
						|
    schema: {
 | 
						|
      params: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          id: { type: 'number' }
 | 
						|
        },
 | 
						|
        required: ['id']
 | 
						|
      },
 | 
						|
      body: {
 | 
						|
        type: 'object',
 | 
						|
        properties: {
 | 
						|
          num: { type: 'number' }
 | 
						|
        },
 | 
						|
        required: ['num']
 | 
						|
      }
 | 
						|
    },
 | 
						|
    handler: (req, reply) => {
 | 
						|
      t.same(typeof req.params.id, 'number')
 | 
						|
      t.same(typeof req.body.num, 'number')
 | 
						|
      t.same(req.params.id, 42)
 | 
						|
      t.same(req.body.num, 12)
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.inject({
 | 
						|
    method: 'POST',
 | 
						|
    url: '/api/42',
 | 
						|
    payload: {
 | 
						|
      num: 12
 | 
						|
    }
 | 
						|
  })
 | 
						|
})
 |