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.
572 lines
14 KiB
572 lines
14 KiB
4 years ago
|
'use strict'
|
||
|
|
||
|
const split = require('split2')
|
||
|
const sget = require('simple-get').concat
|
||
|
const Fastify = require('..')
|
||
|
const fs = require('fs')
|
||
|
const { promisify } = require('util')
|
||
|
const sleep = promisify(setTimeout)
|
||
|
|
||
|
function asyncHookTest (t) {
|
||
|
const test = t.test
|
||
|
test('async hooks', t => {
|
||
|
t.plan(21)
|
||
|
|
||
|
const fastify = Fastify()
|
||
|
fastify.addHook('onRequest', async function (request, reply) {
|
||
|
await sleep(1)
|
||
|
request.test = 'the request is coming'
|
||
|
reply.test = 'the reply has come'
|
||
|
if (request.raw.method === 'DELETE') {
|
||
|
throw new Error('some error')
|
||
|
}
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async function (request, reply) {
|
||
|
await sleep(1)
|
||
|
t.is(request.test, 'the request is coming')
|
||
|
t.is(reply.test, 'the reply has come')
|
||
|
if (request.raw.method === 'HEAD') {
|
||
|
throw new Error('some error')
|
||
|
}
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async function (request, reply, payload) {
|
||
|
await sleep(1)
|
||
|
t.ok('onSend called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async function (request, reply) {
|
||
|
await sleep(1)
|
||
|
t.ok('onResponse called')
|
||
|
})
|
||
|
|
||
|
fastify.get('/', function (request, reply) {
|
||
|
t.is(request.test, 'the request is coming')
|
||
|
t.is(reply.test, 'the reply has come')
|
||
|
reply.code(200).send({ hello: 'world' })
|
||
|
})
|
||
|
|
||
|
fastify.head('/', function (req, reply) {
|
||
|
reply.code(200).send({ hello: 'world' })
|
||
|
})
|
||
|
|
||
|
fastify.delete('/', function (req, reply) {
|
||
|
reply.code(200).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: '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('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', async function (request, reply, thePayload) {
|
||
|
t.ok('onSend called')
|
||
|
t.deepEqual(JSON.parse(thePayload), payload)
|
||
|
return thePayload.replace('world', 'modified')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async function (request, reply, thePayload) {
|
||
|
t.ok('onSend called')
|
||
|
t.deepEqual(JSON.parse(thePayload), modifiedPayload)
|
||
|
return anotherPayload
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async function (request, reply, thePayload) {
|
||
|
t.ok('onSend called')
|
||
|
t.strictEqual(thePayload, anotherPayload)
|
||
|
})
|
||
|
|
||
|
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('onRequest hooks should be able to block a request', t => {
|
||
|
t.plan(5)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('onRequest', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onRequest', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.equal(payload, 'hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preValidation', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.equal(payload, 'hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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 with async onSend', t => {
|
||
|
t.plan(5)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('preValidation', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
await sleep(10)
|
||
|
t.equal(payload, 'hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (request, reply) => {
|
||
|
t.fail('we should not be here')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
fastify.post('/', {
|
||
|
schema: {
|
||
|
body: {
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
hello: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
},
|
||
|
required: ['hello']
|
||
|
}
|
||
|
}
|
||
|
}, function (request, reply) {
|
||
|
t.fail('we should not be here')
|
||
|
})
|
||
|
|
||
|
fastify.inject({
|
||
|
url: '/',
|
||
|
method: 'POST'
|
||
|
}, (err, res) => {
|
||
|
t.error(err)
|
||
|
t.is(res.statusCode, 200)
|
||
|
t.is(res.payload, 'hello')
|
||
|
})
|
||
|
})
|
||
|
|
||
|
test('preSerialization hooks should be able to modify the payload', t => {
|
||
|
t.plan(3)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('preSerialization', async (req, reply, payload) => {
|
||
|
return { hello: 'another world' }
|
||
|
})
|
||
|
|
||
|
fastify.get('/', function (request, reply) {
|
||
|
reply.send({ hello: 'world' })
|
||
|
})
|
||
|
|
||
|
fastify.inject({
|
||
|
url: '/',
|
||
|
method: 'GET'
|
||
|
}, (err, res) => {
|
||
|
t.error(err)
|
||
|
t.is(res.statusCode, 200)
|
||
|
t.deepEqual(JSON.parse(res.payload), { hello: 'another world' })
|
||
|
})
|
||
|
})
|
||
|
|
||
|
test('preSerialization hooks should handle errors', t => {
|
||
|
t.plan(3)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('preSerialization', async (req, reply, payload) => {
|
||
|
throw new Error('kaboom')
|
||
|
})
|
||
|
|
||
|
fastify.get('/', function (request, reply) {
|
||
|
reply.send({ hello: 'world' })
|
||
|
})
|
||
|
|
||
|
fastify.inject({
|
||
|
url: '/',
|
||
|
method: 'GET'
|
||
|
}, (err, res) => {
|
||
|
t.error(err)
|
||
|
t.is(res.statusCode, 500)
|
||
|
t.deepEqual(JSON.parse(res.payload), { error: 'Internal Server Error', message: 'kaboom', statusCode: 500 })
|
||
|
})
|
||
|
})
|
||
|
|
||
|
test('onRequest hooks should be able to block a request (last hook)', t => {
|
||
|
t.plan(5)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('onRequest', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.equal(payload, 'hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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) with a delay in onSend', t => {
|
||
|
t.plan(5)
|
||
|
const fastify = Fastify()
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
reply.send('hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
await sleep(10)
|
||
|
t.equal(payload, 'hello')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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', async (req, reply) => {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
|
||
|
// stream.pipe(res)
|
||
|
// res.once('finish', resolve)
|
||
|
reply.send(stream)
|
||
|
reply.res.once('finish', () => resolve())
|
||
|
})
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onRequest', async (req, res) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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', async (req, res) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
// 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', async (req, reply) => {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
|
||
|
reply.send(stream)
|
||
|
reply.res.once('finish', () => {
|
||
|
t.is(order.shift(), 2)
|
||
|
resolve()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
fastify.addHook('preHandler', async (req, reply) => {
|
||
|
t.fail('this should not be called')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload) => {
|
||
|
t.is(order.shift(), 1)
|
||
|
t.is(typeof payload.pipe, 'function')
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onResponse', async (request, reply) => {
|
||
|
t.ok('called')
|
||
|
})
|
||
|
|
||
|
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('Should log a warning if is an async function with `next`', t => {
|
||
|
t.test('3 arguments', t => {
|
||
|
t.plan(3)
|
||
|
const stream = split(JSON.parse)
|
||
|
const fastify = Fastify({
|
||
|
logger: { stream }
|
||
|
})
|
||
|
|
||
|
stream.on('data', line => {
|
||
|
t.strictEqual(line.level, 40)
|
||
|
t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
||
|
t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onRequest', async (req, reply, next) => {})
|
||
|
})
|
||
|
|
||
|
t.test('4 arguments', t => {
|
||
|
t.plan(9)
|
||
|
const stream = split(JSON.parse)
|
||
|
const fastify = Fastify({
|
||
|
logger: { stream }
|
||
|
})
|
||
|
|
||
|
stream.on('data', line => {
|
||
|
t.strictEqual(line.level, 40)
|
||
|
t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
||
|
t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
||
|
})
|
||
|
|
||
|
fastify.addHook('onSend', async (req, reply, payload, next) => {})
|
||
|
fastify.addHook('preSerialization', async (req, reply, payload, next) => {})
|
||
|
fastify.addHook('onError', async (req, reply, error, next) => {})
|
||
|
})
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.test('early termination, onRequest async', async t => {
|
||
|
t.plan(2)
|
||
|
|
||
|
const app = Fastify()
|
||
|
|
||
|
app.addHook('onRequest', async (req, reply) => {
|
||
|
setImmediate(() => reply.send('hello world'))
|
||
|
return reply
|
||
|
})
|
||
|
|
||
|
app.get('/', (req, reply) => {
|
||
|
t.fail('should not happen')
|
||
|
})
|
||
|
|
||
|
const res = await app.inject('/')
|
||
|
t.is(res.statusCode, 200)
|
||
|
t.is(res.body.toString(), 'hello world')
|
||
|
})
|
||
|
}
|
||
|
|
||
|
module.exports = asyncHookTest
|