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
3 years ago
|
'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
|
||
|
}
|
||
|
})
|
||
|
})
|