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.

2819 lines
64 KiB

'use strict'
const t = require('tap')
const test = t.test
const semver = require('semver')
const sget = require('simple-get').concat
const stream = require('stream')
const Fastify = require('..')
const fp = require('fastify-plugin')
const fs = require('fs')
const split = require('split2')
const symbols = require('../lib/symbols.js')
const payload = { hello: 'world' }
test('hooks', t => {
t.plan(38)
const fastify = Fastify()
try {
fastify.addHook('preHandler', function (request, reply, next) {
t.is(request.test, 'the request is coming')
t.is(reply.test, 'the reply has come')
if (request.raw.method === 'HEAD') {
next(new Error('some error'))
} else {
next()
}
})
t.pass()
} catch (e) {
t.fail()
}
try {
fastify.addHook('preParsing', function (request, reply, next) {
request.preParsing = true
t.is(request.test, 'the request is coming')
t.is(reply.test, 'the reply has come')
next()
})
t.pass()
} catch (e) {
t.fail()
}
try {
fastify.addHook('preValidation', function (request, reply, next) {
t.is(request.preParsing, true)
t.is(request.test, 'the request is coming')
t.is(reply.test, 'the reply has come')
next()
})
t.pass()
} catch (e) {
t.fail()
}
try {
fastify.addHook('preSerialization', function (request, reply, payload, next) {
t.ok('preSerialization called')
next()
})
t.pass()
} catch (e) {
t.fail()
}
try {
fastify.addHook('onRequest', function (request, reply, next) {
request.test = 'the request is coming'
reply.test = 'the reply has come'
if (request.raw.method === 'DELETE') {
next(new Error('some error'))
} else {
next()
}
})
t.pass()
} catch (e) {
t.fail()
}
fastify.addHook('onResponse', function (request, reply, next) {
t.ok('onResponse called')
next()
})
fastify.addHook('onSend', function (req, reply, thePayload, next) {
t.ok('onSend called')
next()
})
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
t.is(req.test, 'the request is coming')
t.is(reply.test, 'the reply has come')
reply.code(200).send(payload)
},
onResponse: function (req, reply, done) {
t.ok('onResponse inside hook')
},
response: {
200: {
type: 'object'
}
}
})
fastify.head('/', function (req, reply) {
reply.code(200).send(payload)
})
fastify.delete('/', function (req, reply) {
reply.code(200).send(payload)
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 500)
})
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 500)
})
})
})
test('onRequest hook should support encapsulation / 1', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRequest', (req, reply, next) => {
t.strictEqual(req.raw.url, '/plugin')
next()
})
instance.get('/plugin', (request, reply) => {
reply.send()
})
next()
})
fastify.get('/root', (request, reply) => {
reply.send()
})
fastify.inject('/root', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
fastify.inject('/plugin', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('onRequest hook should support encapsulation / 2', t => {
t.plan(3)
const fastify = Fastify()
var pluginInstance
fastify.addHook('onRequest', () => {})
fastify.register((instance, opts, next) => {
instance.addHook('onRequest', () => {})
pluginInstance = instance
next()
})
fastify.ready(err => {
t.error(err)
t.is(fastify[symbols.kHooks].onRequest.length, 1)
t.is(pluginInstance[symbols.kHooks].onRequest.length, 2)
})
})
test('onRequest hook should support encapsulation / 3', t => {
t.plan(20)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('onRequest', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
req.first = true
next()
})
fastify.decorate('hello2', 'world')
fastify.get('/first', (req, reply) => {
t.ok(req.first)
t.notOk(req.second)
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello3', 'world')
instance.addHook('onRequest', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
t.ok(this.hello3)
req.second = true
next()
})
instance.get('/second', (req, reply) => {
t.ok(req.first)
t.ok(req.second)
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('preHandler hook should support encapsulation / 5', t => {
t.plan(17)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('preHandler', function (req, res, next) {
t.ok(this.hello)
req.first = true
next()
})
fastify.get('/first', (req, reply) => {
t.ok(req.first)
t.notOk(req.second)
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello2', 'world')
instance.addHook('preHandler', function (req, res, next) {
t.ok(this.hello)
t.ok(this.hello2)
req.second = true
next()
})
instance.get('/second', (req, reply) => {
t.ok(req.first)
t.ok(req.second)
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('onRoute hook should be called / 1', t => {
t.plan(2)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', () => {
t.pass()
})
instance.get('/', opts, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should be called / 2', t => {
t.plan(5)
let firstHandler = 0
let secondHandler = 0
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
t.pass()
firstHandler++
})
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', (route) => {
t.pass()
secondHandler++
})
instance.get('/', opts, function (req, reply) {
reply.send()
})
next()
})
.after(() => {
t.strictEqual(firstHandler, 1)
t.strictEqual(secondHandler, 1)
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should be called / 3', t => {
t.plan(6)
const fastify = Fastify()
function handler (req, reply) {
reply.send()
}
fastify.addHook('onRoute', (route) => {
t.pass()
})
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', (route) => {
t.pass()
})
instance.get('/a', handler)
next()
})
.after((err, done) => {
t.error(err)
setTimeout(() => {
fastify.get('/b', handler)
done()
}, 10)
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute should keep the context', t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.decorate('test', true)
instance.addHook('onRoute', onRoute)
t.ok(instance.prototype === fastify.prototype)
function onRoute (route) {
t.ok(this.test)
t.strictEqual(this, instance)
}
instance.get('/', opts, function (req, reply) {
reply.send()
})
next()
})
fastify.close((err) => {
t.error(err)
})
})
test('onRoute hook should pass correct route', t => {
t.plan(7)
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/')
t.strictEqual(route.path, '/')
})
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', (route) => {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/')
t.strictEqual(route.path, '/')
})
instance.get('/', opts, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should pass correct route with custom prefix', t => {
t.plan(9)
const fastify = Fastify()
fastify.addHook('onRoute', function (route) {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/v1/foo')
t.strictEqual(route.path, '/v1/foo')
t.strictEqual(route.prefix, '/v1')
})
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', function (route) {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/v1/foo')
t.strictEqual(route.path, '/v1/foo')
t.strictEqual(route.prefix, '/v1')
})
instance.get('/foo', opts, function (req, reply) {
reply.send()
})
next()
}, { prefix: '/v1' })
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should pass correct route with custom options', t => {
t.plan(6)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', function (route) {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/foo')
t.strictEqual(route.logLevel, 'info')
t.strictEqual(route.bodyLimit, 100)
t.type(route.logSerializers.test, 'function')
})
instance.get('/foo', {
logLevel: 'info',
bodyLimit: 100,
logSerializers: {
test: value => value
}
}, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should receive any route option', t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', function (route) {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/foo')
t.strictEqual(route.auth, 'basic')
})
instance.get('/foo', { auth: 'basic' }, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should preserve system route configuration', t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', function (route) {
t.strictEqual(route.method, 'GET')
t.strictEqual(route.url, '/foo')
t.strictEqual(route.handler.length, 2)
})
instance.get('/foo', { url: '/bar', method: 'POST' }, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should preserve handler function in options of shorthand route system configuration', t => {
t.plan(2)
const handler = (req, reply) => {}
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', function (route) {
t.strictEqual(route.handler, handler)
})
instance.get('/foo', { handler })
next()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRoute hook should able to change the route url', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', (route) => {
t.strictEqual(route.url, '/föö')
route.url = encodeURI(route.url)
})
instance.get('/föö', (request, reply) => {
reply.send('here /föö')
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + encodeURI('/föö')
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(body.toString(), 'here /föö')
})
})
})
test('onRoute hook that throws should be caught ', t => {
t.plan(1)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onRoute', () => {
throw new Error('snap')
})
instance.get('/', opts, function (req, reply) {
reply.send()
})
next()
})
fastify.ready(err => {
t.ok(err)
})
})
test('onResponse hook should log request error', t => {
t.plan(4)
let fastify = null
const logStream = split(JSON.parse)
try {
fastify = Fastify({
logger: {
stream: logStream,
level: 'error'
}
})
} catch (e) {
t.fail()
}
logStream.once('data', line => {
t.equal(line.msg, 'request errored')
t.equal(line.level, 50)
})
fastify.addHook('onResponse', (request, reply, next) => {
next(new Error('kaboom'))
})
fastify.get('/root', (request, reply) => {
reply.send()
})
fastify.inject('/root', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('onResponse hook should support encapsulation / 1', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('onResponse', (request, reply, next) => {
t.strictEqual(reply.plugin, true)
next()
})
instance.get('/plugin', (request, reply) => {
reply.plugin = true
reply.send()
})
next()
})
fastify.get('/root', (request, reply) => {
reply.send()
})
fastify.inject('/root', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
fastify.inject('/plugin', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('onResponse hook should support encapsulation / 2', t => {
t.plan(3)
const fastify = Fastify()
var pluginInstance
fastify.addHook('onResponse', () => {})
fastify.register((instance, opts, next) => {
instance.addHook('onResponse', () => {})
pluginInstance = instance
next()
})
fastify.ready(err => {
t.error(err)
t.is(fastify[symbols.kHooks].onResponse.length, 1)
t.is(pluginInstance[symbols.kHooks].onResponse.length, 2)
})
})
test('onResponse hook should support encapsulation / 3', t => {
t.plan(16)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('onResponse', function (request, reply, next) {
t.ok(this.hello)
t.ok('onResponse called')
next()
})
fastify.get('/first', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello2', 'world')
instance.addHook('onResponse', function (request, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
t.ok('onResponse called')
next()
})
instance.get('/second', (req, reply) => {
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('onSend hook should support encapsulation / 1', t => {
t.plan(3)
const fastify = Fastify()
var pluginInstance
fastify.addHook('onSend', () => {})
fastify.register((instance, opts, next) => {
instance.addHook('onSend', () => {})
pluginInstance = instance
next()
})
fastify.ready(err => {
t.error(err)
t.is(fastify[symbols.kHooks].onSend.length, 1)
t.is(pluginInstance[symbols.kHooks].onSend.length, 2)
})
})
test('onSend hook should support encapsulation / 2', t => {
t.plan(16)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('onSend', function (request, reply, thePayload, next) {
t.ok(this.hello)
t.ok('onSend called')
next()
})
fastify.get('/first', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello2', 'world')
instance.addHook('onSend', function (request, reply, thePayload, next) {
t.ok(this.hello)
t.ok(this.hello2)
t.ok('onSend called')
next()
})
instance.get('/second', (req, reply) => {
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('onSend hook is called after payload is serialized and headers are set', t => {
t.plan(30)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
const thePayload = { hello: 'world' }
instance.addHook('onSend', function (request, reply, payload, next) {
t.deepEqual(JSON.parse(payload), thePayload)
t.strictEqual(reply[symbols.kReplyHeaders]['content-type'], 'application/json; charset=utf-8')
next()
})
instance.get('/json', (request, reply) => {
reply.send(thePayload)
})
next()
})
fastify.register((instance, opts, next) => {
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(payload, 'some text')
t.strictEqual(reply[symbols.kReplyHeaders]['content-type'], 'text/plain; charset=utf-8')
next()
})
instance.get('/text', (request, reply) => {
reply.send('some text')
})
next()
})
fastify.register((instance, opts, next) => {
const thePayload = Buffer.from('buffer payload')
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(payload, thePayload)
t.strictEqual(reply[symbols.kReplyHeaders]['content-type'], 'application/octet-stream')
next()
})
instance.get('/buffer', (request, reply) => {
reply.send(thePayload)
})
next()
})
fastify.register((instance, opts, next) => {
var chunk = 'stream payload'
const thePayload = new stream.Readable({
read () {
this.push(chunk)
chunk = null
}
})
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(payload, thePayload)
t.strictEqual(reply[symbols.kReplyHeaders]['content-type'], 'application/octet-stream')
next()
})
instance.get('/stream', (request, reply) => {
reply.send(thePayload)
})
next()
})
fastify.register((instance, opts, next) => {
const serializedPayload = 'serialized'
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(payload, serializedPayload)
t.strictEqual(reply[symbols.kReplyHeaders]['content-type'], 'text/custom')
next()
})
instance.get('/custom-serializer', (request, reply) => {
reply
.serializer(() => serializedPayload)
.type('text/custom')
.send('needs to be serialized')
})
next()
})
fastify.inject({
method: 'GET',
url: '/json'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
t.strictEqual(res.headers['content-length'], '17')
})
fastify.inject({
method: 'GET',
url: '/text'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(res.payload, 'some text')
t.strictEqual(res.headers['content-length'], '9')
})
fastify.inject({
method: 'GET',
url: '/buffer'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(res.payload, 'buffer payload')
t.strictEqual(res.headers['content-length'], '14')
})
fastify.inject({
method: 'GET',
url: '/stream'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(res.payload, 'stream payload')
t.strictEqual(res.headers['transfer-encoding'], 'chunked')
})
fastify.inject({
method: 'GET',
url: '/custom-serializer'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(res.payload, 'serialized')
t.strictEqual(res.headers['content-type'], 'text/custom')
})
})
test('modify payload', t => {
t.plan(10)
const fastify = Fastify()
const payload = { hello: 'world' }
const modifiedPayload = { hello: 'modified' }
const anotherPayload = '"winter is coming"'
fastify.addHook('onSend', function (request, reply, thePayload, next) {
t.ok('onSend called')
t.deepEqual(JSON.parse(thePayload), payload)
thePayload = thePayload.replace('world', 'modified')
next(null, thePayload)
})
fastify.addHook('onSend', function (request, reply, thePayload, next) {
t.ok('onSend called')
t.deepEqual(JSON.parse(thePayload), modifiedPayload)
next(null, anotherPayload)
})
fastify.addHook('onSend', function (request, reply, thePayload, next) {
t.ok('onSend called')
t.strictEqual(thePayload, anotherPayload)
next()
})
fastify.get('/', (req, reply) => {
reply.send(payload)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.strictEqual(res.payload, anotherPayload)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['content-length'], '18')
})
})
test('clear payload', t => {
t.plan(6)
const fastify = Fastify()
fastify.addHook('onSend', function (request, reply, payload, next) {
t.ok('onSend called')
reply.code(304)
next(null, null)
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 304)
t.strictEqual(res.payload, '')
t.strictEqual(res.headers['content-length'], undefined)
t.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
})
})
test('onSend hook throws', t => {
t.plan(7)
const fastify = Fastify()
fastify.addHook('onSend', function (request, reply, payload, next) {
if (request.raw.method === 'DELETE') {
next(new Error('some error'))
return
}
next()
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.delete('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 500)
})
})
})
test('onSend hook should receive valid request and reply objects if onRequest hook fails', t => {
t.plan(4)
const fastify = Fastify()
fastify.decorateRequest('testDecorator', 'testDecoratorVal')
fastify.decorateReply('testDecorator', 'testDecoratorVal')
fastify.addHook('onRequest', function (req, reply, next) {
next(new Error('onRequest hook failed'))
})
fastify.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.testDecorator, 'testDecoratorVal')
t.strictEqual(reply.testDecorator, 'testDecoratorVal')
next()
})
fastify.get('/', (req, reply) => {
reply.send('hello')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 500)
})
})
test('onSend hook should receive valid request and reply objects if middleware fails', t => {
t.plan(4)
const fastify = Fastify()
fastify.decorateRequest('testDecorator', 'testDecoratorVal')
fastify.decorateReply('testDecorator', 'testDecoratorVal')
fastify.use(function (req, res, next) {
next(new Error('middlware failed'))
})
fastify.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.testDecorator, 'testDecoratorVal')
t.strictEqual(reply.testDecorator, 'testDecoratorVal')
next()
})
fastify.get('/', (req, reply) => {
reply.send('hello')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 500)
})
})
test('onSend hook should receive valid request and reply objects if a custom content type parser fails', t => {
t.plan(4)
const fastify = Fastify()
fastify.decorateRequest('testDecorator', 'testDecoratorVal')
fastify.decorateReply('testDecorator', 'testDecoratorVal')
fastify.addContentTypeParser('*', function (req, done) {
done(new Error('content type parser failed'))
})
fastify.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.testDecorator, 'testDecoratorVal')
t.strictEqual(reply.testDecorator, 'testDecoratorVal')
next()
})
fastify.get('/', (req, reply) => {
reply.send('hello')
})
fastify.inject({
method: 'POST',
url: '/',
payload: 'body'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 500)
})
})
test('cannot add hook after binding', t => {
t.plan(2)
const instance = Fastify()
instance.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
instance.listen(0, err => {
t.error(err)
t.tearDown(instance.server.close.bind(instance.server))
try {
instance.addHook('onRequest', () => {})
t.fail()
} catch (e) {
t.pass()
}
})
})
test('onRequest hooks should be able to block a request', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('onRequest', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.ok('called')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('preValidation hooks should be able to block a request', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preValidation', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('preValidation', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.ok('called')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('preParsing hooks should be able to block a request', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preParsing', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('preParsing', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.ok('called')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('preHandler hooks should be able to block a request', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preHandler', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.equal(payload, 'hello')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('onRequest hooks should be able to block a request (last hook)', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.ok('called')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('preHandler hooks should be able to block a request (last hook)', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preHandler', (req, reply, next) => {
reply.send('hello')
next()
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.equal(payload, 'hello')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.payload, 'hello')
})
})
test('onRequest respond with a stream', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
// stream.pipe(res)
// res.once('finish', next)
reply.send(stream)
})
fastify.addHook('onRequest', (req, res, next) => {
t.fail('this should not be called')
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.ok('called')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
})
})
test('preHandler respond with a stream', t => {
t.plan(7)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
t.ok('called')
next()
})
// we are calling `reply.send` inside the `preHandler` hook with a stream,
// this triggers the `onSend` hook event if `preHanlder` has not yet finished
const order = [1, 2]
fastify.addHook('preHandler', (req, reply, next) => {
const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
reply.send(stream)
reply.res.once('finish', () => {
t.is(order.shift(), 2)
next()
})
})
fastify.addHook('preHandler', (req, reply, next) => {
t.fail('this should not be called')
})
fastify.addHook('onSend', (req, reply, payload, next) => {
t.is(order.shift(), 1)
t.is(typeof payload.pipe, 'function')
next()
})
fastify.addHook('onResponse', (request, reply, next) => {
t.ok('called')
next()
})
fastify.get('/', function (request, reply) {
t.fail('we should not be here')
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
})
})
test('Register an hook after a plugin inside a plugin', t => {
t.plan(6)
const fastify = Fastify()
fastify.register(fp(function (instance, opts, next) {
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
instance.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
next()
}))
fastify.register(fp(function (instance, opts, next) {
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
next()
}))
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('Register an hook after a plugin inside a plugin (with preHandler option)', t => {
t.plan(7)
const fastify = Fastify()
fastify.register(fp(function (instance, opts, next) {
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
instance.get('/', {
preHandler: (req, reply, next) => {
t.ok('called')
next()
}
}, function (request, reply) {
reply.send({ hello: 'world' })
})
next()
}))
fastify.register(fp(function (instance, opts, next) {
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
instance.addHook('preHandler', function (req, reply, next) {
t.ok('called')
next()
})
next()
}))
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('Register hooks inside a plugin after an encapsulated plugin', t => {
t.plan(7)
const fastify = Fastify()
fastify.register(function (instance, opts, next) {
instance.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
next()
})
fastify.register(fp(function (instance, opts, next) {
instance.addHook('onRequest', function (req, reply, next) {
t.ok('called')
next()
})
instance.addHook('preHandler', function (request, reply, next) {
t.ok('called')
next()
})
instance.addHook('onSend', function (request, reply, payload, next) {
t.ok('called')
next()
})
instance.addHook('onResponse', function (request, reply, next) {
t.ok('called')
next()
})
next()
}))
fastify.inject('/', (err, res) => {
t.error(err)
t.is(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('onRequest hooks should run in the order in which they are defined', t => {
t.plan(9)
const fastify = Fastify()
fastify.register(function (instance, opts, next) {
instance.addHook('onRequest', function (req, reply, next) {
t.strictEqual(req.previous, undefined)
req.previous = 1
next()
})
instance.get('/', function (request, reply) {
t.strictEqual(request.previous, 5)
reply.send({ hello: 'world' })
})
instance.register(fp(function (i, opts, next) {
i.addHook('onRequest', function (req, reply, next) {
t.strictEqual(req.previous, 1)
req.previous = 2
next()
})
next()
}))
next()
})
fastify.register(fp(function (instance, opts, next) {
instance.addHook('onRequest', function (req, reply, next) {
t.strictEqual(req.previous, 2)
req.previous = 3
next()
})
instance.register(fp(function (i, opts, next) {
i.addHook('onRequest', function (req, reply, next) {
t.strictEqual(req.previous, 3)
req.previous = 4
next()
})
next()
}))
instance.addHook('onRequest', function (req, reply, next) {
t.strictEqual(req.previous, 4)
req.previous = 5
next()
})
next()
}))
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('preHandler hooks should run in the order in which they are defined', t => {
t.plan(9)
const fastify = Fastify()
fastify.register(function (instance, opts, next) {
instance.addHook('preHandler', function (request, reply, next) {
t.strictEqual(request.previous, undefined)
request.previous = 1
next()
})
instance.get('/', function (request, reply) {
t.strictEqual(request.previous, 5)
reply.send({ hello: 'world' })
})
instance.register(fp(function (i, opts, next) {
i.addHook('preHandler', function (request, reply, next) {
t.strictEqual(request.previous, 1)
request.previous = 2
next()
})
next()
}))
next()
})
fastify.register(fp(function (instance, opts, next) {
instance.addHook('preHandler', function (request, reply, next) {
t.strictEqual(request.previous, 2)
request.previous = 3
next()
})
instance.register(fp(function (i, opts, next) {
i.addHook('preHandler', function (request, reply, next) {
t.strictEqual(request.previous, 3)
request.previous = 4
next()
})
next()
}))
instance.addHook('preHandler', function (request, reply, next) {
t.strictEqual(request.previous, 4)
request.previous = 5
next()
})
next()
}))
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('onSend hooks should run in the order in which they are defined', t => {
t.plan(8)
const fastify = Fastify()
fastify.register(function (instance, opts, next) {
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.previous, undefined)
request.previous = 1
next()
})
instance.get('/', function (request, reply) {
reply.send({})
})
instance.register(fp(function (i, opts, next) {
i.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.previous, 1)
request.previous = 2
next()
})
next()
}))
next()
})
fastify.register(fp(function (instance, opts, next) {
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.previous, 2)
request.previous = 3
next()
})
instance.register(fp(function (i, opts, next) {
i.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.previous, 3)
request.previous = 4
next()
})
next()
}))
instance.addHook('onSend', function (request, reply, payload, next) {
t.strictEqual(request.previous, 4)
next(null, '5')
})
next()
}))
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), 5)
})
})
test('onResponse hooks should run in the order in which they are defined', t => {
t.plan(8)
const fastify = Fastify()
fastify.register(function (instance, opts, next) {
instance.addHook('onResponse', function (request, reply, next) {
t.strictEqual(reply.previous, undefined)
reply.previous = 1
next()
})
instance.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
instance.register(fp(function (i, opts, next) {
i.addHook('onResponse', function (request, reply, next) {
t.strictEqual(reply.previous, 1)
reply.previous = 2
next()
})
next()
}))
next()
})
fastify.register(fp(function (instance, opts, next) {
instance.addHook('onResponse', function (request, reply, next) {
t.strictEqual(reply.previous, 2)
reply.previous = 3
next()
})
instance.register(fp(function (i, opts, next) {
i.addHook('onResponse', function (request, reply, next) {
t.strictEqual(reply.previous, 3)
reply.previous = 4
next()
})
next()
}))
instance.addHook('onResponse', function (request, reply, next) {
t.strictEqual(reply.previous, 4)
next()
})
next()
}))
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
})
})
test('onRequest, preHandler, and onResponse hooks that resolve to a value do not cause an error', t => {
t.plan(3)
const fastify = Fastify()
fastify
.addHook('onRequest', () => Promise.resolve(1))
.addHook('onRequest', () => Promise.resolve(true))
.addHook('preValidation', () => Promise.resolve(null))
.addHook('preValidation', () => Promise.resolve('a'))
.addHook('preHandler', () => Promise.resolve(null))
.addHook('preHandler', () => Promise.resolve('a'))
.addHook('onResponse', () => Promise.resolve({}))
.addHook('onResponse', () => Promise.resolve([]))
fastify.get('/', (request, reply) => {
reply.send('hello')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.payload, 'hello')
})
})
test('If a response header has been set inside an hook it shoulod not be overwritten by the final response handler', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
reply.header('X-Custom-Header', 'hello')
next()
})
fastify.get('/', (request, reply) => {
reply.send('hello')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.headers['x-custom-header'], 'hello')
t.strictEqual(res.headers['content-type'], 'text/plain; charset=utf-8')
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.payload, 'hello')
})
})
test('If the content type has been set inside an hook it should not be changed', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onRequest', (req, reply, next) => {
reply.header('content-type', 'text/html')
next()
})
fastify.get('/', (request, reply) => {
t.ok(reply[symbols.kReplyHeaders]['content-type'])
reply.send('hello')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.headers['content-type'], 'text/html')
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.payload, 'hello')
})
})
test('request in onRequest, preParsing, preValidation and onResponse', t => {
t.plan(18)
const fastify = Fastify()
fastify.addHook('onRequest', function (request, reply, next) {
t.deepEqual(request.body, null)
t.deepEqual(request.query, { key: 'value' })
t.deepEqual(request.params, { greeting: 'hello' })
t.deepEqual(request.headers, {
'content-length': '17',
'content-type': 'application/json',
host: 'localhost:80',
'user-agent': 'lightMyRequest',
'x-custom': 'hello'
})
next()
})
fastify.addHook('preParsing', function (request, reply, next) {
t.deepEqual(request.body, null)
t.deepEqual(request.query, { key: 'value' })
t.deepEqual(request.params, { greeting: 'hello' })
t.deepEqual(request.headers, {
'content-length': '17',
'content-type': 'application/json',
host: 'localhost:80',
'user-agent': 'lightMyRequest',
'x-custom': 'hello'
})
next()
})
fastify.addHook('preValidation', function (request, reply, next) {
t.deepEqual(request.body, { hello: 'world' })
t.deepEqual(request.query, { key: 'value' })
t.deepEqual(request.params, { greeting: 'hello' })
t.deepEqual(request.headers, {
'content-length': '17',
'content-type': 'application/json',
host: 'localhost:80',
'user-agent': 'lightMyRequest',
'x-custom': 'hello'
})
next()
})
fastify.addHook('onResponse', function (request, reply, next) {
t.deepEqual(request.body, { hello: 'world' })
t.deepEqual(request.query, { key: 'value' })
t.deepEqual(request.params, { greeting: 'hello' })
t.deepEqual(request.headers, {
'content-length': '17',
'content-type': 'application/json',
host: 'localhost:80',
'user-agent': 'lightMyRequest',
'x-custom': 'hello'
})
next()
})
fastify.post('/:greeting', function (req, reply) {
reply.send('ok')
})
fastify.inject({
method: 'POST',
url: '/hello?key=value',
headers: { 'x-custom': 'hello' },
payload: { hello: 'world' }
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('preValidation hook should support encapsulation / 1', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('preValidation', (req, reply, next) => {
t.strictEqual(req.raw.url, '/plugin')
next()
})
instance.get('/plugin', (request, reply) => {
reply.send()
})
next()
})
fastify.get('/root', (request, reply) => {
reply.send()
})
fastify.inject('/root', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
fastify.inject('/plugin', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('preValidation hook should support encapsulation / 2', t => {
t.plan(3)
const fastify = Fastify()
var pluginInstance
fastify.addHook('preValidation', () => {})
fastify.register((instance, opts, next) => {
instance.addHook('preValidation', () => {})
pluginInstance = instance
next()
})
fastify.ready(err => {
t.error(err)
t.is(fastify[symbols.kHooks].preValidation.length, 1)
t.is(pluginInstance[symbols.kHooks].preValidation.length, 2)
})
})
test('preValidation hook should support encapsulation / 3', t => {
t.plan(20)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('preValidation', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
req.first = true
next()
})
fastify.decorate('hello2', 'world')
fastify.get('/first', (req, reply) => {
t.ok(req.first)
t.notOk(req.second)
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello3', 'world')
instance.addHook('preValidation', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
t.ok(this.hello3)
req.second = true
next()
})
instance.get('/second', (req, reply) => {
t.ok(req.first)
t.ok(req.second)
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('onError hook', t => {
t.plan(3)
const fastify = Fastify()
const err = new Error('kaboom')
fastify.addHook('onError', (request, reply, error, next) => {
t.match(error, err)
next()
})
fastify.get('/', (req, reply) => {
reply.send(err)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.deepEqual(JSON.parse(res.payload), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
})
})
test('reply.send should throw if called inside the onError hook', t => {
t.plan(3)
const fastify = Fastify()
const err = new Error('kaboom')
fastify.addHook('onError', (request, reply, error, next) => {
try {
reply.send()
t.fail('Should throw')
} catch (err) {
t.is(err.code, 'FST_ERR_SEND_INSIDE_ONERR')
}
next()
})
fastify.get('/', (req, reply) => {
reply.send(err)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.deepEqual(JSON.parse(res.payload), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
})
})
test('onError hook with setErrorHandler', t => {
t.test('Send error', t => {
t.plan(3)
const fastify = Fastify()
const err = new Error('ouch')
fastify.setErrorHandler((_, req, reply) => {
reply.send(err)
})
fastify.addHook('onError', (request, reply, error, next) => {
t.match(error, err)
next()
})
fastify.get('/', (req, reply) => {
reply.send(new Error('kaboom'))
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.deepEqual(JSON.parse(res.payload), {
error: 'Internal Server Error',
message: 'ouch',
statusCode: 500
})
})
})
t.test('Hide error', t => {
t.plan(2)
const fastify = Fastify()
fastify.setErrorHandler((_, req, reply) => {
reply.send({ hello: 'world' })
})
fastify.addHook('onError', (request, reply, error, next) => {
t.fail('Should not be called')
})
fastify.get('/', (req, reply) => {
reply.send(new Error('kaboom'))
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.deepEqual(
JSON.parse(res.payload),
{ hello: 'world' }
)
})
})
t.end()
})
test('preParsing hook should support encapsulation / 1', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.addHook('preParsing', (req, reply, next) => {
t.strictEqual(req.raw.url, '/plugin')
next()
})
instance.get('/plugin', (request, reply) => {
reply.send()
})
next()
})
fastify.get('/root', (request, reply) => {
reply.send()
})
fastify.inject('/root', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
fastify.inject('/plugin', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
})
})
test('preParsing hook should support encapsulation / 2', t => {
t.plan(3)
const fastify = Fastify()
var pluginInstance
fastify.addHook('preParsing', function a () {})
fastify.register((instance, opts, next) => {
instance.addHook('preParsing', function b () {})
pluginInstance = instance
next()
})
fastify.ready(err => {
t.error(err)
t.is(fastify[symbols.kHooks].preParsing.length, 1)
t.is(pluginInstance[symbols.kHooks].preParsing.length, 2)
})
})
test('preParsing hook should support encapsulation / 3', t => {
t.plan(20)
const fastify = Fastify()
fastify.decorate('hello', 'world')
fastify.addHook('preParsing', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
req.first = true
next()
})
fastify.decorate('hello2', 'world')
fastify.get('/first', (req, reply) => {
t.ok(req.first)
t.notOk(req.second)
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.decorate('hello3', 'world')
instance.addHook('preParsing', function (req, reply, next) {
t.ok(this.hello)
t.ok(this.hello2)
t.ok(this.hello3)
req.second = true
next()
})
instance.get('/second', (req, reply) => {
t.ok(req.first)
t.ok(req.second)
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})
test('preSerialization hook should run before serialization and be able to modify the payload', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preSerialization', function (req, reply, payload, next) {
payload.hello += '1'
payload.world = 'ok'
next(null, payload)
})
fastify.route({
method: 'GET',
url: '/first',
handler: function (req, reply) {
reply.send({ hello: 'world' })
},
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
},
world: {
type: 'string'
}
},
required: ['world'],
additionalProperties: false
}
}
}
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world1', world: 'ok' })
})
})
})
test('preSerialization hook should be able to throw errors which are not validated against schema response', t => {
const fastify = Fastify()
fastify.addHook('preSerialization', function (req, reply, payload, next) {
next(new Error('preSerialization aborted'))
})
fastify.route({
method: 'GET',
url: '/first',
handler: function (req, reply) {
reply.send({ hello: 'world' })
},
schema: {
response: {
500: {
type: 'object',
properties: {
world: {
type: 'string'
}
},
required: ['world'],
additionalProperties: false
}
}
}
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 500)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { error: 'Internal Server Error', message: 'preSerialization aborted', statusCode: 500 })
t.end()
})
})
})
test('preSerialization hook which returned error should still run onError hooks', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('preSerialization', function (req, reply, payload, next) {
next(new Error('preSerialization aborted'))
})
fastify.addHook('onError', function (req, reply, payload, next) {
t.pass()
next()
})
fastify.get('/first', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 500)
})
})
})
test('preSerialization hooks should run in the order in which they are defined', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('preSerialization', function (req, reply, payload, next) {
payload.hello += '2'
next(null, payload)
})
fastify.addHook('preSerialization', function (req, reply, payload, next) {
payload.hello += '1'
next(null, payload)
})
fastify.get('/first', (req, reply) => {
reply.send(payload)
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world21' })
})
})
})
test('preSerialization hooks should support encapsulation', t => {
t.plan(9)
const fastify = Fastify()
fastify.addHook('preSerialization', function (req, reply, payload, next) {
payload.hello += '1'
next(null, payload)
})
fastify.get('/first', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.register((instance, opts, next) => {
instance.addHook('preSerialization', function (req, reply, payload, next) {
payload.hello += '2'
next(null, payload)
})
instance.get('/second', (req, reply) => {
reply.send({ hello: 'world' })
})
next()
})
fastify.listen(0, err => {
t.error(err)
fastify.server.unref()
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/first'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world1' })
})
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/second'
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world12' })
})
})
})
test('onRegister hook should be called / 1', t => {
t.plan(4)
const fastify = Fastify()
const pluginOpts = { prefix: 'hello', custom: 'world' }
fastify.register((instance, opts, next) => {
next()
}, pluginOpts)
let first = true
fastify.addHook('onRegister', (instance, opts) => {
// duck typing for the win!
t.ok(instance.addHook)
if (first) {
// the first call of the onRegister is the main fastify instance, not the registered ones
first = false
} else {
t.deepEquals(opts, pluginOpts)
}
})
fastify.ready(err => {
t.error(err)
})
})
test('onRegister hook should be called / 2', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.register((instance, opts, next) => {
next()
})
next()
})
fastify.register((instance, opts, next) => {
next()
})
fastify.addHook('onRegister', instance => {
// duck typing for the win!
t.ok(instance.addHook)
})
fastify.ready(err => {
t.error(err)
})
})
test('onRegister hook should be called / 3', t => {
t.plan(4)
const fastify = Fastify()
fastify.decorate('data', [])
fastify.register((instance, opts, next) => {
instance.data.push(1)
instance.register((instance, opts, next) => {
instance.data.push(2)
t.deepEqual(instance.data, [1, 2])
next()
})
t.deepEqual(instance.data, [1])
next()
})
fastify.register((instance, opts, next) => {
t.deepEqual(instance.data, [])
next()
})
fastify.addHook('onRegister', instance => {
instance.data = instance.data.slice()
})
fastify.ready(err => {
t.error(err)
})
})
test('onRegister hook should be called / 4', t => {
t.plan(3)
const fastify = Fastify()
function plugin (instance, opts, next) {
next()
}
plugin[Symbol.for('skip-override')] = true
fastify.register(plugin)
fastify.addHook('onRegister', (instance, opts) => {
// duck typing for the win!
t.ok(instance.addHook)
t.deepEquals(opts, {})
})
fastify.ready(err => {
t.error(err)
})
})
test('early termination, onRequest', t => {
t.plan(3)
const app = Fastify()
app.addHook('onRequest', (req, reply) => {
setImmediate(() => reply.send('hello world'))
return reply
})
app.get('/', (req, reply) => {
t.fail('should not happen')
})
app.inject('/', function (err, res) {
t.error(err)
t.is(res.statusCode, 200)
t.is(res.body.toString(), 'hello world')
})
})
test('reply.send should throw if undefined error is thrown', t => {
/* eslint prefer-promise-reject-errors: ["error", {"allowEmptyReject": true}] */
t.plan(3)
const fastify = Fastify()
fastify.addHook('onRequest', function (req, reply, next) {
return Promise.reject()
})
fastify.get('/', (req, reply) => {
reply.send('hello')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 500)
t.deepEqual(JSON.parse(res.payload), {
error: 'Internal Server Error',
code: 'FST_ERR_SEND_UNDEFINED_ERR',
message: 'FST_ERR_SEND_UNDEFINED_ERR: Undefined error has occured',
statusCode: 500
})
})
})
if (semver.gt(process.versions.node, '8.0.0')) {
require('./hooks-async')(t)
} else {
t.pass('Skip because Node version < 8')
t.end()
}
test('onRoute should have compiled schemas - replace-way', t => {
t.plan(2)
const fastify = Fastify()
let routeConfigLink
fastify.addHook('onRoute', function (route) {
routeConfigLink = route
})
fastify.addSchema({
$id: 'mySchema',
type: 'object',
properties: {
host: { type: 'number' }
}
})
fastify.get('/', {
schema: { query: 'mySchema#' }
}, (req, reply) => { reply.send('hello') })
fastify.ready((err) => {
t.error(err)
t.deepEqual(routeConfigLink.schema.query, {
type: 'object',
properties: {
host: { type: 'number' }
}
})
})
})