Spaces:
Runtime error
Runtime error
| const { test } = require('node:test') | |
| const sget = require('simple-get').concat | |
| const Fastify = require('..') | |
| const split = require('split2') | |
| const pino = require('pino') | |
| const { sleep } = require('./helper') | |
| const statusCodes = require('node:http').STATUS_CODES | |
| const opts = { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| hello: { | |
| type: 'string' | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const optsWithHostnameAndPort = { | |
| schema: { | |
| response: { | |
| '2xx': { | |
| type: 'object', | |
| properties: { | |
| hello: { | |
| type: 'string' | |
| }, | |
| hostname: { | |
| type: 'string' | |
| }, | |
| port: { | |
| type: 'string' | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| test('async await', (t, done) => { | |
| t.plan(16) | |
| const fastify = Fastify() | |
| try { | |
| fastify.get('/', opts, async function awaitMyFunc (req, reply) { | |
| await sleep(200) | |
| return { hello: 'world' } | |
| }) | |
| t.assert.ok(true) | |
| } catch (e) { | |
| t.assert.fail() | |
| } | |
| try { | |
| fastify.get('/no-await', opts, async function (req, reply) { | |
| return { hello: 'world' } | |
| }) | |
| t.assert.ok(true) | |
| } catch (e) { | |
| t.assert.fail() | |
| } | |
| try { | |
| fastify.get('/await/hostname_port', optsWithHostnameAndPort, async function awaitMyFunc (req, reply) { | |
| await sleep(200) | |
| return { hello: 'world', hostname: req.hostname, port: req.port } | |
| }) | |
| t.assert.ok(true) | |
| } catch (e) { | |
| t.assert.fail() | |
| } | |
| fastify.listen({ port: 0 }, async err => { | |
| t.assert.ifError(err) | |
| t.after(() => { fastify.close() }) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + fastify.server.address().port | |
| }, (err, response, body) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(response.statusCode, 200) | |
| t.assert.strictEqual(response.headers['content-length'], '' + body.length) | |
| t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' }) | |
| }) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + fastify.server.address().port + '/no-await' | |
| }, (err, response, body) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(response.statusCode, 200) | |
| t.assert.strictEqual(response.headers['content-length'], '' + body.length) | |
| t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' }) | |
| }) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + fastify.server.address().port + '/await/hostname_port' | |
| }, (err, response, body) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(response.statusCode, 200) | |
| const parsedBody = JSON.parse(body) | |
| t.assert.strictEqual(parsedBody.hostname, 'localhost') | |
| t.assert.strictEqual(parseInt(parsedBody.port), fastify.server.address().port) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('ignore the result of the promise if reply.send is called beforehand (undefined)', (t, done) => { | |
| t.plan(4) | |
| const server = Fastify() | |
| const payload = { hello: 'world' } | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| await reply.send(payload) | |
| }) | |
| t.after(() => { server.close() }) | |
| server.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + server.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(payload, JSON.parse(body)) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('ignore the result of the promise if reply.send is called beforehand (object)', (t, done) => { | |
| t.plan(4) | |
| const server = Fastify() | |
| const payload = { hello: 'world2' } | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| await reply.send(payload) | |
| return { hello: 'world' } | |
| }) | |
| t.after(() => { server.close() }) | |
| server.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + server.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(payload, JSON.parse(body)) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('server logs an error if reply.send is called and a value is returned via async/await', (t, done) => { | |
| const lines = ['incoming request', 'request completed', 'Reply was already sent, did you forget to "return reply" in "/" (GET)?'] | |
| t.plan(lines.length + 2) | |
| const splitStream = split(JSON.parse) | |
| splitStream.on('data', (line) => { | |
| t.assert.strictEqual(line.msg, lines.shift()) | |
| }) | |
| const logger = pino(splitStream) | |
| const fastify = Fastify({ | |
| loggerInstance: logger | |
| }) | |
| fastify.get('/', async (req, reply) => { | |
| await reply.send({ hello: 'world' }) | |
| return { hello: 'world2' } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| const payload = JSON.parse(res.payload) | |
| t.assert.deepStrictEqual(payload, { hello: 'world' }) | |
| done() | |
| }) | |
| }) | |
| test('ignore the result of the promise if reply.send is called beforehand (undefined)', (t, done) => { | |
| t.plan(4) | |
| const server = Fastify() | |
| const payload = { hello: 'world' } | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| await reply.send(payload) | |
| }) | |
| t.after(() => { server.close() }) | |
| server.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + server.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(payload, JSON.parse(body)) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('ignore the result of the promise if reply.send is called beforehand (object)', (t, done) => { | |
| t.plan(4) | |
| const server = Fastify() | |
| const payload = { hello: 'world2' } | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| await reply.send(payload) | |
| return { hello: 'world' } | |
| }) | |
| t.after(() => { server.close() }) | |
| server.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + server.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.deepStrictEqual(payload, JSON.parse(body)) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('await reply if we will be calling reply.send in the future', (t, done) => { | |
| const lines = ['incoming request', 'request completed'] | |
| t.plan(lines.length + 2) | |
| const splitStream = split(JSON.parse) | |
| splitStream.on('data', (line) => { | |
| t.assert.strictEqual(line.msg, lines.shift()) | |
| }) | |
| const server = Fastify({ | |
| logger: { | |
| stream: splitStream | |
| } | |
| }) | |
| const payload = { hello: 'world' } | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| setImmediate(function () { | |
| reply.send(payload) | |
| }) | |
| await reply | |
| }) | |
| server.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| const payload = JSON.parse(res.payload) | |
| t.assert.deepStrictEqual(payload, { hello: 'world' }) | |
| done() | |
| }) | |
| }) | |
| test('await reply if we will be calling reply.send in the future (error case)', (t, done) => { | |
| const lines = ['incoming request', 'kaboom', 'request completed'] | |
| t.plan(lines.length + 2) | |
| const splitStream = split(JSON.parse) | |
| splitStream.on('data', (line) => { | |
| t.assert.strictEqual(line.msg, lines.shift()) | |
| }) | |
| const server = Fastify({ | |
| logger: { | |
| stream: splitStream | |
| } | |
| }) | |
| server.get('/', async function awaitMyFunc (req, reply) { | |
| setImmediate(function () { | |
| reply.send(new Error('kaboom')) | |
| }) | |
| await reply | |
| }) | |
| server.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 500) | |
| done() | |
| }) | |
| }) | |
| test('support reply decorators with await', (t, done) => { | |
| t.plan(2) | |
| const fastify = Fastify() | |
| fastify.decorateReply('wow', function () { | |
| setImmediate(() => { | |
| this.send({ hello: 'world' }) | |
| }) | |
| return this | |
| }) | |
| fastify.get('/', async (req, reply) => { | |
| await sleep(1) | |
| await reply.wow() | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| const payload = JSON.parse(res.payload) | |
| t.assert.deepStrictEqual(payload, { hello: 'world' }) | |
| done() | |
| }) | |
| }) | |
| test('inject async await', async t => { | |
| t.plan(1) | |
| const fastify = Fastify() | |
| fastify.get('/', (req, reply) => { | |
| reply.send({ hello: 'world' }) | |
| }) | |
| try { | |
| const res = await fastify.inject({ method: 'GET', url: '/' }) | |
| t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload)) | |
| } catch (err) { | |
| t.assert.fail(err) | |
| } | |
| }) | |
| test('inject async await - when the server equal up', async t => { | |
| t.plan(2) | |
| const fastify = Fastify() | |
| fastify.get('/', (req, reply) => { | |
| reply.send({ hello: 'world' }) | |
| }) | |
| try { | |
| const res = await fastify.inject({ method: 'GET', url: '/' }) | |
| t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload)) | |
| } catch (err) { | |
| t.assert.fail(err) | |
| } | |
| await sleep(200) | |
| try { | |
| const res2 = await fastify.inject({ method: 'GET', url: '/' }) | |
| t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res2.payload)) | |
| } catch (err) { | |
| t.assert.fail(err) | |
| } | |
| }) | |
| test('async await plugin', async t => { | |
| t.plan(1) | |
| const fastify = Fastify() | |
| fastify.register(async (fastify, opts) => { | |
| fastify.get('/', (req, reply) => { | |
| reply.send({ hello: 'world' }) | |
| }) | |
| await sleep(200) | |
| }) | |
| try { | |
| const res = await fastify.inject({ method: 'GET', url: '/' }) | |
| t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload)) | |
| } catch (err) { | |
| t.assert.fail(err) | |
| } | |
| }) | |
| test('does not call reply.send() twice if 204 response equal already sent', (t, done) => { | |
| t.plan(2) | |
| const fastify = Fastify() | |
| fastify.get('/', async (req, reply) => { | |
| reply.code(204).send() | |
| reply.send = () => { | |
| throw new Error('reply.send() was called twice') | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 204) | |
| done() | |
| }) | |
| }) | |
| test('promise was fulfilled with undefined', (t, done) => { | |
| t.plan(4) | |
| let fastify = null | |
| const stream = split(JSON.parse) | |
| try { | |
| fastify = Fastify({ | |
| logger: { | |
| stream, | |
| level: 'error' | |
| } | |
| }) | |
| } catch (e) { | |
| t.assert.fail() | |
| } | |
| t.after(() => { fastify.close() }) | |
| fastify.get('/', async (req, reply) => { | |
| }) | |
| stream.once('data', line => { | |
| t.assert.fail('should not log an error') | |
| }) | |
| fastify.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| t.after(() => { fastify.close() }) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + fastify.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.body, undefined) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('promise was fulfilled with undefined using inject', async (t) => { | |
| const stream = split(JSON.parse) | |
| const fastify = Fastify({ | |
| logger: { | |
| stream, | |
| level: 'error' | |
| } | |
| }) | |
| fastify.get('/', async (req, reply) => { | |
| }) | |
| stream.once('data', line => { | |
| t.assert.fail('should not log an error') | |
| }) | |
| const res = await fastify.inject('/') | |
| t.assert.strictEqual(res.body, '') | |
| t.assert.strictEqual(res.statusCode, 200) | |
| }) | |
| test('error is not logged because promise was fulfilled with undefined but response was sent before promise resolution', (t, done) => { | |
| t.plan(4) | |
| let fastify = null | |
| const stream = split(JSON.parse) | |
| const payload = { hello: 'world' } | |
| try { | |
| fastify = Fastify({ | |
| logger: { | |
| stream, | |
| level: 'error' | |
| } | |
| }) | |
| } catch (e) { | |
| t.assert.fail() | |
| } | |
| t.after(() => { fastify.close() }) | |
| fastify.get('/', async (req, reply) => { | |
| reply.send(payload) | |
| }) | |
| stream.once('data', line => { | |
| t.assert.fail('should not log an error') | |
| }) | |
| fastify.listen({ port: 0 }, (err) => { | |
| t.assert.ifError(err) | |
| t.after(() => { fastify.close() }) | |
| sget({ | |
| method: 'GET', | |
| url: 'http://localhost:' + fastify.server.address().port + '/' | |
| }, (err, res, body) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| t.assert.deepStrictEqual( | |
| payload, | |
| JSON.parse(body) | |
| ) | |
| done() | |
| }) | |
| }) | |
| }) | |
| test('Thrown Error instance sets HTTP status code', (t, done) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| const err = new Error('winter is coming') | |
| err.statusCode = 418 | |
| fastify.get('/', async (req, reply) => { | |
| throw err | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (error, res) => { | |
| t.assert.ifError(error) | |
| t.assert.strictEqual(res.statusCode, 418) | |
| t.assert.deepStrictEqual( | |
| { | |
| error: statusCodes['418'], | |
| message: err.message, | |
| statusCode: 418 | |
| }, | |
| JSON.parse(res.payload) | |
| ) | |
| done() | |
| }) | |
| }) | |
| test('customErrorHandler support', (t, done) => { | |
| t.plan(4) | |
| const fastify = Fastify() | |
| fastify.get('/', async (req, reply) => { | |
| const error = new Error('ouch') | |
| error.statusCode = 400 | |
| throw error | |
| }) | |
| fastify.setErrorHandler(async err => { | |
| t.assert.strictEqual(err.message, 'ouch') | |
| const error = new Error('kaboom') | |
| error.statusCode = 401 | |
| throw error | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 401) | |
| t.assert.deepStrictEqual( | |
| { | |
| error: statusCodes['401'], | |
| message: 'kaboom', | |
| statusCode: 401 | |
| }, | |
| JSON.parse(res.payload) | |
| ) | |
| done() | |
| }) | |
| }) | |
| test('customErrorHandler support without throwing', (t, done) => { | |
| t.plan(4) | |
| const fastify = Fastify() | |
| fastify.get('/', async (req, reply) => { | |
| const error = new Error('ouch') | |
| error.statusCode = 400 | |
| throw error | |
| }) | |
| fastify.setErrorHandler(async (err, req, reply) => { | |
| t.assert.strictEqual(err.message, 'ouch') | |
| await reply.code(401).send('kaboom') | |
| reply.send = t.assert.fail.bind(t, 'should not be called') | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 401) | |
| t.assert.deepStrictEqual( | |
| 'kaboom', | |
| res.payload | |
| ) | |
| done() | |
| }) | |
| }) | |
| // See https://github.com/fastify/fastify/issues/2653 | |
| test('customErrorHandler only called if reply not already sent', (t, done) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/', async (req, reply) => { | |
| await reply.send('success') | |
| const error = new Error('ouch') | |
| error.statusCode = 400 | |
| throw error | |
| }) | |
| fastify.setErrorHandler(t.assert.fail.bind(t, 'should not be called')) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 200) | |
| t.assert.deepStrictEqual( | |
| 'success', | |
| res.payload | |
| ) | |
| done() | |
| }) | |
| }) | |
| // See https://github.com/fastify/fastify/issues/3209 | |
| test('setNotFoundHandler should accept return value', (t, done) => { | |
| t.plan(3) | |
| const fastify = Fastify() | |
| fastify.get('/', async () => ({ hello: 'world' })) | |
| fastify.setNotFoundHandler((req, reply) => { | |
| reply.code(404) | |
| return { | |
| error: statusCodes['404'], | |
| message: 'lost', | |
| statusCode: 404 | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/elsewhere' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 404) | |
| t.assert.deepStrictEqual( | |
| { | |
| error: statusCodes['404'], | |
| message: 'lost', | |
| statusCode: 404 | |
| }, | |
| JSON.parse(res.payload) | |
| ) | |
| done() | |
| }) | |
| }) | |
| // See https://github.com/fastify/fastify/issues/3209 | |
| test('customErrorHandler should accept return value', (t, done) => { | |
| t.plan(4) | |
| const fastify = Fastify() | |
| fastify.get('/', async (req, reply) => { | |
| const error = new Error('ouch') | |
| error.statusCode = 400 | |
| throw error | |
| }) | |
| fastify.setErrorHandler((err, req, reply) => { | |
| t.assert.strictEqual(err.message, 'ouch') | |
| reply.code(401) | |
| return { | |
| error: statusCodes['401'], | |
| message: 'kaboom', | |
| statusCode: 401 | |
| } | |
| }) | |
| fastify.inject({ | |
| method: 'GET', | |
| url: '/' | |
| }, (err, res) => { | |
| t.assert.ifError(err) | |
| t.assert.strictEqual(res.statusCode, 401) | |
| t.assert.deepStrictEqual( | |
| { | |
| error: statusCodes['401'], | |
| message: 'kaboom', | |
| statusCode: 401 | |
| }, | |
| JSON.parse(res.payload) | |
| ) | |
| done() | |
| }) | |
| }) | |
| test('await self', async t => { | |
| const app = Fastify() | |
| t.assert.strictEqual(await app, app) | |
| }) | |