fastify
Version:
Fast and low overhead web framework, for Node.js
294 lines (254 loc) • 6.7 kB
JavaScript
const t = require('tap')
const test = t.test
const net = require('net')
const Fastify = require('..')
const statusCodes = require('http').STATUS_CODES
const boom = require('boom')
const codes = Object.keys(statusCodes)
codes.forEach(code => {
if (Number(code) >= 400) helper(code)
})
function helper (code) {
test('Reply error handling - code: ' + code, t => {
t.plan(3)
const fastify = Fastify()
const err = new Error('winter is coming')
fastify.get('/', (req, reply) => {
reply
.code(Number(code))
.send(err)
})
fastify.inject({
method: 'GET',
url: '/'
}, res => {
t.strictEqual(res.statusCode, Number(code))
t.equal(res.headers['content-type'], 'application/json')
t.deepEqual(
{
error: statusCodes[code],
message: err.message,
statusCode: Number(code)
},
JSON.parse(res.payload)
)
})
})
}
test('preHandler hook error handling with external code', t => {
t.plan(2)
const fastify = Fastify()
const err = new Error('winter is coming')
fastify.addHook('preHandler', (req, reply, done) => {
reply.code(400)
done(err)
})
fastify.get('/', () => {})
fastify.inject({
method: 'GET',
url: '/'
}, res => {
t.strictEqual(res.statusCode, 400)
t.deepEqual(
{
error: statusCodes['400'],
message: err.message,
statusCode: 400
},
JSON.parse(res.payload)
)
})
})
test('onRequest hook error handling with external done', t => {
t.plan(2)
const fastify = Fastify()
const err = new Error('winter is coming')
fastify.addHook('onRequest', (req, res, done) => {
res.statusCode = 400
done(err)
})
fastify.get('/', () => {})
fastify.inject({
method: 'GET',
url: '/'
}, res => {
t.strictEqual(res.statusCode, 400)
t.deepEqual(
{
error: statusCodes['400'],
message: err.message,
statusCode: 400
},
JSON.parse(res.payload)
)
})
})
test('support for boom', t => {
t.plan(3)
const fastify = Fastify()
fastify.get('/500', (req, reply) => {
reply.send(boom.create(500, 'winter is coming', { hello: 'world' }))
})
fastify.get('/400', (req, reply) => {
reply.send(boom.create(400, 'winter is coming', { hello: 'world' }))
})
fastify.get('/401', (req, reply) => {
reply.send(boom.unauthorized('invalid password', 'sample'))
})
t.test('500', t => {
t.plan(3)
fastify.inject({
method: 'GET',
url: '/500'
}, res => {
t.strictEqual(res.statusCode, 500)
t.strictEqual(res.headers['content-type'], 'application/json')
t.deepEqual(
// we are doing this because Boom creates also a stack that is stripped during the stringify phase
JSON.parse(res.payload),
{
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred'
}
)
})
})
t.test('400', t => {
t.plan(3)
fastify.inject({
method: 'GET',
url: '/400'
}, res => {
t.strictEqual(res.statusCode, 400)
t.strictEqual(res.headers['content-type'], 'application/json')
t.deepEqual(
// we are doing this because Boom creates also a stack that is stripped during the stringify phase
JSON.parse(res.payload),
{
statusCode: 400,
error: 'Bad Request',
message: 'winter is coming'
}
)
})
})
t.test('401', t => {
t.plan(4)
fastify.inject({
method: 'GET',
url: '/401'
}, res => {
t.strictEqual(res.statusCode, 401)
t.strictEqual(res.headers['content-type'], 'application/json')
t.deepEqual(
// we are doing this because Boom creates also a stack that is stripped during the stringify phase
JSON.parse(res.payload),
{
statusCode: 401,
error: 'Unauthorized',
message: 'invalid password',
attributes: {
error: 'invalid password'
}
}
)
t.deepEqual(res.headers['www-authenticate'], 'sample error="invalid password"')
})
})
})
test('extendServerError should exist', t => {
t.plan(2)
const fastify = Fastify()
t.ok(fastify.extendServerError)
t.is(typeof fastify.extendServerError, 'function')
})
test('extend server error - encapsulation', t => {
t.plan(9)
const fastify = Fastify()
const err = new Error('error')
const date = new Date()
fastify.get('/', (req, reply) => {
t.notOk(reply._extendServerError)
reply.send(err)
})
fastify.register((instance, opts, next) => {
instance.extendServerError((payloadError) => {
t.ok(payloadError instanceof Error)
t.strictEqual(payloadError.name, err.name)
t.strictEqual(payloadError.message, err.message)
return {
timestamp: date
}
})
instance.get('/encapsulated', (req, reply) => {
t.ok(reply._extendServerError)
reply.send(err)
})
next()
})
fastify.inject({
method: 'GET',
url: '/'
}, res => {
t.strictEqual(res.statusCode, 500)
t.deepEqual(
{
error: statusCodes['500'],
message: err.message,
statusCode: 500
},
JSON.parse(res.payload)
)
})
fastify.inject({
method: 'GET',
url: '/encapsulated'
}, res => {
t.strictEqual(res.statusCode, 500)
t.deepEqual(
{
error: statusCodes['500'],
message: err.message,
statusCode: 500,
timestamp: date.toISOString()
},
JSON.parse(res.payload)
)
})
})
test('extend server error - should throw if the argument is not a function', t => {
t.plan(1)
const fastify = Fastify()
try {
fastify.extendServerError(null)
t.fail()
} catch (e) {
t.is(e.message, 'The server error object must be a function')
}
})
if (Number(process.versions.node[0]) >= 6) {
test('Should reply 400 on client error', t => {
t.plan(2)
const fastify = Fastify()
fastify.listen(0, err => {
t.error(err)
const client = net.connect(fastify.server.address().port)
client.end('oooops!')
var chunks = ''
client.on('data', chunk => {
chunks += chunk
})
client.once('end', () => {
const body = JSON.stringify({
error: 'Bad Request',
message: 'Client Error',
statusCode: 400
})
t.equal(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`, chunks)
fastify.close()
})
})
})
}