Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,15 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const symbols = require('../lib/symbols.js')
const { FastifyError } = require('@fastify/error')
test('default 500', t => {
test('default 500', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
@@ -19,30 +19,119 @@ test('default 500', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
done()
})
})
test('custom 500', t => {
test('default 500 with non-error string', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
throw 'kaboom' // eslint-disable-line no-throw-literal
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain; charset=utf-8')
t.assert.deepStrictEqual(res.payload, 'kaboom')
done()
})
})
test('default 500 with non-error symbol', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
throw Symbol('error')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(res.payload, '')
done()
})
})
test('default 500 with non-error false', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
throw false // eslint-disable-line no-throw-literal
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(res.payload, 'false')
done()
})
})
test('default 500 with non-error null', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
throw null // eslint-disable-line no-throw-literal
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(res.payload, 'null')
done()
})
})
test('custom 500', (t, done) => {
t.plan(6)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
fastify.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
t.assert.ok(typeof request === 'object')
t.assert.ok(request instanceof fastify[symbols.kRequest].parent)
reply
.code(500)
.type('text/plain')
@@ -53,18 +142,19 @@ test('custom 500', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.equal(res.headers['content-type'], 'text/plain')
t.same(res.payload.toString(), 'an error happened: kaboom')
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload.toString(), 'an error happened: kaboom')
done()
})
})
test('encapsulated 500', t => {
t.plan(10)
test('encapsulated 500', async t => {
t.plan(8)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
@@ -76,8 +166,8 @@ test('encapsulated 500', t => {
})
f.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
t.assert.ok(typeof request === 'object')
t.assert.ok(request instanceof fastify[symbols.kRequest].parent)
reply
.code(500)
.type('text/plain')
@@ -87,36 +177,38 @@ test('encapsulated 500', t => {
done()
}, { prefix: 'test' })
fastify.inject({
method: 'GET',
url: '/test'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.equal(res.headers['content-type'], 'text/plain')
t.same(res.payload.toString(), 'an error happened: kaboom')
})
{
const response = await fastify.inject({
method: 'GET',
url: '/test'
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
t.assert.strictEqual(response.statusCode, 500)
t.assert.strictEqual(response.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(response.payload.toString(), 'an error happened: kaboom')
}
{
const response = await fastify.inject({
method: 'GET',
url: '/'
})
t.assert.strictEqual(response.statusCode, 500)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(response.payload), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
})
}
})
test('custom 500 with hooks', t => {
test('custom 500 with hooks', (t, done) => {
t.plan(7)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
@@ -130,15 +222,15 @@ test('custom 500 with hooks', t => {
})
fastify.addHook('onSend', (req, res, payload, done) => {
t.ok('called', 'onSend')
t.assert.ok('called', 'onSend')
done()
})
fastify.addHook('onRequest', (req, res, done) => {
t.ok('called', 'onRequest')
t.assert.ok('called', 'onRequest')
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
t.ok('called', 'onResponse')
t.assert.ok('called', 'onResponse')
done()
})
@@ -146,54 +238,59 @@ test('custom 500 with hooks', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.equal(res.headers['content-type'], 'text/plain')
t.same(res.payload.toString(), 'an error happened: kaboom')
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload.toString(), 'an error happened: kaboom')
done()
})
})
test('cannot set errorHandler after binding', t => {
test('cannot set errorHandler after binding', (t, done) => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
try {
fastify.setErrorHandler(() => { })
t.fail()
t.assert.fail()
} catch (e) {
t.pass()
t.assert.ok(true)
} finally {
done()
}
})
})
test('cannot set childLoggerFactory after binding', t => {
test('cannot set childLoggerFactory after binding', (t, done) => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
try {
fastify.setChildLoggerFactory(() => { })
t.fail()
t.assert.fail()
} catch (e) {
t.pass()
t.assert.ok(true)
} finally {
done()
}
})
})
test('catch synchronous errors', t => {
test('catch synchronous errors', (t, done) => {
t.plan(3)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.setErrorHandler((_, req, reply) => {
throw new Error('kaboom2')
@@ -211,12 +308,115 @@ test('catch synchronous errors', t => {
},
payload: JSON.stringify({ hello: 'world' }).substring(0, 5)
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.same(res.json(), {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.deepStrictEqual(res.json(), {
error: 'Internal Server Error',
message: 'kaboom2',
statusCode: 500
})
done()
})
})
test('custom 500 with non-error and custom errorHandler', (t, done) => {
t.plan(6)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/', function (req, reply) {
throw 'kaboom' // eslint-disable-line no-throw-literal
})
fastify.setErrorHandler(function (err, request, reply) {
t.assert.ok(typeof request === 'object')
t.assert.ok(request instanceof fastify[symbols.kRequest].parent)
reply
.code(500)
.type('text/plain')
.send('an error happened: ' + err.message)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload, 'an error happened: undefined')
done()
})
})
test('custom 500 with FastifyError detection', (t, done) => {
t.plan(18)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.get('/string', function (req, reply) {
throw 'kaboom' // eslint-disable-line no-throw-literal
})
fastify.get('/native-error', function (req, reply) {
throw new Error('kaboom')
})
fastify.get('/fastify-error', function (req, reply) {
throw new FastifyError('kaboom')
})
fastify.setErrorHandler(function (err, request, reply) {
t.assert.ok(typeof request === 'object')
t.assert.ok(request instanceof fastify[symbols.kRequest].parent)
if (err instanceof FastifyError) {
reply
.code(500)
.type('text/plain')
.send('FastifyError thrown')
} else if (err instanceof Error) {
reply
.code(500)
.type('text/plain')
.send('Error thrown')
} else {
reply
.code(500)
.type('text/plain')
.send('Primitive thrown')
}
})
fastify.inject({
method: 'GET',
url: '/string'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload, 'Primitive thrown')
fastify.inject({
method: 'GET',
url: '/native-error'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload, 'Error thrown')
fastify.inject({
method: 'GET',
url: '/fastify-error'
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.headers['content-type'], 'text/plain')
t.assert.deepStrictEqual(res.payload, 'FastifyError thrown')
done()
})
})
})
})

View File

@@ -0,0 +1,92 @@
'use strict'
const { test } = require('node:test')
const Fastify = require('..')
test('allow unsafe regex', async t => {
t.plan(2)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.after(() => fastify.close())
fastify.get('/:foo(^[0-9]*$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
await fastify.listen({ port: 0 })
const result = await fetch(`http://localhost:${fastify.server.address().port}/1234`)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { foo: '1234' })
})
test('allow unsafe regex not match', async t => {
t.plan(1)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.after(() => fastify.close())
fastify.get('/:foo(^[0-9]*$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
await fastify.listen({ port: 0 })
const result = await fetch(`http://localhost:${fastify.server.address().port}/a1234`)
t.assert.strictEqual(result.status, 404)
})
test('allow unsafe regex not safe', (t, done) => {
t.plan(1)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.after(() => fastify.close())
t.assert.throws(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
done()
})
test('allow unsafe regex not safe by default', (t, done) => {
t.plan(1)
const fastify = Fastify()
t.after(() => fastify.close())
t.assert.throws(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
done()
})
test('allow unsafe regex allow unsafe', async t => {
t.plan(3)
const fastify = Fastify({
allowUnsafeRegex: true
})
t.after(() => fastify.close())
t.assert.doesNotThrow(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
await fastify.listen({ port: 0 })
const result = await fetch(`http://localhost:${fastify.server.address().port}/1234`)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { foo: '1234' })
})

View File

@@ -1,117 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const sget = require('simple-get').concat
test('allow unsafe regex', t => {
t.plan(4)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/:foo(^[0-9]*$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/1234'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), {
foo: '1234'
})
})
})
})
test('allow unsafe regex not match', t => {
t.plan(3)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/:foo(^[0-9]*$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/a1234'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 404)
})
})
})
test('allow unsafe regex not safe', t => {
t.plan(1)
const fastify = Fastify({
allowUnsafeRegex: false
})
t.teardown(fastify.close.bind(fastify))
t.throws(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
})
test('allow unsafe regex not safe by default', t => {
t.plan(1)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.throws(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
})
test('allow unsafe regex allow unsafe', t => {
t.plan(5)
const fastify = Fastify({
allowUnsafeRegex: true
})
t.teardown(fastify.close.bind(fastify))
t.doesNotThrow(() => {
fastify.get('/:foo(^([0-9]+){4}$)', (req, reply) => {
reply.send({ foo: req.params.foo })
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/1234'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), {
foo: '1234'
})
})
})
})

View File

@@ -1,74 +1,65 @@
'use strict'
const { AsyncLocalStorage } = require('node:async_hooks')
const t = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
const sget = require('simple-get').concat
if (!AsyncLocalStorage) {
t.skip('AsyncLocalStorage not available, skipping test')
process.exit(0)
}
test('Async Local Storage test', async (t) => {
t.plan(12)
if (!AsyncLocalStorage) {
t.skip('AsyncLocalStorage not available, skipping test')
process.exit(0)
}
const storage = new AsyncLocalStorage()
const app = Fastify({ logger: false })
const storage = new AsyncLocalStorage()
const app = Fastify({ logger: false })
let counter = 0
app.addHook('onRequest', (req, reply, next) => {
const id = counter++
storage.run({ id }, next)
})
t.after(() => app.close())
app.get('/', function (request, reply) {
t.ok(storage.getStore())
const id = storage.getStore().id
reply.send({ id })
})
app.post('/', function (request, reply) {
t.ok(storage.getStore())
const id = storage.getStore().id
reply.send({ id })
})
app.listen({ port: 0 }, function (err, address) {
t.error(err)
sget({
method: 'POST',
url: 'http://localhost:' + app.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { id: 0 })
sget({
method: 'POST',
url: 'http://localhost:' + app.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { id: 1 })
sget({
method: 'GET',
url: 'http://localhost:' + app.server.address().port,
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { id: 2 })
app.close()
t.end()
})
})
let counter = 0
app.addHook('onRequest', (req, reply, next) => {
const id = counter++
storage.run({ id }, next)
})
app.get('/', function (request, reply) {
t.assert.ok(storage.getStore())
const id = storage.getStore().id
reply.send({ id })
})
app.post('/', function (request, reply) {
t.assert.ok(storage.getStore())
const id = storage.getStore().id
reply.send({ id })
})
const fastifyServer = await app.listen({ port: 0 })
// First POST request
const result1 = await fetch(fastifyServer, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.deepStrictEqual(await result1.json(), { id: 0 })
const result2 = await fetch(fastifyServer, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.deepStrictEqual(await result2.json(), { id: 1 })
// GET request
const result3 = await fetch(fastifyServer, {
method: 'GET'
})
t.assert.ok(result3.ok)
t.assert.strictEqual(result3.status, 200)
t.assert.deepStrictEqual(await result3.json(), { id: 2 })
})

View File

@@ -1,8 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('..')
const split = require('split2')
const pino = require('pino')
@@ -24,56 +22,89 @@ const opts = {
}
}
test('async await', t => {
t.plan(11)
const optsWithHostnameAndPort = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
},
hostname: {
type: 'string'
},
port: {
type: 'string'
}
}
}
}
}
}
test('async await', async t => {
t.plan(15)
const fastify = Fastify()
try {
fastify.get('/', opts, async function awaitMyFunc (req, reply) {
await sleep(200)
return { hello: 'world' }
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
try {
fastify.get('/no-await', opts, async function (req, reply) {
return { hello: 'world' }
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
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()
}
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/no-await'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
})
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const result = await fetch(fastifyServer)
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
const result1 = await fetch(`${fastifyServer}/no-await`)
const body1 = await result1.text()
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.strictEqual(result1.headers.get('content-length'), '' + body1.length)
t.assert.deepStrictEqual(JSON.parse(body1), { hello: 'world' })
const result2 = await fetch(`http://localhost:${fastify.server.address().port}/await/hostname_port`)
const parsedBody = await result2.json()
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.strictEqual(parsedBody.hostname, 'localhost')
t.assert.strictEqual(parseInt(parsedBody.port), fastify.server.address().port)
})
test('ignore the result of the promise if reply.send is called beforehand (undefined)', t => {
t.plan(4)
test('ignore the result of the promise if reply.send is called beforehand (undefined)', async (t) => {
t.plan(3)
const server = Fastify()
const payload = { hello: 'world' }
@@ -82,23 +113,19 @@ test('ignore the result of the promise if reply.send is called beforehand (undef
await reply.send(payload)
})
t.teardown(server.close.bind(server))
t.after(() => { server.close() })
server.listen({ port: 0 }, (err) => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + server.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.same(payload, JSON.parse(body))
t.equal(res.statusCode, 200)
})
})
const fastifyServer = await server.listen({ port: 0 })
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.deepStrictEqual(payload, await result.json())
t.assert.strictEqual(result.status, 200)
})
test('ignore the result of the promise if reply.send is called beforehand (object)', t => {
t.plan(4)
test('ignore the result of the promise if reply.send is called beforehand (object)', async (t) => {
t.plan(3)
const server = Fastify()
const payload = { hello: 'world2' }
@@ -108,34 +135,30 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
return { hello: 'world' }
})
t.teardown(server.close.bind(server))
t.after(() => { server.close() })
server.listen({ port: 0 }, (err) => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + server.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.same(payload, JSON.parse(body))
t.equal(res.statusCode, 200)
})
})
const fastifyServer = await server.listen({ port: 0 })
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.deepStrictEqual(payload, await result.json())
t.assert.strictEqual(result.status, 200)
})
test('server logs an error if reply.send is called and a value is returned via async/await', t => {
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.equal(line.msg, lines.shift())
t.assert.strictEqual(line.msg, lines.shift())
})
const logger = pino(splitStream)
const fastify = Fastify({
logger
loggerInstance: logger
})
fastify.get('/', async (req, reply) => {
@@ -147,14 +170,15 @@ test('server logs an error if reply.send is called and a value is returned via a
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
t.assert.deepStrictEqual(payload, { hello: 'world' })
done()
})
})
test('ignore the result of the promise if reply.send is called beforehand (undefined)', t => {
t.plan(4)
test('ignore the result of the promise if reply.send is called beforehand (undefined)', async (t) => {
t.plan(3)
const server = Fastify()
const payload = { hello: 'world' }
@@ -163,23 +187,19 @@ test('ignore the result of the promise if reply.send is called beforehand (undef
await reply.send(payload)
})
t.teardown(server.close.bind(server))
t.after(() => { server.close() })
server.listen({ port: 0 }, (err) => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + server.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.same(payload, JSON.parse(body))
t.equal(res.statusCode, 200)
})
})
const fastifyServer = await server.listen({ port: 0 })
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.deepStrictEqual(payload, await result.json())
t.assert.strictEqual(result.status, 200)
})
test('ignore the result of the promise if reply.send is called beforehand (object)', t => {
t.plan(4)
test('ignore the result of the promise if reply.send is called beforehand (object)', async (t) => {
t.plan(3)
const server = Fastify()
const payload = { hello: 'world2' }
@@ -189,28 +209,24 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
return { hello: 'world' }
})
t.teardown(server.close.bind(server))
t.after(() => { server.close() })
server.listen({ port: 0 }, (err) => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + server.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.same(payload, JSON.parse(body))
t.equal(res.statusCode, 200)
})
})
const fastifyServer = await server.listen({ port: 0 })
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.deepStrictEqual(payload, await result.json())
t.assert.strictEqual(result.status, 200)
})
test('await reply if we will be calling reply.send in the future', t => {
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.equal(line.msg, lines.shift())
t.assert.strictEqual(line.msg, lines.shift())
})
const server = Fastify({
@@ -232,19 +248,20 @@ test('await reply if we will be calling reply.send in the future', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
t.assert.deepStrictEqual(payload, { hello: 'world' })
done()
})
})
test('await reply if we will be calling reply.send in the future (error case)', t => {
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.equal(line.msg, lines.shift())
t.assert.strictEqual(line.msg, lines.shift())
})
const server = Fastify({
@@ -265,12 +282,13 @@ test('await reply if we will be calling reply.send in the future (error case)',
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
done()
})
})
test('support reply decorators with await', t => {
test('support reply decorators with await', (t, done) => {
t.plan(2)
const fastify = Fastify()
@@ -292,9 +310,10 @@ test('support reply decorators with await', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
t.assert.deepStrictEqual(payload, { hello: 'world' })
done()
})
})
@@ -309,9 +328,9 @@ test('inject async await', async t => {
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
})
@@ -326,18 +345,18 @@ test('inject async await - when the server equal up', async t => {
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
await sleep(200)
try {
const res2 = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res2.payload))
t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res2.payload))
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
})
@@ -356,13 +375,13 @@ test('async await plugin', async t => {
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
})
test('does not call reply.send() twice if 204 response equal already sent', t => {
test('does not call reply.send() twice if 204 response equal already sent', (t, done) => {
t.plan(2)
const fastify = Fastify()
@@ -378,13 +397,14 @@ test('does not call reply.send() twice if 204 response equal already sent', t =>
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 204)
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 204)
done()
})
})
test('promise was fulfilled with undefined', t => {
t.plan(4)
test('promise was fulfilled with undefined', async (t) => {
t.plan(3)
let fastify = null
const stream = split(JSON.parse)
@@ -396,31 +416,25 @@ test('promise was fulfilled with undefined', t => {
}
})
} catch (e) {
t.fail()
t.assert.fail()
}
t.teardown(fastify.close.bind(fastify))
t.after(() => { fastify.close() })
fastify.get('/', async (req, reply) => {
})
stream.once('data', line => {
t.fail('should not log an error')
t.assert.fail('should not log an error')
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.equal(res.body, undefined)
t.equal(res.statusCode, 200)
})
})
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.strictEqual(await result.text(), '')
t.assert.strictEqual(result.status, 200)
})
test('promise was fulfilled with undefined using inject', async (t) => {
@@ -436,17 +450,17 @@ test('promise was fulfilled with undefined using inject', async (t) => {
})
stream.once('data', line => {
t.fail('should not log an error')
t.assert.fail('should not log an error')
})
const res = await fastify.inject('/')
t.equal(res.body, '')
t.equal(res.statusCode, 200)
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 => {
t.plan(4)
test('error is not logged because promise was fulfilled with undefined but response was sent before promise resolution', async (t) => {
t.plan(3)
let fastify = null
const stream = split(JSON.parse)
@@ -459,38 +473,32 @@ test('error is not logged because promise was fulfilled with undefined but respo
}
})
} catch (e) {
t.fail()
t.assert.fail()
}
t.teardown(fastify.close.bind(fastify))
t.after(() => { fastify.close() })
fastify.get('/', async (req, reply) => {
reply.send(payload)
})
stream.once('data', line => {
t.fail('should not log an error')
t.assert.fail('should not log an error')
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/'
}, (err, res, body) => {
t.error(err)
t.equal(res.statusCode, 200)
t.same(
payload,
JSON.parse(body)
)
})
})
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(
payload,
await result.json()
)
})
test('Thrown Error instance sets HTTP status code', t => {
test('Thrown Error instance sets HTTP status code', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -506,9 +514,9 @@ test('Thrown Error instance sets HTTP status code', t => {
method: 'GET',
url: '/'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 418)
t.same(
t.assert.ifError(error)
t.assert.strictEqual(res.statusCode, 418)
t.assert.deepStrictEqual(
{
error: statusCodes['418'],
message: err.message,
@@ -516,10 +524,11 @@ test('Thrown Error instance sets HTTP status code', t => {
},
JSON.parse(res.payload)
)
done()
})
})
test('customErrorHandler support', t => {
test('customErrorHandler support', (t, done) => {
t.plan(4)
const fastify = Fastify()
@@ -531,7 +540,7 @@ test('customErrorHandler support', t => {
})
fastify.setErrorHandler(async err => {
t.equal(err.message, 'ouch')
t.assert.strictEqual(err.message, 'ouch')
const error = new Error('kaboom')
error.statusCode = 401
throw error
@@ -541,9 +550,9 @@ test('customErrorHandler support', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 401)
t.assert.deepStrictEqual(
{
error: statusCodes['401'],
message: 'kaboom',
@@ -551,10 +560,11 @@ test('customErrorHandler support', t => {
},
JSON.parse(res.payload)
)
done()
})
})
test('customErrorHandler support without throwing', t => {
test('customErrorHandler support without throwing', (t, done) => {
t.plan(4)
const fastify = Fastify()
@@ -566,26 +576,27 @@ test('customErrorHandler support without throwing', t => {
})
fastify.setErrorHandler(async (err, req, reply) => {
t.equal(err.message, 'ouch')
t.assert.strictEqual(err.message, 'ouch')
await reply.code(401).send('kaboom')
reply.send = t.fail.bind(t, 'should not be called')
reply.send = t.assert.fail.bind(t, 'should not be called')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
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 => {
test('customErrorHandler only called if reply not already sent', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -597,23 +608,24 @@ test('customErrorHandler only called if reply not already sent', t => {
throw error
})
fastify.setErrorHandler(t.fail.bind(t, 'should not be called'))
fastify.setErrorHandler(t.assert.fail.bind(t, 'should not be called'))
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.same(
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 => {
test('setNotFoundHandler should accept return value', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -633,9 +645,9 @@ test('setNotFoundHandler should accept return value', t => {
method: 'GET',
url: '/elsewhere'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
t.same(
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 404)
t.assert.deepStrictEqual(
{
error: statusCodes['404'],
message: 'lost',
@@ -643,11 +655,12 @@ test('setNotFoundHandler should accept return value', t => {
},
JSON.parse(res.payload)
)
done()
})
})
// See https://github.com/fastify/fastify/issues/3209
test('customErrorHandler should accept return value', t => {
test('customErrorHandler should accept return value', (t, done) => {
t.plan(4)
const fastify = Fastify()
@@ -659,7 +672,7 @@ test('customErrorHandler should accept return value', t => {
})
fastify.setErrorHandler((err, req, reply) => {
t.equal(err.message, 'ouch')
t.assert.strictEqual(err.message, 'ouch')
reply.code(401)
return {
error: statusCodes['401'],
@@ -672,9 +685,9 @@ test('customErrorHandler should accept return value', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 401)
t.assert.deepStrictEqual(
{
error: statusCodes['401'],
message: 'kaboom',
@@ -682,10 +695,11 @@ test('customErrorHandler should accept return value', t => {
},
JSON.parse(res.payload)
)
done()
})
})
test('await self', async t => {
const app = Fastify()
t.equal(await app, app)
t.assert.strictEqual(await app, app)
})

View File

@@ -1,21 +1,20 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const Fastify = require('../fastify')
// asyncDispose doesn't exist in node <= 16
t.test('async dispose should close fastify', { skip: !('asyncDispose' in Symbol) }, async t => {
test('async dispose should close fastify', { skip: !('asyncDispose' in Symbol) }, async t => {
t.plan(2)
const fastify = Fastify()
await fastify.listen({ port: 0 })
t.equal(fastify.server.listening, true)
t.assert.strictEqual(fastify.server.listening, true)
// the same as syntax sugar for
// await using app = fastify()
await fastify[Symbol.asyncDispose]()
t.equal(fastify.server.listening, false)
t.assert.strictEqual(fastify.server.listening, false)
})

View File

@@ -1,9 +1,8 @@
'use strict'
const { createHook } = require('node:async_hooks')
const t = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
const sget = require('simple-get').concat
const remainingIds = new Set()
@@ -20,50 +19,34 @@ createHook({
const app = Fastify({ logger: false })
app.get('/', function (request, reply) {
reply.send({ id: 42 })
})
app.post('/', function (request, reply) {
reply.send({ id: 42 })
})
app.listen({ port: 0 }, function (err, address) {
t.error(err)
sget({
method: 'POST',
url: 'http://localhost:' + app.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
sget({
method: 'POST',
url: 'http://localhost:' + app.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
sget({
method: 'GET',
url: 'http://localhost:' + app.server.address().port,
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
app.close()
t.equal(remainingIds.size, 0)
t.end()
})
})
test('test async hooks', async (t) => {
app.get('/', function (request, reply) {
reply.send({ id: 42 })
})
app.post('/', function (request, reply) {
reply.send({ id: 42 })
})
t.after(() => app.close())
const fastifyServer = await app.listen({ port: 0 })
const result1 = await fetch(fastifyServer, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
})
t.assert.strictEqual(result1.status, 200)
const result2 = await fetch(fastifyServer, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
})
t.assert.strictEqual(result2.status, 200)
const result3 = await fetch(fastifyServer)
t.assert.strictEqual(result3.status, 200)
t.assert.strictEqual(remainingIds.size, 0)
})

224
backend/node_modules/fastify/test/body-limit.test.js generated vendored Normal file
View File

@@ -0,0 +1,224 @@
'use strict'
const Fastify = require('../fastify')
const zlib = require('node:zlib')
const { test } = require('node:test')
test('bodyLimit', async t => {
t.plan(4)
try {
Fastify({ bodyLimit: 1.3 })
t.assert.fail('option must be an integer')
} catch (err) {
t.assert.ok(err)
}
try {
Fastify({ bodyLimit: [] })
t.assert.fail('option must be an integer')
} catch (err) {
t.assert.ok(err)
}
const fastify = Fastify({ bodyLimit: 1 })
fastify.post('/', (request, reply) => {
reply.send({ error: 'handler should not be called' })
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const result = await fetch(fastifyServer, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([])
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 413)
})
test('bodyLimit is applied to decoded content', async (t) => {
t.plan(6)
const body = { x: 'x'.repeat(30000) }
const json = JSON.stringify(body)
const encoded = zlib.gzipSync(json)
const fastify = Fastify()
fastify.addHook('preParsing', async (req, reply, payload) => {
t.assert.strictEqual(req.headers['content-length'], `${encoded.length}`)
const unzip = zlib.createGunzip()
Object.defineProperty(unzip, 'receivedEncodedLength', {
get () {
return unzip.bytesWritten
}
})
payload.pipe(unzip)
return unzip
})
fastify.post('/body-limit-40k', {
bodyLimit: 40000,
onError: async (req, res, err) => {
t.fail('should not be called')
}
}, (request, reply) => {
reply.send({ x: request.body.x })
})
fastify.post('/body-limit-20k', {
bodyLimit: 20000,
onError: async (req, res, err) => {
t.assert.strictEqual(err.code, 'FST_ERR_CTP_BODY_TOO_LARGE')
t.assert.strictEqual(err.statusCode, 413)
}
}, (request, reply) => {
reply.send({ x: 'handler should not be called' })
})
await t.test('bodyLimit 40k', async (t) => {
const result = await fastify.inject({
method: 'POST',
url: '/body-limit-40k',
headers: {
'content-encoding': 'gzip',
'content-type': 'application/json'
},
payload: encoded
})
t.assert.strictEqual(result.statusCode, 200)
t.assert.deepStrictEqual(result.json(), body)
})
await t.test('bodyLimit 20k', async (t) => {
const result = await fastify.inject({
method: 'POST',
url: '/body-limit-20k',
headers: {
'content-encoding': 'gzip',
'content-type': 'application/json'
},
payload: encoded
})
t.assert.strictEqual(result.statusCode, 413)
})
})
test('default request.routeOptions.bodyLimit should be 1048576', async t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/default-bodylimit', {
handler (request, reply) {
t.assert.strictEqual(1048576, request.routeOptions.bodyLimit)
reply.send({ })
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const result = await fetch(fastifyServer + '/default-bodylimit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([])
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
test('request.routeOptions.bodyLimit should be equal to route limit', async t => {
t.plan(3)
const fastify = Fastify({ bodyLimit: 1 })
fastify.post('/route-limit', {
bodyLimit: 1000,
handler (request, reply) {
t.assert.strictEqual(1000, request.routeOptions.bodyLimit)
reply.send({})
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const result = await fetch(fastifyServer + '/route-limit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([])
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
test('request.routeOptions.bodyLimit should be equal to server limit', async t => {
t.plan(3)
const fastify = Fastify({ bodyLimit: 100 })
fastify.post('/server-limit', {
handler (request, reply) {
t.assert.strictEqual(100, request.routeOptions.bodyLimit)
reply.send({})
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const result = await fetch(fastifyServer + '/server-limit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([])
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
test('bodyLimit should use byte length for UTF-8 strings, not character length', async t => {
t.plan(4)
// Create a string with multi-byte UTF-8 characters
// Use Japanese characters that are 3 bytes each in UTF-8
const multiByteString = 'あああ' // 3 characters, 9 bytes in UTF-8
t.assert.strictEqual(multiByteString.length, 3) // 3 characters
t.assert.strictEqual(Buffer.byteLength(multiByteString, 'utf8'), 9) // 9 bytes
const fastify = Fastify()
// Add a custom text parser that returns the string as-is
fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, (req, body, done) => {
done(null, body)
})
// Set body limit to 7 bytes - this should reject the string (9 bytes)
// even though string.length (3) would be under any reasonable limit
fastify.post('/test-utf8', {
bodyLimit: 7
}, (request, reply) => {
reply.send({ body: request.body, length: request.body.length })
})
await t.test('should reject body when byte length exceeds limit', async (t) => {
const result = await fastify.inject({
method: 'POST',
url: '/test-utf8',
headers: { 'Content-Type': 'text/plain', 'Content-Length': null },
payload: multiByteString
})
t.assert.strictEqual(result.statusCode, 413)
})
await t.test('should accept body when byte length is within limit', async (t) => {
const smallString = 'あ' // 1 character, 3 bytes, under the 7 byte limit
const result = await fastify.inject({
method: 'POST',
url: '/test-utf8',
headers: { 'Content-Type': 'text/plain' },
payload: smallString
})
t.assert.strictEqual(result.statusCode, 200)
t.assert.strictEqual(result.json().body, smallString)
t.assert.strictEqual(result.json().length, 1) // 1 character
})
})

View File

@@ -1,194 +0,0 @@
'use strict'
const Fastify = require('..')
const sget = require('simple-get').concat
const zlib = require('node:zlib')
const t = require('tap')
const test = t.test
test('bodyLimit', t => {
t.plan(5)
try {
Fastify({ bodyLimit: 1.3 })
t.fail('option must be an integer')
} catch (err) {
t.ok(err)
}
try {
Fastify({ bodyLimit: [] })
t.fail('option must be an integer')
} catch (err) {
t.ok(err)
}
const fastify = Fastify({ bodyLimit: 1 })
fastify.post('/', (request, reply) => {
reply.send({ error: 'handler should not be called' })
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port,
headers: { 'Content-Type': 'application/json' },
body: [],
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
})
})
})
test('bodyLimit is applied to decoded content', t => {
t.plan(9)
const body = { x: 'x'.repeat(30000) }
const json = JSON.stringify(body)
const encoded = zlib.gzipSync(json)
const fastify = Fastify()
fastify.addHook('preParsing', async (req, reply, payload) => {
t.equal(req.headers['content-length'], `${encoded.length}`)
const unzip = zlib.createGunzip()
Object.defineProperty(unzip, 'receivedEncodedLength', {
get () {
return unzip.bytesWritten
}
})
payload.pipe(unzip)
return unzip
})
fastify.post('/body-limit-40k', {
bodyLimit: 40000,
onError: async (req, res, err) => {
t.fail('should not be called')
}
}, (request, reply) => {
reply.send({ x: request.body.x })
})
fastify.post('/body-limit-20k', {
bodyLimit: 20000,
onError: async (req, res, err) => {
t.equal(err.code, 'FST_ERR_CTP_BODY_TOO_LARGE')
t.equal(err.statusCode, 413)
}
}, (request, reply) => {
reply.send({ x: 'handler should not be called' })
})
fastify.inject({
method: 'POST',
url: '/body-limit-40k',
headers: {
'content-encoding': 'gzip',
'content-type': 'application/json'
},
payload: encoded
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.same(res.json(), body)
})
fastify.inject({
method: 'POST',
url: '/body-limit-20k',
headers: {
'content-encoding': 'gzip',
'content-type': 'application/json'
},
payload: encoded
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 413)
})
})
test('default request.routeOptions.bodyLimit should be 1048576', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/default-bodylimit', {
handler (request, reply) {
t.equal(1048576, request.routeOptions.bodyLimit)
reply.send({ })
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port + '/default-bodylimit',
headers: { 'Content-Type': 'application/json' },
body: [],
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
})
test('request.routeOptions.bodyLimit should be equal to route limit', t => {
t.plan(4)
const fastify = Fastify({ bodyLimit: 1 })
fastify.post('/route-limit', {
bodyLimit: 1000,
handler (request, reply) {
t.equal(1000, request.routeOptions.bodyLimit)
reply.send({})
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port + '/route-limit',
headers: { 'Content-Type': 'application/json' },
body: [],
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
})
test('request.routeOptions.bodyLimit should be equal to server limit', t => {
t.plan(4)
const fastify = Fastify({ bodyLimit: 100 })
fastify.post('/server-limit', {
handler (request, reply) {
t.equal(100, request.routeOptions.bodyLimit)
reply.send({})
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port + '/server-limit',
headers: { 'Content-Type': 'application/json' },
body: [],
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
test('Buffer test', async t => {
@@ -12,7 +11,7 @@ test('Buffer test', async t => {
return request.body
})
test('should return 200 if the body is not empty', async t => {
await test('should return 200 if the body is not empty', async t => {
t.plan(3)
const response = await fastify.inject({
@@ -24,12 +23,12 @@ test('Buffer test', async t => {
}
})
t.error(response.error)
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '{"hello":"world"}')
t.assert.ifError(response.error)
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.payload.toString(), '{"hello":"world"}')
})
test('should return 400 if the body is empty', async t => {
await test('should return 400 if the body is empty', async t => {
t.plan(3)
const response = await fastify.inject({
@@ -41,13 +40,35 @@ test('Buffer test', async t => {
}
})
t.error(response.error)
t.equal(response.statusCode, 400)
t.same(JSON.parse(response.payload.toString()), {
t.assert.ifError(response.error)
t.assert.strictEqual(response.statusCode, 400)
t.assert.deepStrictEqual(JSON.parse(response.payload.toString()), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
})
await test('should return 400 if the body is invalid json', async t => {
t.plan(3)
const response = await fastify.inject({
method: 'DELETE',
url: '/',
payload: Buffer.from(']'),
headers: {
'content-type': 'application/json'
}
})
t.assert.ifError(response.error)
t.assert.strictEqual(response.statusCode, 400)
t.assert.deepStrictEqual(JSON.parse(response.payload.toString()), {
error: 'Bad Request',
code: 'FST_ERR_CTP_INVALID_JSON_BODY',
message: 'Body is not valid JSON but content-type is set to \'application/json\'',
statusCode: 400
})
})
})

View File

@@ -1,6 +1,6 @@
'use strict'
const os = require('os')
const os = require('node:os')
const forge = require('node-forge')
// from self-cert module
@@ -91,7 +91,7 @@ function selfCert (opts) {
}
}
async function buildCertificate () {
function buildCertificate () {
// "global" is used in here because "t.context" is only supported by "t.beforeEach" and "t.afterEach"
// For the test case which execute this code which will be using `t.before` and it can reduce the
// number of times executing it.

View File

@@ -1,9 +1,9 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const fs = require('node:fs')
const path = require('node:path')
const { loadESLint } = require('eslint')
const { code } = require('../../build/build-error-serializer')
@@ -15,12 +15,13 @@ test('check generated code syntax', async (t) => {
t.plan(1)
// standard is a esm, we import it like this
const { default: standard } = await import('standard')
const result = await standard.lintText(code)
const Eslint = await loadESLint({ useFlatConfig: true })
const eslint = new Eslint()
const result = await eslint.lintText(code)
// if there are any invalid syntax
// fatal count will be greater than 0
t.equal(result[0].fatalErrorCount, 0)
t.assert.strictEqual(result[0].fatalErrorCount, 0)
})
const isPrepublish = !!process.env.PREPUBLISH
@@ -31,5 +32,5 @@ test('ensure the current error serializer is latest', { skip: !isPrepublish }, a
const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
// line break should not be a problem depends on system
t.equal(unifyLineBreak(current), unifyLineBreak(code))
t.assert.strictEqual(unifyLineBreak(current), unifyLineBreak(code))
})

View File

@@ -2,8 +2,7 @@
const fs = require('node:fs')
const path = require('node:path')
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const fastify = require('../../fastify')()
test('should be the same as package.json', t => {
@@ -11,5 +10,5 @@ test('should be the same as package.json', t => {
const json = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json')).toString('utf8'))
t.equal(fastify.version, json.version)
t.assert.strictEqual(fastify.version, json.version)
})

View File

@@ -1,12 +1,12 @@
# Bundlers test stack
In some cases, developers bundle their apps for several targets such as serverless applications.
Even if it's not recommended by Fastify team; we need to ensure we do not break the build process.
In some cases, developers bundle their apps for several targets such as serverless applications.
Even if it's not recommended by Fastify team; we need to ensure we do not break the build process.
Please note this might result in features behaving differently, like the version handling check for plugins.
## Test bundlers
The bundler test stack has been defined separately from the rest of the Unit testing stack because it's not a
The bundler test stack has been defined separately from the rest of the Unit testing stack because it's not a
part of the fastify lib itself. Note that the tests run in CI only on NodeJs LTS version.
Developers do not need to install every bundler to run unit tests.
@@ -23,7 +23,7 @@ stack dependencies. See:
## Bundler test development
To not break the fastify unit testing stack please name test files like this `*-test.js` and not `*.test.js`,
To not break the fastify unit testing stack please name test files like this `*-test.js` and not `*.test.js`,
otherwise it will be targeted by the regular expression used for unit tests for fastify.
Tests need to ensure the build process works and the fastify application can be run,
Tests need to ensure the build process works and the fastify application can be run,
no need to go in deep testing unless an issue is raised.

View File

@@ -1,31 +1,32 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const fastifySuccess = require('./dist/success')
const fastifyFailPlugin = require('./dist/failPlugin')
test('Bundled package should work', (t) => {
test('Bundled package should work', (t, done) => {
t.plan(4)
fastifySuccess.ready((err) => {
t.error(err)
t.assert.ifError(err)
fastifySuccess.inject(
{
method: 'GET',
url: '/'
},
(error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
t.same(res.json(), { hello: 'world' })
t.assert.ifError(error)
t.assert.strictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), { hello: 'world' })
done()
}
)
})
})
test('Bundled package should not work with bad plugin version', (t) => {
test('Bundled package should not work with bad plugin version', (t, done) => {
t.plan(1)
fastifyFailPlugin.ready((err) => {
t.match(err.message, /expected '9.x' fastify version/i)
t.assert.match(err.message, /expected '9.x' fastify version/i)
done()
})
})

View File

@@ -5,6 +5,6 @@
"test": "npm run bundle && node bundler-test.js"
},
"devDependencies": {
"esbuild": "^0.14.11"
"esbuild": "^0.25.0"
}
}

View File

@@ -1,31 +1,32 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const fastifySuccess = require('./dist/success')
const fastifyFailPlugin = require('./dist/failPlugin')
test('Bundled package should work', (t) => {
test('Bundled package should work', (t, done) => {
t.plan(4)
fastifySuccess.ready((err) => {
t.error(err)
t.assert.ifError(err)
fastifySuccess.inject(
{
method: 'GET',
url: '/'
},
(error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
t.same(res.json(), { hello: 'world' })
t.assert.ifError(error)
t.assert.strictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), { hello: 'world' })
done()
}
)
})
})
test('Bundled package should not work with bad plugin version', (t) => {
test('Bundled package should not work with bad plugin version', (t, done) => {
t.plan(1)
fastifyFailPlugin.ready((err) => {
t.match(err.message, /expected '9.x' fastify version/i)
t.assert.match(err.message, /expected '9.x' fastify version/i)
done()
})
})

View File

@@ -1,120 +1,102 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const sget = require('simple-get').concat
test('case insensitive', t => {
t.plan(4)
test('case insensitive', async (t) => {
t.plan(3)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/foo', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/FOO'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), {
hello: 'world'
})
})
const result = await fetch(`${fastifyServer}/FOO`)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), {
hello: 'world'
})
})
test('case insensitive inject', t => {
t.plan(4)
test('case insensitive inject', async t => {
t.plan(2)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/foo', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/FOO'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), {
hello: 'world'
})
})
const result = await fastify.inject({
method: 'GET',
url: fastifyServer + '/FOO'
})
t.assert.strictEqual(result.statusCode, 200)
t.assert.deepStrictEqual(result.json(), {
hello: 'world'
})
})
test('case insensitive (parametric)', t => {
t.plan(5)
test('case insensitive (parametric)', async (t) => {
t.plan(4)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/foo/:param', (req, reply) => {
t.equal(req.params.param, 'bAr')
t.assert.strictEqual(req.params.param, 'bAr')
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/FoO/bAr'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), {
hello: 'world'
})
})
const result = await fetch(`${fastifyServer}/FoO/bAr`, {
method: 'GET'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), {
hello: 'world'
})
})
test('case insensitive (wildcard)', t => {
t.plan(5)
test('case insensitive (wildcard)', async (t) => {
t.plan(4)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/foo/*', (req, reply) => {
t.equal(req.params['*'], 'bAr/baZ')
t.assert.strictEqual(req.params['*'], 'bAr/baZ')
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/FoO/bAr/baZ'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), {
hello: 'world'
})
})
const result = await fetch(`${fastifyServer}/FoO/bAr/baZ`)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), {
hello: 'world'
})
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const fastify = require('..')()
const noop = () => {}
@@ -22,17 +21,17 @@ const opts = {
test('chainable - get', t => {
t.plan(1)
t.type(fastify.get('/', opts, noop), fastify)
t.assert.strictEqual(fastify.get('/', opts, noop), fastify)
})
test('chainable - post', t => {
t.plan(1)
t.type(fastify.post('/', opts, noop), fastify)
t.assert.strictEqual(fastify.post('/', opts, noop), fastify)
})
test('chainable - route', t => {
t.plan(1)
t.type(fastify.route({
t.assert.strictEqual(fastify.route({
method: 'GET',
url: '/other',
schema: opts.schema,

View File

@@ -0,0 +1,128 @@
'use strict'
const { test } = require('node:test')
const Fastify = require('..')
test('Should accept a custom childLoggerFactory function', (t, done) => {
t.plan(4)
const fastify = Fastify()
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
t.assert.ok(bindings.reqId)
t.assert.ok(opts)
this.log.debug(bindings, 'created child logger')
return logger.child(bindings, opts)
})
fastify.get('/', (req, reply) => {
req.log.info('log message')
reply.send()
})
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.assert.ifError(err)
done()
})
})
})
test('Should accept a custom childLoggerFactory function as option', (t, done) => {
t.plan(4)
const fastify = Fastify({
childLoggerFactory: function (logger, bindings, opts) {
t.assert.ok(bindings.reqId)
t.assert.ok(opts)
this.log.debug(bindings, 'created child logger')
return logger.child(bindings, opts)
}
})
fastify.get('/', (req, reply) => {
req.log.info('log message')
reply.send()
})
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.assert.ifError(err)
done()
})
})
})
test('req.log should be the instance returned by the factory', (t, done) => {
t.plan(3)
const fastify = Fastify()
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
this.log.debug('using root logger')
return this.log
})
fastify.get('/', (req, reply) => {
t.assert.strictEqual(req.log, fastify.log)
req.log.info('log message')
reply.send()
})
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.assert.ifError(err)
done()
})
})
})
test('should throw error if invalid logger is returned', (t, done) => {
t.plan(2)
const fastify = Fastify()
fastify.setChildLoggerFactory(function () {
this.log.debug('returning an invalid logger, expect error')
return undefined
})
fastify.get('/', (req, reply) => {
reply.send()
})
t.after(() => fastify.close())
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
t.assert.throws(() => {
try {
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err) => {
t.assert.fail('request should have failed but did not')
t.assert.ifError(err)
done()
})
} finally {
done()
}
}, { code: 'FST_ERR_LOG_INVALID_LOGGER' })
})
})

View File

@@ -1,91 +0,0 @@
'use strict'
const { test } = require('tap')
const Fastify = require('..')
test('Should accept a custom childLoggerFactory function', t => {
t.plan(4)
const fastify = Fastify()
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
t.ok(bindings.reqId)
t.ok(opts)
this.log.debug(bindings, 'created child logger')
return logger.child(bindings, opts)
})
fastify.get('/', (req, reply) => {
req.log.info('log message')
reply.send()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
fastify.close()
})
})
})
test('req.log should be the instance returned by the factory', t => {
t.plan(3)
const fastify = Fastify()
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
this.log.debug('using root logger')
return this.log
})
fastify.get('/', (req, reply) => {
t.equal(req.log, fastify.log)
req.log.info('log message')
reply.send()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
fastify.close()
})
})
})
test('should throw error if invalid logger is returned', t => {
t.plan(2)
const fastify = Fastify()
fastify.setChildLoggerFactory(function () {
this.log.debug('returning an invalid logger, expect error')
return undefined
})
fastify.get('/', (req, reply) => {
reply.send()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.throws(() => {
try {
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err) => {
t.fail('request should have failed but did not')
t.error(err)
fastify.close()
})
} finally {
fastify.close()
}
}, { code: 'FST_ERR_LOG_INVALID_LOGGER' })
})
})

View File

@@ -1,13 +1,13 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const fastify = require('..')({ requestTimeout: 5, http: { connectionsCheckingInterval: 1000 } })
const { connect } = require('node:net')
test('requestTimeout should return 408', t => {
test('requestTimeout should return 408', (t, done) => {
t.plan(1)
t.teardown(() => {
t.after(() => {
fastify.close()
})
@@ -28,11 +28,11 @@ test('requestTimeout should return 408', t => {
socket.on('data', c => (data = Buffer.concat([data, c])))
socket.on('end', () => {
t.equal(
t.assert.strictEqual(
data.toString('utf-8'),
'HTTP/1.1 408 Request Timeout\r\nContent-Length: 71\r\nContent-Type: application/json\r\n\r\n{"error":"Request Timeout","message":"Client Timeout","statusCode":408}'
)
t.end()
done()
})
})
})

View File

@@ -1,10 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const { Client } = require('undici')
const semver = require('semver')
test('Should return 503 while closing - pipelining', async t => {
const fastify = Fastify({
@@ -13,46 +11,10 @@ test('Should return 503 while closing - pipelining', async t => {
})
fastify.get('/', async (req, reply) => {
fastify.close()
return { hello: 'world' }
})
await fastify.listen({ port: 0 })
const instance = new Client('http://localhost:' + fastify.server.address().port, {
pipelining: 2
})
const codes = [200, 200, 503]
const responses = await Promise.all([
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' })
])
const actual = responses.map(r => r.statusCode)
t.same(actual, codes)
await instance.close()
})
// default enable of idle closing idle connection is accidentally backported to 18.19.0 and fixed in 18.20.3
// Refs: https://github.com/nodejs/node/releases/tag/v18.20.3
const isNodeDefaultClosingIdleConnection =
(
semver.gte(process.version, '18.19.0') &&
semver.lt(process.version, '18.20.3')
) ||
semver.gte(process.version, '19.0.0')
test('Should not return 503 while closing - pipelining - return503OnClosing: false, skip when Node default closing idle connection', { skip: isNodeDefaultClosingIdleConnection }, async t => {
const fastify = Fastify({
return503OnClosing: false,
forceCloseConnections: false
})
fastify.get('/', (req, reply) => {
fastify.close()
// Simulate a delay to allow pipelining to kick in
await new Promise(resolve => setTimeout(resolve, 5))
reply.send({ hello: 'world' })
fastify.close()
})
await fastify.listen({ port: 0 })
@@ -61,21 +23,25 @@ test('Should not return 503 while closing - pipelining - return503OnClosing: fal
pipelining: 2
})
const codes = [200, 200, 200]
const responses = await Promise.all([
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' })
const [firstRequest, secondRequest, thirdRequest] = await Promise.allSettled([
instance.request({ path: '/', method: 'GET', blocking: false }),
instance.request({ path: '/', method: 'GET', blocking: false }),
instance.request({ path: '/', method: 'GET', blocking: false })
])
const actual = responses.map(r => r.statusCode)
t.assert.strictEqual(firstRequest.status, 'fulfilled')
t.assert.strictEqual(secondRequest.status, 'fulfilled')
t.same(actual, codes)
t.assert.strictEqual(firstRequest.value.statusCode, 200)
t.assert.strictEqual(secondRequest.value.statusCode, 200)
t.assert.strictEqual(thirdRequest.status, 'fulfilled')
t.assert.strictEqual(thirdRequest.value.statusCode, 503)
await instance.close()
})
test('Should close the socket abruptly - pipelining - return503OnClosing: false, skip when Node not default closing idle connection', { skip: !isNodeDefaultClosingIdleConnection }, async t => {
// Since Node v19, we will always invoke server.closeIdleConnections()
test('Should close the socket abruptly - pipelining - return503OnClosing: false', async t => {
// Since Node v20, we will always invoke server.closeIdleConnections()
// therefore our socket will be closed
const fastify = Fastify({
return503OnClosing: false,
@@ -83,6 +49,8 @@ test('Should close the socket abruptly - pipelining - return503OnClosing: false,
})
fastify.get('/', async (req, reply) => {
// Simulate a delay to allow pipelining to kick in
await new Promise(resolve => setTimeout(resolve, 5))
reply.send({ hello: 'world' })
fastify.close()
})
@@ -90,20 +58,21 @@ test('Should close the socket abruptly - pipelining - return503OnClosing: false,
await fastify.listen({ port: 0 })
const instance = new Client('http://localhost:' + fastify.server.address().port, {
pipelining: 2
pipelining: 1
})
const responses = await Promise.allSettled([
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' }),
instance.request({ path: '/', method: 'GET' })
instance.request({ path: '/', method: 'GET', blocking: false }),
instance.request({ path: '/', method: 'GET', blocking: false }),
instance.request({ path: '/', method: 'GET', blocking: false }),
instance.request({ path: '/', method: 'GET', blocking: false })
])
t.equal(responses[0].status, 'fulfilled')
t.equal(responses[1].status, 'fulfilled')
t.equal(responses[2].status, 'rejected')
t.equal(responses[3].status, 'rejected')
const fulfilled = responses.filter(r => r.status === 'fulfilled')
const rejected = responses.filter(r => r.status === 'rejected')
t.assert.strictEqual(fulfilled.length, 1)
t.assert.strictEqual(rejected.length, 3)
await instance.close()
})

View File

@@ -2,43 +2,43 @@
const net = require('node:net')
const http = require('node:http')
const { test } = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
const { Client } = require('undici')
const semver = require('semver')
const split = require('split2')
const { sleep } = require('./helper')
test('close callback', t => {
test('close callback', (t, testDone) => {
t.plan(7)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
function onClose (instance, done) {
t.type(fastify, this)
t.type(fastify, instance)
t.equal(fastify, this)
t.equal(fastify, instance)
t.assert.ok(typeof fastify === typeof this)
t.assert.ok(typeof fastify === typeof instance)
t.assert.strictEqual(fastify, this)
t.assert.strictEqual(fastify, instance)
done()
}
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
t.assert.ifError(err)
t.assert.ok('close callback')
testDone()
})
})
})
test('inside register', t => {
test('inside register', (t, done) => {
t.plan(5)
const fastify = Fastify()
fastify.register(function (f, opts, done) {
f.addHook('onClose', onClose)
function onClose (instance, done) {
t.ok(instance.prototype === fastify.prototype)
t.equal(instance, f)
t.assert.ok(instance.prototype === fastify.prototype)
t.assert.strictEqual(instance, f)
done()
}
@@ -46,23 +46,24 @@ test('inside register', t => {
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
t.assert.ifError(err)
t.assert.ok('close callback')
done()
})
})
})
test('close order', t => {
test('close order', (t, done) => {
t.plan(5)
const fastify = Fastify()
const order = [1, 2, 3]
fastify.register(function (f, opts, done) {
f.addHook('onClose', (instance, done) => {
t.equal(order.shift(), 1)
t.assert.strictEqual(order.shift(), 1)
done()
})
@@ -70,16 +71,17 @@ test('close order', t => {
})
fastify.addHook('onClose', (instance, done) => {
t.equal(order.shift(), 2)
t.assert.strictEqual(order.shift(), 2)
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.close((err) => {
t.error(err)
t.equal(order.shift(), 3)
t.assert.ifError(err)
t.assert.strictEqual(order.shift(), 3)
done()
})
})
})
@@ -91,37 +93,38 @@ test('close order - async', async t => {
fastify.register(function (f, opts, done) {
f.addHook('onClose', async instance => {
t.equal(order.shift(), 1)
t.assert.strictEqual(order.shift(), 1)
})
done()
})
fastify.addHook('onClose', () => {
t.equal(order.shift(), 2)
t.assert.strictEqual(order.shift(), 2)
})
await fastify.listen({ port: 0 })
await fastify.close()
t.equal(order.shift(), 3)
t.assert.strictEqual(order.shift(), 3)
})
test('should not throw an error if the server is not listening', t => {
test('should not throw an error if the server is not listening', (t, done) => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
function onClose (instance, done) {
t.type(fastify, instance)
t.assert.ok(instance.prototype === fastify.prototype)
done()
}
fastify.close((err) => {
t.error(err)
t.assert.ifError(err)
done()
})
})
test('onClose should keep the context', t => {
test('onClose should keep the context', (t, done) => {
t.plan(4)
const fastify = Fastify()
fastify.register(plugin)
@@ -129,11 +132,11 @@ test('onClose should keep the context', t => {
function plugin (instance, opts, done) {
instance.decorate('test', true)
instance.addHook('onClose', onClose)
t.ok(instance.prototype === fastify.prototype)
t.assert.ok(instance.prototype === fastify.prototype)
function onClose (i, done) {
t.ok(i.test)
t.equal(i, instance)
t.assert.ok(i.test)
t.assert.strictEqual(i, instance)
done()
}
@@ -141,11 +144,12 @@ test('onClose should keep the context', t => {
}
fastify.close((err) => {
t.error(err)
t.assert.ifError(err)
done()
})
})
test('Should return error while closing (promise) - injection', t => {
test('Should return error while closing (promise) - injection', (t, done) => {
t.plan(4)
const fastify = Fastify()
@@ -159,8 +163,8 @@ test('Should return error while closing (promise) - injection', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 200)
fastify.close()
process.nextTick(() => {
@@ -168,14 +172,15 @@ test('Should return error while closing (promise) - injection', t => {
method: 'GET',
url: '/'
}).catch(err => {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.assert.ok(err)
t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
done()
})
}, 100)
})
})
test('Should return error while closing (callback) - injection', t => {
test('Should return error while closing (callback) - injection', (t, done) => {
t.plan(4)
const fastify = Fastify()
@@ -191,8 +196,8 @@ test('Should return error while closing (callback) - injection', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 200)
fastify.close()
setTimeout(() => {
@@ -200,53 +205,15 @@ test('Should return error while closing (callback) - injection', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.assert.ok(err)
t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
done()
})
}, 100)
})
})
const isNodeVersionGte1819 = semver.gte(process.version, '18.19.0')
test('Current opened connection should continue to work after closing and return "connection: close" header - return503OnClosing: false, skip Node >= v18.19.x', { skip: isNodeVersionGte1819 }, t => {
const fastify = Fastify({
return503OnClosing: false,
forceCloseConnections: false
})
fastify.get('/', (req, reply) => {
fastify.close()
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const port = fastify.server.address().port
const client = net.createConnection({ port }, () => {
client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
client.once('data', data => {
t.match(data.toString(), /Connection:\s*keep-alive/i)
t.match(data.toString(), /200 OK/i)
client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
client.once('data', data => {
t.match(data.toString(), /Connection:\s*close/i)
t.match(data.toString(), /200 OK/i)
// Test that fastify closes the TCP connection
client.once('close', () => {
t.end()
})
})
})
})
})
})
test('Current opened connection should NOT continue to work after closing and return "connection: close" header - return503OnClosing: false, skip Node < v18.19.x', { skip: !isNodeVersionGte1819 }, t => {
test('Current opened connection should NOT continue to work after closing and return "connection: close" header - return503OnClosing: false', (t, done) => {
t.plan(4)
const fastify = Fastify({
return503OnClosing: false,
@@ -259,7 +226,7 @@ test('Current opened connection should NOT continue to work after closing and re
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
const port = fastify.server.address().port
const client = net.createConnection({ port }, () => {
@@ -272,12 +239,13 @@ test('Current opened connection should NOT continue to work after closing and re
})
client.on('close', function () {
t.pass('close')
t.assert.ok(true)
done()
})
client.once('data', data => {
t.match(data.toString(), /Connection:\s*keep-alive/i)
t.match(data.toString(), /200 OK/i)
t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
t.assert.match(data.toString(), /200 OK/i)
client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
})
@@ -285,7 +253,7 @@ test('Current opened connection should NOT continue to work after closing and re
})
})
test('Current opened connection should not accept new incoming connections', t => {
test('Current opened connection should not accept new incoming connections', (t, done) => {
t.plan(3)
const fastify = Fastify({ forceCloseConnections: false })
fastify.get('/', (req, reply) => {
@@ -295,19 +263,20 @@ test('Current opened connection should not accept new incoming connections', t =
}, 250)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.listen({ port: 0 }, async err => {
t.assert.ifError(err)
const instance = new Client('http://localhost:' + fastify.server.address().port)
instance.request({ path: '/', method: 'GET' }).then(data => {
t.equal(data.statusCode, 200)
})
instance.request({ path: '/', method: 'GET' }).then(data => {
t.equal(data.statusCode, 503)
})
let response = await instance.request({ path: '/', method: 'GET' })
t.assert.strictEqual(response.statusCode, 200)
response = await instance.request({ path: '/', method: 'GET' })
t.assert.strictEqual(response.statusCode, 503)
done()
})
})
test('rejected incoming connections should be logged', t => {
test('rejected incoming connections should be logged', (t, done) => {
t.plan(2)
const stream = split(JSON.parse)
const fastify = Fastify({
@@ -330,13 +299,14 @@ test('rejected incoming connections should be logged', t => {
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
const instance = new Client('http://localhost:' + fastify.server.address().port)
// initial request to trigger close
instance.request({ path: '/', method: 'GET' })
// subsequent request should be rejected
instance.request({ path: '/', method: 'GET' }).then(() => {
t.ok(messages.find(message => message.msg.includes('request aborted')))
t.assert.ok(messages.find(message => message.msg.includes('request aborted')))
done()
})
})
})
@@ -351,8 +321,8 @@ test('Cannot be reopened the closed server without listen callback', async t =>
try {
await fastify.listen({ port: 0 })
} catch (err) {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.assert.ok(err)
t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
}
})
@@ -368,15 +338,15 @@ test('Cannot be reopened the closed server has listen callback', async t => {
reject(err)
})
}).catch(err => {
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.ok(err)
t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.assert.ok(err)
})
})
const server = http.createServer()
const noSupport = typeof server.closeAllConnections !== 'function'
test('shutsdown while keep-alive connections are active (non-async, native)', { skip: noSupport }, t => {
test('shutsdown while keep-alive connections are active (non-async, native)', { skip: noSupport }, (t, done) => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
@@ -390,31 +360,32 @@ test('shutsdown while keep-alive connections are active (non-async, native)', {
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.assert.ifError(err)
const client = new Client(
'http://localhost:' + fastify.server.address().port,
{ keepAliveTimeout: 1 * 60 * 1000 }
)
client.request({ path: '/', method: 'GET' }, (err, response) => {
t.error(err)
t.equal(client.closed, false)
t.assert.ifError(err)
t.assert.strictEqual(client.closed, false)
fastify.close((err) => {
t.error(err)
t.assert.ifError(err)
// Due to the nature of the way we reap these keep-alive connections,
// there hasn't been enough time before the server fully closed in order
// for the client to have seen the socket get destroyed. The mere fact
// that we have reached this callback is enough indication that the
// feature being tested works as designed.
t.equal(client.closed, false)
t.assert.strictEqual(client.closed, false)
done()
})
})
})
})
test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, t => {
test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, (t, done) => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
@@ -428,25 +399,27 @@ test('shutsdown while keep-alive connections are active (non-async, idle, native
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.assert.ifError(err)
const client = new Client(
'http://localhost:' + fastify.server.address().port,
{ keepAliveTimeout: 1 * 60 * 1000 }
)
client.request({ path: '/', method: 'GET' }, (err, response) => {
t.error(err)
t.equal(client.closed, false)
t.assert.ifError(err)
t.assert.strictEqual(client.closed, false)
fastify.close((err) => {
t.error(err)
t.assert.ifError(err)
// Due to the nature of the way we reap these keep-alive connections,
// there hasn't been enough time before the server fully closed in order
// for the client to have seen the socket get destroyed. The mere fact
// that we have reached this callback is enough indication that the
// feature being tested works as designed.
t.equal(client.closed, false)
t.assert.strictEqual(client.closed, false)
done()
})
})
})
@@ -474,9 +447,9 @@ test('triggers on-close hook in the right order with multiple bindings', async t
setTimeout(() => {
fastify.close(err => {
order.push(3)
t.match(order, expectedOrder)
t.assert.deepEqual(order, expectedOrder)
if (err) t.error(err)
if (err) t.assert.ifError(err)
else resolve()
})
}, 2000)
@@ -519,20 +492,20 @@ test('triggers on-close hook in the right order with multiple bindings (forceClo
})
client.request({ path: '/', method: 'GET' })
.then((res) => res.body.json(), err => t.error(err))
.then((res) => res.body.json(), err => t.assert.ifError(err))
.then(json => {
t.match(json, expectedPayload, 'should payload match')
t.notOk(client.closed, 'should client not be closed')
}, err => t.error(err))
t.assert.deepEqual(json, expectedPayload, 'should payload match')
t.assert.ok(!client.closed, 'should client not be closed')
}, err => t.assert.ifError(err))
}
await new Promise((resolve, reject) => {
setTimeout(() => {
fastify.close(err => {
order.push(2)
t.match(order, expectedOrder)
t.assert.deepEqual(order, expectedOrder)
if (err) t.error(err)
if (err) t.assert.ifError(err)
else resolve()
})
}, 2000)
@@ -575,27 +548,27 @@ test('triggers on-close hook in the right order with multiple bindings (forceClo
})
client.request({ path: '/', method: 'GET' })
.then((res) => res.body.json(), err => t.error(err))
.then((res) => res.body.json(), err => t.assert.ifError(err))
.then(json => {
t.match(json, expectedPayload, 'should payload match')
t.notOk(client.closed, 'should client not be closed')
}, err => t.error(err))
t.assert.deepEqual(json, expectedPayload, 'should payload match')
t.assert.ok(!client.closed, 'should client not be closed')
}, err => t.assert.ifError(err))
}
await new Promise((resolve, reject) => {
setTimeout(() => {
fastify.close(err => {
order.push(2)
t.match(order, expectedOrder)
t.assert.deepEqual(order, expectedOrder)
if (err) t.error(err)
if (err) t.assert.ifError(err)
else resolve()
})
}, 2000)
})
})
test('shutsdown while keep-alive connections are active (non-async, custom)', t => {
test('shutsdown while keep-alive connections are active (non-async, custom)', (t, done) => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
@@ -618,53 +591,56 @@ test('shutsdown while keep-alive connections are active (non-async, custom)', t
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.assert.ifError(err)
const client = new Client(
'http://localhost:' + fastify.server.address().port,
{ keepAliveTimeout: 1 * 60 * 1000 }
)
client.request({ path: '/', method: 'GET' }, (err, response) => {
t.error(err)
t.equal(client.closed, false)
t.assert.ifError(err)
t.assert.strictEqual(client.closed, false)
fastify.close((err) => {
t.error(err)
t.assert.ifError(err)
// Due to the nature of the way we reap these keep-alive connections,
// there hasn't been enough time before the server fully closed in order
// for the client to have seen the socket get destroyed. The mere fact
// that we have reached this callback is enough indication that the
// feature being tested works as designed.
t.equal(client.closed, false)
t.assert.strictEqual(client.closed, false)
done()
})
})
})
})
test('preClose callback', t => {
test('preClose callback', (t, done) => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
let preCloseCalled = false
function onClose (instance, done) {
t.equal(preCloseCalled, true)
t.assert.strictEqual(preCloseCalled, true)
done()
}
fastify.addHook('preClose', preClose)
function preClose (done) {
t.type(this, fastify)
t.assert.ok(typeof this === typeof fastify)
preCloseCalled = true
done()
}
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
t.assert.ifError(err)
t.assert.ok('close callback')
done()
})
})
})
@@ -675,13 +651,13 @@ test('preClose async', async t => {
fastify.addHook('onClose', onClose)
let preCloseCalled = false
async function onClose () {
t.equal(preCloseCalled, true)
t.assert.strictEqual(preCloseCalled, true)
}
fastify.addHook('preClose', preClose)
async function preClose () {
preCloseCalled = true
t.type(this, fastify)
t.assert.ok(typeof this === typeof fastify)
}
await fastify.listen({ port: 0 })
@@ -689,13 +665,13 @@ test('preClose async', async t => {
await fastify.close()
})
test('preClose execution order', t => {
test('preClose execution order', (t, done) => {
t.plan(4)
const fastify = Fastify()
const order = []
fastify.addHook('onClose', onClose)
function onClose (instance, done) {
t.same(order, [1, 2, 3])
t.assert.deepStrictEqual(order, [1, 2, 3])
done()
}
@@ -719,11 +695,12 @@ test('preClose execution order', t => {
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
t.assert.ifError(err)
t.assert.ok('close callback')
done()
})
})
})

View File

@@ -0,0 +1,47 @@
'use strict'
const { test } = require('node:test')
test("pino is not require'd if logger is not passed", t => {
t.plan(1)
const fastify = require('..')
fastify()
t.assert.strictEqual(require.cache[require.resolve('pino')], undefined)
})
test("pino is require'd if logger is passed", t => {
t.plan(1)
const fastify = require('..')
fastify({
logger: true
})
t.assert.notStrictEqual(require.cache[require.resolve('pino')], undefined)
})
test("pino is require'd if loggerInstance is passed", t => {
t.plan(1)
const fastify = require('..')
const loggerInstance = {
fatal: (msg) => { },
error: (msg) => { },
warn: (msg) => { },
info: (msg) => { },
debug: (msg) => { },
trace: (msg) => { },
child: () => loggerInstance
}
fastify({
loggerInstance
})
t.assert.notStrictEqual(require.cache[require.resolve('pino')], undefined)
})

View File

@@ -2,34 +2,33 @@
const Fastify = require('..')
const http = require('node:http')
const t = require('tap')
const test = t.test
const { test } = require('node:test')
test('connectionTimeout', t => {
test('connectionTimeout', async t => {
t.plan(6)
try {
Fastify({ connectionTimeout: 1.3 })
t.fail('option must be an integer')
t.assert.fail('option must be an integer')
} catch (err) {
t.ok(err)
t.assert.ok(err)
}
try {
Fastify({ connectionTimeout: [] })
t.fail('option must be an integer')
t.assert.fail('option must be an integer')
} catch (err) {
t.ok(err)
t.assert.ok(err)
}
const httpServer = Fastify({ connectionTimeout: 1 }).server
t.equal(httpServer.timeout, 1)
t.assert.strictEqual(httpServer.timeout, 1)
const httpsServer = Fastify({ connectionTimeout: 2, https: {} }).server
t.equal(httpsServer.timeout, 2)
t.assert.strictEqual(httpsServer.timeout, 2)
const http2Server = Fastify({ connectionTimeout: 3, http2: true }).server
t.equal(http2Server.timeout, 3)
t.assert.strictEqual(http2Server.timeout, 3)
const serverFactory = (handler, _) => {
const server = http.createServer((req, res) => {
@@ -39,5 +38,5 @@ test('connectionTimeout', t => {
return server
}
const customServer = Fastify({ connectionTimeout: 4, serverFactory }).server
t.equal(customServer.timeout, 5)
t.assert.strictEqual(customServer.timeout, 5)
})

View File

@@ -1,11 +1,10 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../fastify')
test('Should register a host constrained route', t => {
t.plan(7)
test('Should register a host constrained route', async t => {
t.plan(4)
const fastify = Fastify()
fastify.route({
@@ -17,40 +16,41 @@ test('Should register a host constrained route', t => {
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'world' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'world' })
t.assert.strictEqual(res.statusCode, 200)
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
t.assert.strictEqual(res.statusCode, 404)
}
{
const res = await fastify.inject({
method: 'GET',
url: '/'
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Should register the same route with host constraints', t => {
t.plan(8)
test('Should register the same route with host constraints', async t => {
t.plan(5)
const fastify = Fastify()
fastify.route({
@@ -71,44 +71,45 @@ test('Should register the same route with host constraints', t => {
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload, 'fastify.dev')
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
})
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'fastify.dev')
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload, 'example.com')
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fancy.ca'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'example.com')
}
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fancy.ca'
}
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Should allow registering custom constrained routes', t => {
t.plan(8)
test('Should allow registering custom constrained routes', async t => {
t.plan(5)
const constraint = {
name: 'secret',
@@ -145,44 +146,44 @@ test('Should allow registering custom constrained routes', t => {
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'alpha'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from alpha' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'alpha'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from alpha' })
t.assert.strictEqual(res.statusCode, 200)
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'beta'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from beta' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'beta'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from beta' })
t.assert.strictEqual(res.statusCode, 200)
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Should allow registering custom constrained routes outside constructor', t => {
t.plan(8)
test('Should allow registering custom constrained routes outside constructor', async t => {
t.plan(5)
const constraint = {
name: 'secret',
@@ -220,43 +221,44 @@ test('Should allow registering custom constrained routes outside constructor', t
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'alpha'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from alpha' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'alpha'
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'beta'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from beta' })
t.equal(res.statusCode, 200)
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from alpha' })
t.assert.strictEqual(res.statusCode, 200)
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'beta'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from beta' })
t.assert.strictEqual(res.statusCode, 200)
}
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Custom constrained routes registered also for HEAD method generated by fastify', t => {
test('Custom constrained routes registered also for HEAD method generated by fastify', (t, done) => {
t.plan(3)
const constraint = {
@@ -292,13 +294,14 @@ test('Custom constrained routes registered also for HEAD method generated by fas
'X-Secret': 'mySecret'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '31')
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.headers['content-length'], '31')
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
test('Custom constrained routes registered with addConstraintStrategy apply also for HEAD method generated by fastify', t => {
test('Custom constrained routes registered with addConstraintStrategy apply also for HEAD method generated by fastify', (t, done) => {
t.plan(3)
const constraint = {
@@ -335,13 +338,14 @@ test('Custom constrained routes registered with addConstraintStrategy apply also
'X-Secret': 'mySecret'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '31')
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.headers['content-length'], '31')
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
test('Add a constraint strategy after fastify instance was started', t => {
test('Add a constraint strategy after fastify instance was started', (t, done) => {
t.plan(4)
const constraint = {
@@ -371,14 +375,14 @@ test('Add a constraint strategy after fastify instance was started', t => {
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same(res.payload, 'ok')
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.payload, 'ok')
t.assert.strictEqual(res.statusCode, 200)
t.throws(
() => fastify.addConstraintStrategy(constraint),
'Cannot add constraint strategy when fastify instance is already started!'
t.assert.throws(
() => fastify.addConstraintStrategy(constraint)
)
done()
})
})
@@ -403,9 +407,9 @@ test('Add a constraint strategy should throw an error if there already exist cus
const fastify = Fastify()
fastify.addConstraintStrategy(constraint)
t.throws(
t.assert.throws(
() => fastify.addConstraintStrategy(constraint),
'There already exists a custom constraint with the name secret.'
/^Error: There already exists a custom constraint with the name secret.$/
)
})
@@ -430,7 +434,7 @@ test('Add a constraint strategy shouldn\'t throw an error if default constraint
const fastify = Fastify()
fastify.addConstraintStrategy(constraint)
t.pass()
t.assert.ok(true)
})
test('Add a constraint strategy should throw an error if default constraint with the same name is used', t => {
@@ -462,9 +466,9 @@ test('Add a constraint strategy should throw an error if default constraint with
}
})
t.throws(
t.assert.throws(
() => fastify.addConstraintStrategy(constraint),
'There already exists a route with version constraint.'
/^Error: There already exists a route with version constraint.$/
)
})
@@ -473,8 +477,8 @@ test('The hasConstraintStrategy should return false for default constraints unti
const fastify = Fastify()
t.equal(fastify.hasConstraintStrategy('version'), false)
t.equal(fastify.hasConstraintStrategy('host'), false)
t.assert.strictEqual(fastify.hasConstraintStrategy('version'), false)
t.assert.strictEqual(fastify.hasConstraintStrategy('host'), false)
fastify.route({
method: 'GET',
@@ -485,8 +489,8 @@ test('The hasConstraintStrategy should return false for default constraints unti
}
})
t.equal(fastify.hasConstraintStrategy('version'), false)
t.equal(fastify.hasConstraintStrategy('host'), true)
t.assert.strictEqual(fastify.hasConstraintStrategy('version'), false)
t.assert.strictEqual(fastify.hasConstraintStrategy('host'), true)
fastify.route({
method: 'GET',
@@ -497,8 +501,8 @@ test('The hasConstraintStrategy should return false for default constraints unti
}
})
t.equal(fastify.hasConstraintStrategy('version'), true)
t.equal(fastify.hasConstraintStrategy('host'), true)
t.assert.strictEqual(fastify.hasConstraintStrategy('version'), true)
t.assert.strictEqual(fastify.hasConstraintStrategy('host'), true)
})
test('The hasConstraintStrategy should return true if there already exist a custom constraint with the same name', t => {
@@ -521,13 +525,13 @@ test('The hasConstraintStrategy should return true if there already exist a cust
const fastify = Fastify()
t.equal(fastify.hasConstraintStrategy('secret'), false)
t.assert.strictEqual(fastify.hasConstraintStrategy('secret'), false)
fastify.addConstraintStrategy(constraint)
t.equal(fastify.hasConstraintStrategy('secret'), true)
t.assert.strictEqual(fastify.hasConstraintStrategy('secret'), true)
})
test('Should allow registering an unconstrained route after a constrained route', t => {
t.plan(6)
test('Should allow registering an unconstrained route after a constrained route', async t => {
t.plan(4)
const fastify = Fastify()
fastify.route({
@@ -547,32 +551,32 @@ test('Should allow registering an unconstrained route after a constrained route'
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from fastify.dev' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fastify.dev'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from fastify.dev' })
t.assert.strictEqual(res.statusCode, 200)
}
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from any other domain' })
t.equal(res.statusCode, 200)
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from any other domain' })
t.assert.strictEqual(res.statusCode, 200)
}
})
test('Should allow registering constrained routes in a prefixed plugin', t => {
test('Should allow registering constrained routes in a prefixed plugin', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -595,13 +599,14 @@ test('Should allow registering constrained routes in a prefixed plugin', t => {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { ok: true })
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), { ok: true })
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
test('Should allow registering a constrained GET route after a constrained HEAD route', t => {
test('Should allow registering a constrained GET route after a constrained HEAD route', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -631,13 +636,14 @@ test('Should allow registering a constrained GET route after a constrained HEAD
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(res.payload, 'custom HEAD response')
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.payload, 'custom HEAD response')
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
test('Should allow registering a constrained GET route after an unconstrained HEAD route', t => {
test('Should allow registering a constrained GET route after an unconstrained HEAD route', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -667,9 +673,10 @@ test('Should allow registering a constrained GET route after an unconstrained HE
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '41')
t.equal(res.statusCode, 200)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.headers['content-length'], '41')
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
@@ -702,7 +709,7 @@ test('Will not try to re-createprefixed HEAD route if it already exists and expo
await fastify.ready()
t.ok(true)
t.assert.ok(true)
})
test('allows separate constrained and unconstrained HEAD routes', async (t) => {
@@ -744,7 +751,7 @@ test('allows separate constrained and unconstrained HEAD routes', async (t) => {
await fastify.ready()
t.ok(true)
t.assert.ok(true)
})
test('allow async constraints', async (t) => {
@@ -787,17 +794,17 @@ test('allow async constraints', async (t) => {
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'alpha' } })
t.same(JSON.parse(payload), { hello: 'from alpha' })
t.equal(statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(payload), { hello: 'from alpha' })
t.assert.strictEqual(statusCode, 200)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'beta' } })
t.same(JSON.parse(payload), { hello: 'from beta' })
t.equal(statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(payload), { hello: 'from beta' })
t.assert.strictEqual(statusCode, 200)
}
{
const { statusCode } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'gamma' } })
t.equal(statusCode, 404)
t.assert.strictEqual(statusCode, 404)
}
})
@@ -841,28 +848,28 @@ test('error in async constraints', async (t) => {
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'alpha' } })
t.same(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.equal(statusCode, 500)
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'beta' } })
t.same(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.equal(statusCode, 500)
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'gamma' } })
t.same(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.equal(statusCode, 500)
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/' })
t.same(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.equal(statusCode, 500)
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
})
test('Allow regex constraints in routes', t => {
t.plan(5)
test('Allow regex constraints in routes', async t => {
t.plan(3)
const fastify = Fastify()
@@ -875,26 +882,257 @@ test('Allow regex constraints in routes', t => {
}
})
fastify.inject({
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'dev.fastify.dev'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from fastify dev domain' })
t.assert.strictEqual(res.statusCode, 200)
}
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'google.com'
}
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Should allow registering custom rotuerOptions constrained routes', async t => {
t.plan(5)
const constraint = {
name: 'secret',
storage: function () {
const secrets = {}
return {
get: (secret) => { return secrets[secret] || null },
set: (secret, store) => { secrets[secret] = store }
}
},
deriveConstraint: (req, ctx) => {
return req.headers['x-secret']
},
validate () { return true }
}
const fastify = Fastify({ routerOptions: { constraints: { secret: constraint } } })
fastify.route({
method: 'GET',
url: '/',
headers: {
host: 'dev.fastify.dev'
constraints: { secret: 'alpha' },
handler: (req, reply) => {
reply.send({ hello: 'from alpha' })
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'beta' },
handler: (req, reply) => {
reply.send({ hello: 'from beta' })
}
})
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'alpha'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from alpha' })
t.assert.strictEqual(res.statusCode, 200)
}
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'beta'
}
})
t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'from beta' })
t.assert.strictEqual(res.statusCode, 200)
}
{
const res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
})
t.assert.strictEqual(res.statusCode, 404)
}
})
test('Custom rotuerOptions constrained routes registered also for HEAD method generated by fastify', (t, done) => {
t.plan(3)
const constraint = {
name: 'secret',
storage: function () {
const secrets = {}
return {
get: (secret) => { return secrets[secret] || null },
set: (secret, store) => { secrets[secret] = store }
}
},
deriveConstraint: (req, ctx) => {
return req.headers['x-secret']
},
validate () { return true }
}
const fastify = Fastify({ routerOptions: { constraints: { secret: constraint } } })
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'mySecret' },
handler: (req, reply) => {
reply.send('from mySecret - my length is 31')
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { hello: 'from fastify dev domain' })
t.equal(res.statusCode, 200)
})
fastify.inject({
method: 'GET',
method: 'HEAD',
url: '/',
headers: {
host: 'google.com'
'X-Secret': 'mySecret'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
t.assert.ifError(err)
t.assert.deepStrictEqual(res.headers['content-length'], '31')
t.assert.strictEqual(res.statusCode, 200)
done()
})
})
test('allow async rotuerOptions constraints', async (t) => {
t.plan(5)
const constraint = {
name: 'secret',
storage: function () {
const secrets = {}
return {
get: (secret) => { return secrets[secret] || null },
set: (secret, store) => { secrets[secret] = store }
}
},
deriveConstraint: (req, ctx, done) => {
done(null, req.headers['x-secret'])
},
validate () { return true }
}
const fastify = Fastify({ routerOptions: { constraints: { secret: constraint } } })
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'alpha' },
handler: (req, reply) => {
reply.send({ hello: 'from alpha' })
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'beta' },
handler: (req, reply) => {
reply.send({ hello: 'from beta' })
}
})
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'alpha' } })
t.assert.deepStrictEqual(JSON.parse(payload), { hello: 'from alpha' })
t.assert.strictEqual(statusCode, 200)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'beta' } })
t.assert.deepStrictEqual(JSON.parse(payload), { hello: 'from beta' })
t.assert.strictEqual(statusCode, 200)
}
{
const { statusCode } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'gamma' } })
t.assert.strictEqual(statusCode, 404)
}
})
test('error in async rotuerOptions constraints', async (t) => {
t.plan(8)
const constraint = {
name: 'secret',
storage: function () {
const secrets = {}
return {
get: (secret) => { return secrets[secret] || null },
set: (secret, store) => { secrets[secret] = store }
}
},
deriveConstraint: (req, ctx, done) => {
done(Error('kaboom'))
},
validate () { return true }
}
const fastify = Fastify({ routerOptions: { constraints: { secret: constraint } } })
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'alpha' },
handler: (req, reply) => {
reply.send({ hello: 'from alpha' })
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'beta' },
handler: (req, reply) => {
reply.send({ hello: 'from beta' })
}
})
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'alpha' } })
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'beta' } })
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'gamma' } })
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
{
const { statusCode, payload } = await fastify.inject({ method: 'GET', path: '/' })
t.assert.deepStrictEqual(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.assert.strictEqual(statusCode, 500)
}
})

View File

@@ -1,11 +1,10 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
test('default 413 with bodyLimit option', t => {
t.plan(4)
test('default 413 with bodyLimit option', async (t) => {
t.plan(3)
const fastify = Fastify({
bodyLimit: 10
@@ -15,27 +14,25 @@ test('default 413 with bodyLimit option', t => {
reply.send({ hello: 'world' })
})
fastify.inject({
const response = await fastify.inject({
method: 'POST',
url: '/',
body: {
text: '12345678901234567890123456789012345678901234567890'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 413)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
error: 'Payload Too Large',
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
message: 'Request body is too large',
statusCode: 413
})
})
t.assert.strictEqual(response.statusCode, 413)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(response.payload), {
error: 'Payload Too Large',
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
message: 'Request body is too large',
statusCode: 413
})
})
test('default 400 with wrong content-length', t => {
t.plan(4)
test('default 400 with wrong content-length', async (t) => {
t.plan(3)
const fastify = Fastify()
@@ -43,7 +40,7 @@ test('default 400 with wrong content-length', t => {
reply.send({ hello: 'world' })
})
fastify.inject({
const response = await fastify.inject({
method: 'POST',
url: '/',
headers: {
@@ -52,21 +49,19 @@ test('default 400 with wrong content-length', t => {
body: {
text: '12345678901234567890123456789012345678901234567890'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
message: 'Request body size did not match Content-Length',
statusCode: 400
})
})
t.assert.strictEqual(response.statusCode, 400)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(response.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
message: 'Request body size did not match Content-Length',
statusCode: 400
})
})
test('custom 413 with bodyLimit option', t => {
t.plan(4)
test('custom 413 with bodyLimit option', async (t) => {
t.plan(3)
const fastify = Fastify({
bodyLimit: 10
@@ -83,27 +78,25 @@ test('custom 413 with bodyLimit option', t => {
.send(err)
})
fastify.inject({
const response = await fastify.inject({
method: 'POST',
url: '/',
body: {
text: '12345678901234567890123456789012345678901234567890'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 413)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
error: 'Payload Too Large',
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
message: 'Request body is too large',
statusCode: 413
})
})
t.assert.strictEqual(response.statusCode, 413)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(response.payload), {
error: 'Payload Too Large',
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
message: 'Request body is too large',
statusCode: 413
})
})
test('custom 400 with wrong content-length', t => {
t.plan(4)
test('custom 400 with wrong content-length', async (t) => {
t.plan(3)
const fastify = Fastify()
@@ -118,7 +111,7 @@ test('custom 400 with wrong content-length', t => {
.send(err)
})
fastify.inject({
const response = await fastify.inject({
method: 'POST',
url: '/',
headers: {
@@ -127,20 +120,18 @@ test('custom 400 with wrong content-length', t => {
body: {
text: '12345678901234567890123456789012345678901234567890'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.same(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
message: 'Request body size did not match Content-Length',
statusCode: 400
})
})
t.assert.strictEqual(response.statusCode, 400)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(JSON.parse(response.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
message: 'Request body size did not match Content-Length',
statusCode: 400
})
})
test('#2214 - wrong content-length', t => {
test('#2214 - wrong content-length', async (t) => {
const fastify = Fastify()
fastify.get('/', async () => {
@@ -151,17 +142,14 @@ test('#2214 - wrong content-length', t => {
throw error
})
fastify.inject({
const response = await fastify.inject({
method: 'GET',
path: '/'
})
.then(response => {
t.equal(response.headers['content-length'], '' + response.rawPayload.length)
t.end()
})
t.assert.strictEqual(response.headers['content-length'], '' + response.rawPayload.length)
})
test('#2543 - wrong content-length with errorHandler', t => {
test('#2543 - wrong content-length with errorHandler', async (t) => {
const fastify = Fastify()
fastify.setErrorHandler((_error, _request, reply) => {
@@ -176,14 +164,11 @@ test('#2543 - wrong content-length with errorHandler', t => {
throw error
})
fastify.inject({
const response = await fastify.inject({
method: 'GET',
path: '/'
})
.then(res => {
t.equal(res.statusCode, 500)
t.equal(res.headers['content-length'], '' + res.rawPayload.length)
t.same(JSON.parse(res.payload), { message: 'longer than 2 bytes' })
t.end()
})
t.assert.strictEqual(response.statusCode, 500)
t.assert.strictEqual(response.headers['content-length'], '' + response.rawPayload.length)
t.assert.deepStrictEqual(JSON.parse(response.payload), { message: 'longer than 2 bytes' })
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const keys = require('../lib/symbols')
const { FST_ERR_CTP_ALREADY_PRESENT, FST_ERR_CTP_INVALID_TYPE, FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('../lib/errors')
@@ -10,42 +9,46 @@ const first = function (req, payload, done) {}
const second = function (req, payload, done) {}
const third = function (req, payload, done) {}
test('hasContentTypeParser', t => {
test('should know about internal parsers', t => {
t.plan(4)
test('hasContentTypeParser', async t => {
await t.test('should know about internal parsers', (t, done) => {
t.plan(5)
const fastify = Fastify()
fastify.ready(err => {
t.error(err)
t.ok(fastify.hasContentTypeParser('application/json'))
t.ok(fastify.hasContentTypeParser('text/plain'))
t.notOk(fastify.hasContentTypeParser('application/jsoff'))
t.assert.ifError(err)
t.assert.ok(fastify.hasContentTypeParser('application/json'))
t.assert.ok(fastify.hasContentTypeParser('text/plain'))
t.assert.ok(fastify.hasContentTypeParser(' text/plain '))
t.assert.ok(!fastify.hasContentTypeParser('application/jsoff'))
done()
})
})
test('should work with string and RegExp', t => {
t.plan(7)
await t.test('should only work with string and RegExp', t => {
t.plan(8)
const fastify = Fastify()
fastify.addContentTypeParser(/^image\/.*/, first)
fastify.addContentTypeParser(/^application\/.+\+xml/, first)
fastify.addContentTypeParser('image/gif', first)
t.ok(fastify.hasContentTypeParser('application/json'))
t.ok(fastify.hasContentTypeParser(/^image\/.*/))
t.ok(fastify.hasContentTypeParser(/^application\/.+\+xml/))
t.ok(fastify.hasContentTypeParser('image/gif'))
t.notOk(fastify.hasContentTypeParser(/^image\/.+\+xml/))
t.notOk(fastify.hasContentTypeParser('image/png'))
t.notOk(fastify.hasContentTypeParser('*'))
t.assert.ok(fastify.hasContentTypeParser('application/json'))
t.assert.ok(fastify.hasContentTypeParser(/^image\/.*/))
t.assert.ok(fastify.hasContentTypeParser(/^application\/.+\+xml/))
t.assert.ok(fastify.hasContentTypeParser('image/gif'))
t.assert.ok(!fastify.hasContentTypeParser(/^image\/.+\+xml/))
t.assert.ok(!fastify.hasContentTypeParser('image/png'))
t.assert.ok(!fastify.hasContentTypeParser('*'))
t.assert.throws(
() => fastify.hasContentTypeParser(123),
FST_ERR_CTP_INVALID_TYPE
)
})
t.end()
})
test('getParser', t => {
test('should return matching parser', t => {
t.plan(3)
test('getParser', async t => {
await t.test('should return matching parser', t => {
t.plan(7)
const fastify = Fastify()
@@ -53,27 +56,63 @@ test('getParser', t => {
fastify.addContentTypeParser(/^application\/.+\+xml/, second)
fastify.addContentTypeParser('text/html', third)
t.equal(fastify[keys.kContentTypeParser].getParser('application/t+xml').fn, second)
t.equal(fastify[keys.kContentTypeParser].getParser('image/png').fn, first)
t.equal(fastify[keys.kContentTypeParser].getParser('text/html').fn, third)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('application/t+xml').fn, second)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('image/png').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html').fn, third)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html; charset=utf-8').fn, third)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html ; charset=utf-8').fn, third)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html\t; charset=utf-8').fn, third)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/htmlINVALID')?.fn, undefined)
})
test('should return matching parser with caching', t => {
await t.test('should return matching parser with caching /1', t => {
t.plan(6)
const fastify = Fastify()
fastify.addContentTypeParser('text/html', first)
t.equal(fastify[keys.kContentTypeParser].getParser('text/html').fn, first)
t.equal(fastify[keys.kContentTypeParser].cache.size, 0)
t.equal(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
t.equal(fastify[keys.kContentTypeParser].cache.size, 1)
t.equal(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
t.equal(fastify[keys.kContentTypeParser].cache.size, 1)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 0)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 1)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 1)
})
test('should prefer content type parser with string value', t => {
await t.test('should return matching parser with caching /2', t => {
t.plan(8)
const fastify = Fastify()
fastify.addContentTypeParser('text/html', first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 0)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/HTML').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 1)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('TEXT/html').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 2)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('TEXT/html').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 2)
})
await t.test('should return matching parser with caching /3', t => {
t.plan(6)
const fastify = Fastify()
fastify.addContentTypeParser(/^text\/html(;\s*charset=[^;]+)?$/, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 1)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html;charset=utf-8').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 2)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html;charset=utf-8').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].cache.size, 2)
})
await t.test('should prefer content type parser with string value', t => {
t.plan(2)
const fastify = Fastify()
@@ -81,11 +120,11 @@ test('getParser', t => {
fastify.addContentTypeParser(/^image\/.*/, first)
fastify.addContentTypeParser('image/gif', second)
t.equal(fastify[keys.kContentTypeParser].getParser('image/gif').fn, second)
t.equal(fastify[keys.kContentTypeParser].getParser('image/png').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('image/gif').fn, second)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('image/png').fn, first)
})
test('should return parser that catches all if no other is set', t => {
await t.test('should return parser that catches all if no other is set', t => {
t.plan(3)
const fastify = Fastify()
@@ -93,12 +132,12 @@ test('getParser', t => {
fastify.addContentTypeParser('*', first)
fastify.addContentTypeParser(/^text\/.*/, second)
t.equal(fastify[keys.kContentTypeParser].getParser('image/gif').fn, first)
t.equal(fastify[keys.kContentTypeParser].getParser('text/html').fn, second)
t.equal(fastify[keys.kContentTypeParser].getParser('text').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('image/gif').fn, first)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text/html').fn, second)
t.assert.strictEqual(fastify[keys.kContentTypeParser].getParser('text').fn, first)
})
test('should return undefined if no matching parser exist', t => {
await t.test('should return undefined if no matching parser exist', t => {
t.plan(2)
const fastify = Fastify()
@@ -106,15 +145,13 @@ test('getParser', t => {
fastify.addContentTypeParser(/^weirdType\/.+/, first)
fastify.addContentTypeParser('application/javascript', first)
t.notOk(fastify[keys.kContentTypeParser].getParser('application/xml'))
t.notOk(fastify[keys.kContentTypeParser].getParser('weirdType/'))
t.assert.ok(!fastify[keys.kContentTypeParser].getParser('application/xml'))
t.assert.ok(!fastify[keys.kContentTypeParser].getParser('weirdType/'))
})
t.end()
})
test('existingParser', t => {
test('returns always false for "*"', t => {
test('existingParser', async t => {
await t.test('returns always false for "*"', t => {
t.plan(2)
const fastify = Fastify()
@@ -123,14 +160,14 @@ test('existingParser', t => {
fastify.addContentTypeParser(/^application\/.+\+xml/, first)
fastify.addContentTypeParser('text/html', first)
t.notOk(fastify[keys.kContentTypeParser].existingParser('*'))
t.assert.ok(!fastify[keys.kContentTypeParser].existingParser('*'))
fastify.addContentTypeParser('*', first)
t.notOk(fastify[keys.kContentTypeParser].existingParser('*'))
t.assert.ok(!fastify[keys.kContentTypeParser].existingParser('*'))
})
test('let you override the default parser once', t => {
await t.test('let you override the default parser once', t => {
t.plan(2)
const fastify = Fastify()
@@ -138,15 +175,13 @@ test('existingParser', t => {
fastify.addContentTypeParser('application/json', first)
fastify.addContentTypeParser('text/plain', first)
t.throws(
t.assert.throws(
() => fastify.addContentTypeParser('application/json', first),
FST_ERR_CTP_ALREADY_PRESENT,
"Content type parser 'application/json' already present"
FST_ERR_CTP_ALREADY_PRESENT
)
t.throws(
t.assert.throws(
() => fastify.addContentTypeParser('text/plain', first),
FST_ERR_CTP_ALREADY_PRESENT,
"Content type parser 'text/plain' already present"
FST_ERR_CTP_ALREADY_PRESENT
)
})
@@ -157,52 +192,78 @@ test('existingParser', t => {
fastify.addContentTypeParser(/^application\/.+\+xml/, first)
fastify.addContentTypeParser('text/html', first)
t.ok(contentTypeParser.existingParser(/^image\/.*/))
t.ok(contentTypeParser.existingParser('text/html'))
t.ok(contentTypeParser.existingParser(/^application\/.+\+xml/))
t.notOk(contentTypeParser.existingParser('application/json'))
t.notOk(contentTypeParser.existingParser('text/plain'))
t.notOk(contentTypeParser.existingParser('image/png'))
t.notOk(contentTypeParser.existingParser(/^application\/.+\+json/))
t.end()
t.assert.ok(contentTypeParser.existingParser(/^image\/.*/))
t.assert.ok(contentTypeParser.existingParser('text/html'))
t.assert.ok(contentTypeParser.existingParser(/^application\/.+\+xml/))
t.assert.ok(!contentTypeParser.existingParser('application/json'))
t.assert.ok(!contentTypeParser.existingParser('text/plain'))
t.assert.ok(!contentTypeParser.existingParser('image/png'))
t.assert.ok(!contentTypeParser.existingParser(/^application\/.+\+json/))
})
test('add', t => {
test('should only accept string and RegExp', t => {
test('add', async t => {
await t.test('should only accept string and RegExp', t => {
t.plan(4)
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
t.error(contentTypeParser.add('test', {}, first))
t.error(contentTypeParser.add(/test/, {}, first))
t.throws(
t.assert.ifError(contentTypeParser.add('test', {}, first))
t.assert.ifError(contentTypeParser.add(/test/, {}, first))
t.assert.throws(
() => contentTypeParser.add({}, {}, first),
FST_ERR_CTP_INVALID_TYPE,
'The content type should be a string or a RegExp'
)
t.throws(
t.assert.throws(
() => contentTypeParser.add(1, {}, first),
FST_ERR_CTP_INVALID_TYPE,
'The content type should be a string or a RegExp'
)
})
test('should set "*" as parser that catches all', t => {
await t.test('should set "*" as parser that catches all', t => {
t.plan(1)
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
contentTypeParser.add('*', {}, first)
t.equal(contentTypeParser.customParsers.get('').fn, first)
t.assert.strictEqual(contentTypeParser.customParsers.get('').fn, first)
})
t.end()
await t.test('should lowercase contentTypeParser name', async t => {
t.plan(1)
const fastify = Fastify()
fastify.addContentTypeParser('text/html', function (req, done) {
done()
})
try {
fastify.addContentTypeParser('TEXT/html', function (req, done) {
done()
})
} catch (err) {
t.assert.strictEqual(err.message, FST_ERR_CTP_ALREADY_PRESENT('text/html').message)
}
})
await t.test('should trim contentTypeParser name', async t => {
t.plan(1)
const fastify = Fastify()
fastify.addContentTypeParser('text/html', function (req, done) {
done()
})
try {
fastify.addContentTypeParser(' text/html', function (req, done) {
done()
})
} catch (err) {
t.assert.strictEqual(err.message, FST_ERR_CTP_ALREADY_PRESENT('text/html').message)
}
})
})
test('non-Error thrown from content parser is properly handled', t => {
test('non-Error thrown from content parser is properly handled', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -218,7 +279,7 @@ test('non-Error thrown from content parser is properly handled', t => {
})
fastify.setErrorHandler((err, req, res) => {
t.equal(err, throwable)
t.assert.strictEqual(err, throwable)
res.send(payload)
})
@@ -229,12 +290,13 @@ test('non-Error thrown from content parser is properly handled', t => {
headers: { 'Content-Type': 'text/test' },
body: 'some text'
}, (err, res) => {
t.error(err)
t.equal(res.payload, payload)
t.assert.ifError(err)
t.assert.strictEqual(res.payload, payload)
done()
})
})
test('Error thrown 415 from content type is null and make post request to server', t => {
test('Error thrown 415 from content type is null and make post request to server', (t, done) => {
t.plan(3)
const fastify = Fastify()
@@ -248,25 +310,29 @@ test('Error thrown 415 from content type is null and make post request to server
url: '/',
body: 'some text'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 415)
t.equal(JSON.parse(res.body).message, errMsg)
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 415)
t.assert.strictEqual(JSON.parse(res.body).message, errMsg)
done()
})
})
test('remove', t => {
test('should remove default parser', t => {
t.plan(3)
test('remove', async t => {
await t.test('should remove default parser', t => {
t.plan(6)
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
t.ok(contentTypeParser.remove('application/json'))
t.notOk(contentTypeParser.customParsers['application/json'])
t.notOk(contentTypeParser.parserList.find(parser => parser === 'application/json'))
t.assert.ok(contentTypeParser.remove('application/json'))
t.assert.ok(!contentTypeParser.customParsers['application/json'])
t.assert.ok(!contentTypeParser.parserList.find(parser => parser === 'application/json'))
t.assert.ok(contentTypeParser.remove(' text/plain '))
t.assert.ok(!contentTypeParser.customParsers['text/plain'])
t.assert.ok(!contentTypeParser.parserList.find(parser => parser === 'text/plain'))
})
test('should remove RegExp parser', t => {
await t.test('should remove RegExp parser', t => {
t.plan(3)
const fastify = Fastify()
@@ -274,39 +340,37 @@ test('remove', t => {
const contentTypeParser = fastify[keys.kContentTypeParser]
t.ok(contentTypeParser.remove(/^text\/*/))
t.notOk(contentTypeParser.customParsers[/^text\/*/])
t.notOk(contentTypeParser.parserRegExpList.find(parser => parser.toString() === /^text\/*/.toString()))
t.assert.ok(contentTypeParser.remove(/^text\/*/))
t.assert.ok(!contentTypeParser.customParsers[/^text\/*/])
t.assert.ok(!contentTypeParser.parserRegExpList.find(parser => parser.toString() === /^text\/*/.toString()))
})
test('should throw an error if content type is neither string nor RegExp', t => {
await t.test('should throw an error if content type is neither string nor RegExp', t => {
t.plan(1)
const fastify = Fastify()
t.throws(() => fastify[keys.kContentTypeParser].remove(12), FST_ERR_CTP_INVALID_TYPE)
t.assert.throws(() => fastify[keys.kContentTypeParser].remove(12), FST_ERR_CTP_INVALID_TYPE)
})
test('should return false if content type does not exist', t => {
await t.test('should return false if content type does not exist', t => {
t.plan(1)
const fastify = Fastify()
t.notOk(fastify[keys.kContentTypeParser].remove('image/png'))
t.assert.ok(!fastify[keys.kContentTypeParser].remove('image/png'))
})
test('should not remove any content type parser if content type does not exist', t => {
await t.test('should not remove any content type parser if content type does not exist', t => {
t.plan(2)
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
t.notOk(contentTypeParser.remove('image/png'))
t.same(contentTypeParser.customParsers.size, 2)
t.assert.ok(!contentTypeParser.remove('image/png'))
t.assert.strictEqual(contentTypeParser.customParsers.size, 2)
})
t.end()
})
test('remove all should remove all existing parsers and reset cache', t => {
@@ -321,10 +385,10 @@ test('remove all should remove all existing parsers and reset cache', t => {
contentTypeParser.getParser('application/xml') // fill cache with one entry
contentTypeParser.removeAll()
t.same(contentTypeParser.cache.size, 0)
t.same(contentTypeParser.parserList.length, 0)
t.same(contentTypeParser.parserRegExpList.length, 0)
t.same(Object.keys(contentTypeParser.customParsers).length, 0)
t.assert.strictEqual(contentTypeParser.cache.size, 0)
t.assert.strictEqual(contentTypeParser.parserList.length, 0)
t.assert.strictEqual(contentTypeParser.parserRegExpList.length, 0)
t.assert.strictEqual(Object.keys(contentTypeParser.customParsers).length, 0)
})
test('Safeguard against malicious content-type / 1', async t => {
@@ -347,7 +411,7 @@ test('Safeguard against malicious content-type / 1', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
}
})
@@ -369,7 +433,7 @@ test('Safeguard against malicious content-type / 2', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
})
test('Safeguard against malicious content-type / 3', async t => {
@@ -390,7 +454,7 @@ test('Safeguard against malicious content-type / 3', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
})
test('Safeguard against content-type spoofing - string', async t => {
@@ -399,11 +463,11 @@ test('Safeguard against content-type spoofing - string', async t => {
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain', function (request, body, done) {
t.pass('should be called')
t.assert.ok('should be called')
done(null, body)
})
fastify.addContentTypeParser('application/json', function (request, body, done) {
t.fail('shouldn\'t be called')
t.assert.fail('shouldn\'t be called')
done(null, body)
})
@@ -421,31 +485,42 @@ test('Safeguard against content-type spoofing - string', async t => {
})
})
test('Safeguard against content-type spoofing - regexp', async t => {
t.plan(1)
test('Warning against improper content-type - regexp', async t => {
await t.test('improper regex - text plain', (t, done) => {
t.plan(2)
const fastify = Fastify()
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
process.on('warning', onWarning)
function onWarning (warning) {
t.assert.strictEqual(warning.name, 'FastifySecurity')
t.assert.strictEqual(warning.code, 'FSTSEC001')
done()
}
t.after(() => process.removeListener('warning', onWarning))
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
done(null, body)
})
})
fastify.post('/', async () => {
return 'ok'
})
await t.test('improper regex - application json', (t, done) => {
t.plan(2)
const fastify = Fastify()
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'text/plain; content-type="application/json"'
},
body: ''
process.on('warning', onWarning)
function onWarning (warning) {
t.assert.strictEqual(warning.name, 'FastifySecurity')
t.assert.strictEqual(warning.code, 'FSTSEC001')
done()
}
t.after(() => process.removeListener('warning', onWarning))
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
done(null, body)
})
})
})
@@ -455,11 +530,11 @@ test('content-type match parameters - string 1', async t => {
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain; charset=utf8', function (request, body, done) {
t.fail('shouldn\'t be called')
t.assert.fail('shouldn\'t be called')
done(null, body)
})
fastify.addContentTypeParser('application/json; charset=utf8', function (request, body, done) {
t.pass('should be called')
t.assert.ok('should be called')
done(null, body)
})
@@ -477,41 +552,13 @@ test('content-type match parameters - string 1', async t => {
})
})
test('content-type match parameters - string 2', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.addContentTypeParser('text/plain; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; foo=bar; charset=utf8'
},
body: ''
})
})
test('content-type match parameters - regexp', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json; charset=utf8/, function (request, body, done) {
t.pass('should be called')
t.assert.ok('should be called')
done(null, body)
})
@@ -535,7 +582,7 @@ test('content-type fail when parameters not match - string 1', async t => {
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
t.assert.fail('shouldn\'t be called')
done(null, body)
})
@@ -552,7 +599,7 @@ test('content-type fail when parameters not match - string 1', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
})
test('content-type fail when parameters not match - string 2', async t => {
@@ -561,7 +608,7 @@ test('content-type fail when parameters not match - string 2', async t => {
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
t.assert.fail('shouldn\'t be called')
done(null, body)
})
@@ -578,7 +625,7 @@ test('content-type fail when parameters not match - string 2', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
})
test('content-type fail when parameters not match - regexp', async t => {
@@ -587,7 +634,7 @@ test('content-type fail when parameters not match - regexp', async t => {
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json; charset=utf8; foo=bar/, function (request, body, done) {
t.fail('shouldn\'t be called')
t.assert.fail('shouldn\'t be called')
done(null, body)
})
@@ -604,7 +651,7 @@ test('content-type fail when parameters not match - regexp', async t => {
body: ''
})
t.same(response.statusCode, 415)
t.assert.strictEqual(response.statusCode, 415)
})
// Refs: https://github.com/fastify/fastify/issues/4495
@@ -632,9 +679,9 @@ test('content-type regexp list should be cloned when plugin override', async t =
payload: 'jpeg',
headers: { 'content-type': 'image/jpeg' }
})
t.same(statusCode, 200)
t.same(headers['content-type'], 'image/jpeg')
t.same(payload, 'jpeg')
t.assert.strictEqual(statusCode, 200)
t.assert.strictEqual(headers['content-type'], 'image/jpeg')
t.assert.strictEqual(payload, 'jpeg')
}
{
@@ -644,85 +691,19 @@ test('content-type regexp list should be cloned when plugin override', async t =
payload: 'png',
headers: { 'content-type': 'image/png' }
})
t.same(statusCode, 200)
t.same(headers['content-type'], 'image/png')
t.same(payload, 'png')
t.assert.strictEqual(statusCode, 200)
t.assert.strictEqual(headers['content-type'], 'image/png')
t.assert.strictEqual(payload, 'png')
}
})
test('allow partial content-type - essence check', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('json', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; foo=bar; charset=utf8'
},
body: ''
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'image/jpeg'
},
body: ''
})
})
test('allow partial content-type - not essence check', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('json;', function (request, body, done) {
t.pass('should be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; foo=bar; charset=utf8'
},
body: ''
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'image/jpeg'
},
body: ''
})
})
test('edge case content-type - ;', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(';', function (request, body, done) {
t.fail('should not be called')
t.assert.fail('should not be called')
done(null, body)
})
@@ -748,5 +729,5 @@ test('edge case content-type - ;', async t => {
body: ''
})
t.pass('end')
t.assert.ok('end')
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
test('should remove content-type for setErrorHandler', async t => {
@@ -10,22 +9,22 @@ test('should remove content-type for setErrorHandler', async t => {
const fastify = Fastify()
fastify.setErrorHandler(function (error, request, reply) {
t.same(error.message, 'kaboom')
t.same(reply.hasHeader('content-type'), false)
t.assert.strictEqual(error.message, 'kaboom')
t.assert.strictEqual(reply.hasHeader('content-type'), false)
reply.code(400).send({ foo: 'bar' })
})
fastify.addHook('onSend', async function (request, reply, payload) {
count++
t.same(typeof payload, 'string')
t.assert.strictEqual(typeof payload, 'string')
switch (count) {
case 1: {
// should guess the correct content-type based on payload
t.same(reply.getHeader('content-type'), 'text/plain; charset=utf-8')
t.assert.strictEqual(reply.getHeader('content-type'), 'text/plain; charset=utf-8')
throw Error('kaboom')
}
case 2: {
// should guess the correct content-type based on payload
t.same(reply.getHeader('content-type'), 'application/json; charset=utf-8')
t.assert.strictEqual(reply.getHeader('content-type'), 'application/json; charset=utf-8')
return payload
}
default: {
@@ -38,6 +37,6 @@ test('should remove content-type for setErrorHandler', async t => {
})
const { statusCode, body } = await fastify.inject({ method: 'GET', path: '/' })
t.same(statusCode, 400)
t.same(body, JSON.stringify({ foo: 'bar' }))
t.assert.strictEqual(statusCode, 400)
t.assert.strictEqual(body, JSON.stringify({ foo: 'bar' }))
})

View File

@@ -1,7 +1,7 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const { kRouteContext } = require('../lib/symbols')
const Fastify = require('..')
@@ -17,8 +17,8 @@ function handler (req, reply) {
reply.send(reply[kRouteContext].config)
}
test('config', t => {
t.plan(9)
test('config', async t => {
t.plan(6)
const fastify = Fastify()
fastify.get('/get', {
@@ -41,36 +41,33 @@ test('config', t => {
handler
})
fastify.inject({
method: 'GET',
url: '/get'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/get', method: 'GET' }, schema.config))
})
fastify.inject({
let response = await fastify.inject({
method: 'GET',
url: '/route'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/route', method: 'GET' }, schema.config))
})
fastify.inject({
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/route', method: 'GET' }, schema.config))
response = await fastify.inject({
method: 'GET',
url: '/route'
})
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/route', method: 'GET' }, schema.config))
response = await fastify.inject({
method: 'GET',
url: '/no-config'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), { url: '/no-config', method: 'GET' })
})
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), { url: '/no-config', method: 'GET' })
})
test('config with exposeHeadRoutes', t => {
t.plan(9)
test('config with exposeHeadRoutes', async t => {
t.plan(6)
const fastify = Fastify({ exposeHeadRoutes: true })
fastify.get('/get', {
@@ -93,36 +90,33 @@ test('config with exposeHeadRoutes', t => {
handler
})
fastify.inject({
let response = await fastify.inject({
method: 'GET',
url: '/get'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/get', method: 'GET' }, schema.config))
})
fastify.inject({
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/get', method: 'GET' }, schema.config))
response = await fastify.inject({
method: 'GET',
url: '/route'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/route', method: 'GET' }, schema.config))
})
fastify.inject({
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/route', method: 'GET' }, schema.config))
response = await fastify.inject({
method: 'GET',
url: '/no-config'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), { url: '/no-config', method: 'GET' })
})
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), { url: '/no-config', method: 'GET' })
})
test('config without exposeHeadRoutes', t => {
t.plan(9)
test('config without exposeHeadRoutes', async t => {
t.plan(6)
const fastify = Fastify({ exposeHeadRoutes: false })
fastify.get('/get', {
@@ -145,30 +139,26 @@ test('config without exposeHeadRoutes', t => {
handler
})
fastify.inject({
let response = await fastify.inject({
method: 'GET',
url: '/get'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/get', method: 'GET' }, schema.config))
})
fastify.inject({
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/get', method: 'GET' }, schema.config))
response = await fastify.inject({
method: 'GET',
url: '/route'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), Object.assign({ url: '/route', method: 'GET' }, schema.config))
})
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), Object.assign({ url: '/route', method: 'GET' }, schema.config))
fastify.inject({
response = await fastify.inject({
method: 'GET',
url: '/no-config'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(response.payload), { url: '/no-config', method: 'GET' })
})
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.json(), { url: '/no-config', method: 'GET' })
})

View File

@@ -1,48 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
test('can be created - copy', t => {
t.plan(1)
try {
fastify.route({
method: 'COPY',
url: '*',
handler: function (req, reply) {
reply.code(204)
.header('location', req.headers.destination)
.header('body', req.body.toString())
.send()
}
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('request - copy', t => {
t.plan(4)
sget({
url: `http://localhost:${fastify.server.address().port}/test.txt`,
method: 'COPY',
headers: {
destination: '/test2.txt',
'Content-Type': 'text/plain'
},
body: '/test3.txt'
}, (err, response) => {
t.error(err)
t.equal(response.headers.location, '/test2.txt')
t.equal(response.headers.body, '/test3.txt')
t.equal(response.statusCode, 204)
})
})
})

View File

@@ -1,10 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const http = require('node:http')
const dns = require('node:dns').promises
const sget = require('simple-get').concat
const Fastify = require('..')
const { FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE } = require('../lib/errors')
@@ -12,11 +10,11 @@ async function setup () {
const localAddresses = await dns.lookup('localhost', { all: true })
test('Should support a custom http server', { skip: localAddresses.length < 1 }, async t => {
t.plan(4)
t.plan(5)
const fastify = Fastify({
serverFactory: (handler, opts) => {
t.ok(opts.serverFactory, 'it is called once for localhost')
t.assert.ok(opts.serverFactory, 'it is called once for localhost')
const server = http.createServer((req, res) => {
req.custom = true
@@ -27,35 +25,27 @@ async function setup () {
}
})
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.get('/', (req, reply) => {
t.ok(req.raw.custom)
t.assert.ok(req.raw.custom)
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 0 })
await new Promise((resolve, reject) => {
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port,
rejectUnauthorized: false
}, (err, response, body) => {
if (err) {
return reject(err)
}
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), { hello: 'world' })
resolve()
})
const response = await fetch('http://localhost:' + fastify.server.address().port, {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
test('Should not allow forceCloseConnection=idle if the server does not support closeIdleConnections', t => {
t.plan(1)
t.throws(
t.assert.throws(
() => {
Fastify({
forceCloseConnections: 'idle',
@@ -75,13 +65,11 @@ async function setup () {
test('Should accept user defined serverFactory and ignore secondary server creation', async t => {
const server = http.createServer(() => { })
t.teardown(() => new Promise(resolve => server.close(resolve)))
const app = await Fastify({
t.after(() => new Promise(resolve => server.close(resolve)))
const app = Fastify({
serverFactory: () => server
})
t.resolves(async () => {
await app.listen({ port: 0 })
})
await t.assert.doesNotReject(async () => { await app.listen({ port: 0 }) })
})
test('Should not call close on the server if it has not created it', async t => {
@@ -107,12 +95,12 @@ async function setup () {
})
const address = server.address()
t.equal(server.listening, true)
t.assert.strictEqual(server.listening, true)
await fastify.close()
t.equal(server.listening, true)
t.same(server.address(), address)
t.same(fastify.addresses(), [address])
t.assert.strictEqual(server.listening, true)
t.assert.deepStrictEqual(server.address(), address)
t.assert.deepStrictEqual(fastify.addresses(), [address])
await new Promise((resolve, reject) => {
server.close((err) => {
@@ -122,8 +110,8 @@ async function setup () {
resolve()
})
})
t.equal(server.listening, false)
t.same(server.address(), null)
t.assert.strictEqual(server.listening, false)
t.assert.deepStrictEqual(server.address(), null)
})
}

View File

@@ -1,14 +1,12 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('../fastify')
process.removeAllListeners('warning')
test('contentTypeParser should add a custom async parser', t => {
t.plan(3)
test('contentTypeParser should add a custom async parser', async t => {
t.plan(2)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -24,43 +22,38 @@ test('contentTypeParser should add a custom async parser', t => {
return res
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.after(() => fastify.close())
const fastifyServer = await fastify.listen({ port: 0 })
t.teardown(() => fastify.close())
await t.test('in POST', async t => {
t.plan(3)
t.test('in POST', t => {
t.plan(3)
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port,
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const result = await fetch(fastifyServer, {
method: 'POST',
headers: {
'Content-Type': 'application/jsoff'
},
body: '{"hello":"world"}'
})
t.test('in OPTIONS', t => {
t.plan(3)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
sget({
method: 'OPTIONS',
url: 'http://localhost:' + fastify.server.address().port,
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
await t.test('in OPTIONS', async t => {
t.plan(3)
const result = await fetch(fastifyServer, {
method: 'OPTIONS',
headers: {
'Content-Type': 'application/jsoff'
},
body: '{"hello":"world"}'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
})

View File

@@ -1,23 +1,21 @@
'use strict'
const fs = require('node:fs')
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl, plainTextParser } = require('./helper')
const { plainTextParser } = require('./helper')
process.removeAllListeners('warning')
test('contentTypeParser method should exist', t => {
t.plan(1)
const fastify = Fastify()
t.ok(fastify.addContentTypeParser)
t.assert.ok(fastify.addContentTypeParser)
})
test('contentTypeParser should add a custom parser', t => {
t.plan(3)
test('contentTypeParser should add a custom parser', async (t) => {
t.plan(2)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -34,49 +32,44 @@ test('contentTypeParser should add a custom parser', t => {
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
t.teardown(() => fastify.close())
await t.test('in POST', async (t) => {
t.plan(3)
t.test('in POST', t => {
t.plan(3)
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.test('in OPTIONS', t => {
t.plan(3)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
sget({
method: 'OPTIONS',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
await t.test('in OPTIONS', async (t) => {
t.plan(2)
const result = await fetch(fastifyServer, {
method: 'OPTIONS',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, JSON.stringify({ hello: 'world' }))
})
})
test('contentTypeParser should handle multiple custom parsers', t => {
t.plan(7)
test('contentTypeParser should handle multiple custom parsers', async (t) => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -96,40 +89,37 @@ test('contentTypeParser should handle multiple custom parsers', t => {
fastify.addContentTypeParser('application/jsoff', customParser)
fastify.addContentTypeParser('application/ffosj', customParser)
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
sget({
method: 'POST',
url: getServerUrl(fastify) + '/hello',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/ffosj'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.deepStrictEqual(await result1.json(), { hello: 'world' })
const result2 = await fetch(fastifyServer + '/hello', {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/ffosj'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.deepStrictEqual(await result2.json(), { hello: 'world' })
})
test('contentTypeParser should handle an array of custom contentTypes', t => {
t.plan(7)
test('contentTypeParser should handle an array of custom contentTypes', async (t) => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -148,40 +138,36 @@ test('contentTypeParser should handle an array of custom contentTypes', t => {
fastify.addContentTypeParser(['application/jsoff', 'application/ffosj'], customParser)
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
sget({
method: 'POST',
url: getServerUrl(fastify) + '/hello',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/ffosj'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.deepStrictEqual(await result1.json(), { hello: 'world' })
const result2 = await fetch(fastifyServer + '/hello', {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/ffosj'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.deepStrictEqual(await result2.json(), { hello: 'world' })
})
test('contentTypeParser should handle errors', t => {
t.plan(3)
test('contentTypeParser should handle errors', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -192,36 +178,32 @@ test('contentTypeParser should handle errors', t => {
done(new Error('kaboom!'), {})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 500)
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.strictEqual(result.status, 500)
})
test('contentTypeParser should support encapsulation', t => {
test('contentTypeParser should support encapsulation', (t, testDone) => {
t.plan(6)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
instance.addContentTypeParser('application/jsoff', () => {})
t.ok(instance.hasContentTypeParser('application/jsoff'))
t.assert.ok(instance.hasContentTypeParser('application/jsoff'))
instance.register((instance, opts, done) => {
instance.addContentTypeParser('application/ffosj', () => {})
t.ok(instance.hasContentTypeParser('application/jsoff'))
t.ok(instance.hasContentTypeParser('application/ffosj'))
t.assert.ok(instance.hasContentTypeParser('application/jsoff'))
t.assert.ok(instance.hasContentTypeParser('application/ffosj'))
done()
})
@@ -229,14 +211,15 @@ test('contentTypeParser should support encapsulation', t => {
})
fastify.ready(err => {
t.error(err)
t.notOk(fastify.hasContentTypeParser('application/jsoff'))
t.notOk(fastify.hasContentTypeParser('application/ffosj'))
t.assert.ifError(err)
t.assert.ok(!fastify.hasContentTypeParser('application/jsoff'))
t.assert.ok(!fastify.hasContentTypeParser('application/ffosj'))
testDone()
})
})
test('contentTypeParser should support encapsulation, second try', t => {
t.plan(4)
test('contentTypeParser should support encapsulation, second try', async (t) => {
t.plan(2)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
@@ -253,27 +236,24 @@ test('contentTypeParser should support encapsulation, second try', t => {
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, JSON.stringify({ hello: 'world' }))
})
test('contentTypeParser shouldn\'t support request with undefined "Content-Type"', t => {
t.plan(3)
test('contentTypeParser shouldn\'t support request with undefined "Content-Type"', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -286,22 +266,18 @@ test('contentTypeParser shouldn\'t support request with undefined "Content-Type"
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'unknown content type!',
headers: {
// 'Content-Type': undefined
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'unknown content type!',
headers: {
'Content-Type': undefined
}
})
t.assert.strictEqual(result.status, 415)
})
test('the content type should be a string or RegExp', t => {
@@ -310,10 +286,10 @@ test('the content type should be a string or RegExp', t => {
try {
fastify.addContentTypeParser(null, () => {})
t.fail()
t.assert.fail()
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_INVALID_TYPE')
t.equal(err.message, 'The content type should be a string or a RegExp')
t.assert.strictEqual(err.code, 'FST_ERR_CTP_INVALID_TYPE')
t.assert.strictEqual(err.message, 'The content type should be a string or a RegExp')
}
})
@@ -323,10 +299,10 @@ test('the content type cannot be an empty string', t => {
try {
fastify.addContentTypeParser('', () => {})
t.fail()
t.assert.fail()
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_EMPTY_TYPE')
t.equal(err.message, 'The content type cannot be an empty string')
t.assert.strictEqual(err.code, 'FST_ERR_CTP_EMPTY_TYPE')
t.assert.strictEqual(err.message, 'The content type cannot be an empty string')
}
})
@@ -336,15 +312,15 @@ test('the content type handler should be a function', t => {
try {
fastify.addContentTypeParser('aaa', null)
t.fail()
t.assert.fail()
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_INVALID_HANDLER')
t.equal(err.message, 'The content type handler should be a function')
t.assert.strictEqual(err.code, 'FST_ERR_CTP_INVALID_HANDLER')
t.assert.strictEqual(err.message, 'The content type handler should be a function')
}
})
test('catch all content type parser', t => {
t.plan(7)
test('catch all content type parser', async (t) => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -359,40 +335,36 @@ test('catch all content type parser', t => {
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'hello')
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello',
headers: {
'Content-Type': 'very-weird-content-type'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'hello')
fastify.close()
})
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: 'hello',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.strictEqual(await result1.text(), 'hello')
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: 'hello',
headers: {
'Content-Type': 'very-weird-content-type'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.strictEqual(await result2.text(), 'hello')
})
test('catch all content type parser should not interfere with other conte type parsers', t => {
t.plan(7)
test('catch all content type parser should not interfere with other conte type parsers', async (t) => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -413,45 +385,41 @@ test('catch all content type parser should not interfere with other conte type p
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello',
headers: {
'Content-Type': 'very-weird-content-type'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'hello')
fastify.close()
})
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.deepStrictEqual(await result1.json(), { hello: 'world' })
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: 'hello',
headers: {
'Content-Type': 'very-weird-content-type'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.strictEqual(await result2.text(), 'hello')
})
// Issue 492 https://github.com/fastify/fastify/issues/492
test('\'*\' catch undefined Content-Type requests', t => {
t.plan(4)
test('\'*\' catch undefined Content-Type requests', async (t) => {
t.plan(3)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.addContentTypeParser('*', function (req, payload, done) {
let data = ''
@@ -466,48 +434,47 @@ test('\'*\' catch undefined Content-Type requests', t => {
res.type('text/plain').send(req.body)
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
const fileStream = fs.createReadStream(__filename)
const fileStream = fs.createReadStream(__filename)
sget({
method: 'POST',
url: getServerUrl(fastify) + '/',
body: fileStream
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body + '', fs.readFileSync(__filename).toString())
})
const result = await fetch(fastifyServer + '/', {
method: 'POST',
body: fileStream,
duplex: 'half'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), fs.readFileSync(__filename).toString())
})
test('cannot add custom parser after binding', t => {
test('cannot add custom parser after binding', (t, testDone) => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.post('/', (req, res) => {
res.type('text/plain').send(req.body)
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.assert.ifError(err)
try {
fastify.addContentTypeParser('*', () => {})
t.fail()
t.assert.fail()
} catch (e) {
t.pass()
t.assert.ok(true)
testDone()
}
})
})
test('Can override the default json parser', t => {
t.plan(5)
test('Can override the default json parser', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -515,33 +482,30 @@ test('Can override the default json parser', t => {
})
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '{"hello":"world"}')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, '{"hello":"world"}')
})
test('Can override the default plain text parser', t => {
t.plan(5)
test('Can override the default plain text parser', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -549,38 +513,35 @@ test('Can override the default plain text parser', t => {
})
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), 'hello world')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, 'hello world')
})
test('Can override the default json parser in a plugin', t => {
t.plan(5)
test('Can override the default json parser in a plugin', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
instance.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
@@ -593,23 +554,20 @@ test('Can override the default json parser in a plugin', t => {
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '{"hello":"world"}')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, '{"hello":"world"}')
})
test('Can\'t override the json parser multiple times', t => {
@@ -624,14 +582,14 @@ test('Can\'t override the json parser multiple times', t => {
try {
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')
t.equal(err.message, 'Content type parser \'application/json\' already present.')
t.assert.strictEqual(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')
t.assert.strictEqual(err.message, 'Content type parser \'application/json\' already present.')
}
})
@@ -647,19 +605,19 @@ test('Can\'t override the plain text parser multiple times', t => {
try {
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')
t.equal(err.message, 'Content type parser \'text/plain\' already present.')
t.assert.strictEqual(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')
t.assert.strictEqual(err.message, 'Content type parser \'text/plain\' already present.')
}
})
test('Should get the body as string', t => {
t.plan(6)
test('Should get the body as string', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -667,8 +625,8 @@ test('Should get the body as string', t => {
})
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
t.ok('called')
t.ok(typeof body === 'string')
t.assert.ok('called')
t.assert.ok(typeof body === 'string')
try {
const json = JSON.parse(body)
done(null, json)
@@ -678,74 +636,66 @@ test('Should get the body as string', t => {
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '{"hello":"world"}')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, '{"hello":"world"}')
})
test('Should return defined body with no custom parser defined and content type = \'text/plain\'', t => {
t.plan(4)
test('Should return defined body with no custom parser defined and content type = \'text/plain\'', async (t) => {
t.plan(2)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), 'hello world')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(body, 'hello world')
})
test('Should have typeof body object with no custom parser defined, no body defined and content type = \'text/plain\'', t => {
t.plan(4)
test('Should have typeof body object with no custom parser defined, no body defined and content type = \'text/plain\'', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(typeof body, 'object')
fastify.close()
})
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(fastifyServer, {
method: 'POST',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), '')
})

View File

@@ -1,70 +1,61 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl } = require('./helper')
process.removeAllListeners('warning')
test('Should have typeof body object with no custom parser defined, null body and content type = \'text/plain\'', t => {
t.plan(4)
test('Should have typeof body object with no custom parser defined, null body and content type = \'text/plain\'', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: null,
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(typeof body, 'object')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: null,
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), '')
})
test('Should have typeof body object with no custom parser defined, undefined body and content type = \'text/plain\'', t => {
t.plan(4)
test('Should have typeof body object with no custom parser defined, undefined body and content type = \'text/plain\'', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: undefined,
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(typeof body, 'object')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: undefined,
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), '')
})
test('Should get the body as string', t => {
t.plan(6)
test('Should get the body as string /1', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -72,8 +63,8 @@ test('Should get the body as string', t => {
})
fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, function (req, body, done) {
t.ok('called')
t.ok(typeof body === 'string')
t.assert.ok('called')
t.assert.ok(typeof body === 'string')
try {
const plainText = body
done(null, plainText)
@@ -83,27 +74,58 @@ test('Should get the body as string', t => {
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), 'hello world')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), 'hello world')
})
test('Should get the body as buffer', t => {
t.plan(6)
test('Should get the body as string /2', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('text/plain/test', { parseAs: 'string' }, function (req, body, done) {
t.assert.ok('called')
t.assert.ok(typeof body === 'string')
try {
const plainText = body
done(null, plainText)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': ' text/plain/test '
}
})
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), 'hello world')
})
test('Should get the body as buffer', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -111,8 +133,8 @@ test('Should get the body as buffer', t => {
})
fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, function (req, body, done) {
t.ok('called')
t.ok(body instanceof Buffer)
t.assert.ok('called')
t.assert.ok(body instanceof Buffer)
try {
const json = JSON.parse(body)
done(null, json)
@@ -122,27 +144,23 @@ test('Should get the body as buffer', t => {
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '{"hello":"world"}')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), '{"hello":"world"}')
})
test('Should get the body as buffer', t => {
t.plan(6)
test('Should get the body as buffer', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -150,8 +168,8 @@ test('Should get the body as buffer', t => {
})
fastify.addContentTypeParser('text/plain', { parseAs: 'buffer' }, function (req, body, done) {
t.ok('called')
t.ok(body instanceof Buffer)
t.assert.ok('called')
t.assert.ok(body instanceof Buffer)
try {
const plainText = body
done(null, plainText)
@@ -161,31 +179,27 @@ test('Should get the body as buffer', t => {
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), 'hello world')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), 'hello world')
})
test('Should parse empty bodies as a string', t => {
t.plan(9)
test('Should parse empty bodies as a string', async (t) => {
t.plan(8)
const fastify = Fastify()
fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, (req, body, done) => {
t.equal(body, '')
t.assert.strictEqual(body, '')
done(null, body)
})
@@ -197,41 +211,37 @@ test('Should parse empty bodies as a string', t => {
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '')
})
sget({
method: 'DELETE',
url: getServerUrl(fastify),
body: '',
headers: {
'Content-Type': 'text/plain',
'Content-Length': '0'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '')
})
const postResult = await fetch(fastifyServer, {
method: 'POST',
body: '',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.ok(postResult.ok)
t.assert.strictEqual(postResult.status, 200)
t.assert.strictEqual(await postResult.text(), '')
const deleteResult = await fetch(fastifyServer, {
method: 'DELETE',
body: '',
headers: {
'Content-Type': 'text/plain',
'Content-Length': '0'
}
})
t.assert.ok(deleteResult.ok)
t.assert.strictEqual(deleteResult.status, 200)
t.assert.strictEqual(await deleteResult.text(), '')
})
test('Should parse empty bodies as a buffer', t => {
t.plan(6)
test('Should parse empty bodies as a buffer', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -239,32 +249,28 @@ test('Should parse empty bodies as a buffer', t => {
})
fastify.addContentTypeParser('text/plain', { parseAs: 'buffer' }, function (req, body, done) {
t.ok(body instanceof Buffer)
t.equal(body.length, 0)
t.assert.ok(body instanceof Buffer)
t.assert.strictEqual(body.length, 0)
done(null, body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.length, 0)
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual((await result.arrayBuffer()).byteLength, 0)
})
test('The charset should not interfere with the content type handling', t => {
t.plan(5)
test('The charset should not interfere with the content type handling', async (t) => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
@@ -272,27 +278,24 @@ test('The charset should not interfere with the content type handling', t => {
})
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '{"hello":"world"}')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), '{"hello":"world"}')
})

View File

@@ -1,10 +1,7 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
const { getServerUrl } = require('./helper')
const { test } = require('node:test')
const Fastify = require('..')
process.removeAllListeners('warning')
@@ -14,17 +11,17 @@ test('Wrong parseAs parameter', t => {
try {
fastify.addContentTypeParser('application/json', { parseAs: 'fireworks' }, () => {})
t.fail('should throw')
t.assert.fail('should throw')
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_INVALID_PARSE_TYPE')
t.equal(err.message, "The body parser can only parse your data as 'string' or 'buffer', you asked 'fireworks' which is not supported.")
t.assert.strictEqual(err.code, 'FST_ERR_CTP_INVALID_PARSE_TYPE')
t.assert.strictEqual(err.message, "The body parser can only parse your data as 'string' or 'buffer', you asked 'fireworks' which is not supported.")
}
})
test('Should allow defining the bodyLimit per parser', t => {
t.plan(3)
test('Should allow defining the bodyLimit per parser', async (t) => {
t.plan(2)
const fastify = Fastify()
t.teardown(() => fastify.close())
t.after(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
@@ -34,38 +31,34 @@ test('Should allow defining the bodyLimit per parser', t => {
'x/foo',
{ parseAs: 'string', bodyLimit: 5 },
function (req, body, done) {
t.fail('should not be invoked')
t.assert.fail('should not be invoked')
done()
}
)
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '1234567890',
headers: {
'Content-Type': 'x/foo'
}
}, (err, response, body) => {
t.error(err)
t.strictSame(JSON.parse(body.toString()), {
statusCode: 413,
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
error: 'Payload Too Large',
message: 'Request body is too large'
})
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '1234567890',
headers: {
'Content-Type': 'x/foo'
}
})
t.assert.ok(!result.ok)
t.assert.deepStrictEqual(await result.json(), {
statusCode: 413,
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
error: 'Payload Too Large',
message: 'Request body is too large'
})
})
test('route bodyLimit should take precedence over a custom parser bodyLimit', t => {
t.plan(3)
test('route bodyLimit should take precedence over a custom parser bodyLimit', async (t) => {
t.plan(2)
const fastify = Fastify()
t.teardown(() => fastify.close())
t.after(() => fastify.close())
fastify.post('/', { bodyLimit: 5 }, (request, reply) => {
reply.send(request.body)
@@ -75,28 +68,24 @@ test('route bodyLimit should take precedence over a custom parser bodyLimit', t
'x/foo',
{ parseAs: 'string', bodyLimit: 100 },
function (req, body, done) {
t.fail('should not be invoked')
t.assert.fail('should not be invoked')
done()
}
)
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '1234567890',
headers: { 'Content-Type': 'x/foo' }
}, (err, response, body) => {
t.error(err)
t.strictSame(JSON.parse(body.toString()), {
statusCode: 413,
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
error: 'Payload Too Large',
message: 'Request body is too large'
})
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: '1234567890',
headers: { 'Content-Type': 'x/foo' }
})
t.assert.ok(!result.ok)
t.assert.deepStrictEqual(await result.json(), {
statusCode: 413,
code: 'FST_ERR_CTP_BODY_TOO_LARGE',
error: 'Payload Too Large',
message: 'Request body is too large'
})
})

View File

@@ -1,18 +1,15 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
const { test } = require('node:test')
const Fastify = require('..')
const jsonParser = require('fast-json-body')
const { getServerUrl } = require('./helper')
process.removeAllListeners('warning')
test('should be able to use default parser for extra content type', t => {
t.plan(4)
test('should be able to use default parser for extra content type', async t => {
t.plan(3)
const fastify = Fastify()
t.teardown(() => fastify.close())
t.after(() => fastify.close())
fastify.post('/', (request, reply) => {
reply.send(request.body)
@@ -20,29 +17,23 @@ test('should be able to use default parser for extra content type', t => {
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'text/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.strictSame(JSON.parse(body.toString()), { hello: 'world' })
fastify.close()
})
const response = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'text/json'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
t.assert.deepStrictEqual(await response.json(), { hello: 'world' })
})
test('contentTypeParser should add a custom parser with RegExp value', t => {
t.plan(3)
test('contentTypeParser should add a custom parser with RegExp value', async (t) => {
const fastify = Fastify()
t.after(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
@@ -58,51 +49,45 @@ test('contentTypeParser should add a custom parser with RegExp value', t => {
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
t.teardown(() => fastify.close())
await t.test('in POST', async t => {
t.plan(3)
t.test('in POST', t => {
t.plan(3)
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/vnd.test+json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const response = await fetch(fastifyServer, {
method: 'POST',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/vnd.test+json'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
})
t.test('in OPTIONS', t => {
t.plan(3)
await t.test('in OPTIONS', async t => {
t.plan(3)
sget({
method: 'OPTIONS',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'weird/content-type+json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
const response = await fetch(fastifyServer, {
method: 'OPTIONS',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'weird/content-type+json'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
})
})
test('contentTypeParser should add multiple custom parsers with RegExp values', async t => {
t.plan(6)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
@@ -137,8 +122,8 @@ test('contentTypeParser should add multiple custom parsers with RegExp values',
'Content-Type': 'application/vnd.hello+json'
}
})
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '{"hello":"world"}')
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.payload.toString(), '{"hello":"world"}')
}
{
@@ -150,8 +135,8 @@ test('contentTypeParser should add multiple custom parsers with RegExp values',
'Content-Type': 'application/test+xml'
}
})
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), 'xml')
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.payload.toString(), 'xml')
}
await fastify.inject({
@@ -162,17 +147,17 @@ test('contentTypeParser should add multiple custom parsers with RegExp values',
'Content-Type': 'application/+myExtension'
}
}).then((response) => {
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), 'abcdefgmyExtension')
t.assert.strictEqual(response.statusCode, 200)
t.assert.deepStrictEqual(response.payload.toString(), 'abcdefgmyExtension')
}).catch((err) => {
t.error(err)
t.assert.ifError(err)
})
})
test('catch all content type parser should not interfere with content type parser', t => {
t.plan(10)
test('catch all content type parser should not interfere with content type parser', async t => {
t.plan(9)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
@@ -200,46 +185,24 @@ test('catch all content type parser should not interfere with content type parse
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"myKey":"myValue"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ myKey: 'myValue' }))
})
const assertions = [
{ body: '{"myKey":"myValue"}', contentType: 'application/json', expected: JSON.stringify({ myKey: 'myValue' }) },
{ body: 'body', contentType: 'very-weird-content-type', expected: 'body' },
{ body: 'my text', contentType: 'text/html', expected: 'my texthtml' }
]
sget({
for (const { body, contentType, expected } of assertions) {
const response = await fetch(fastifyServer, {
method: 'POST',
url: getServerUrl(fastify),
body: 'body',
body,
headers: {
'Content-Type': 'very-weird-content-type'
'Content-Type': contentType
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'body')
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'my text',
headers: {
'Content-Type': 'text/html'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'my texthtml')
})
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
t.assert.deepStrictEqual(await response.text(), expected)
}
})

View File

@@ -1,19 +1,15 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl } = require('./helper')
process.removeAllListeners('warning')
test('should prefer string content types over RegExp ones', t => {
t.plan(7)
test('should prefer string content types over RegExp ones', async (t) => {
t.plan(6)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => { fastify.close() })
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
@@ -32,42 +28,38 @@ test('should prefer string content types over RegExp ones', t => {
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"k1":"myValue", "k2": "myValue"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'javascript',
headers: {
'Content-Type': 'application/javascript'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'javascript')
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: '{"k1":"myValue", "k2": "myValue"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.equal(await result1.text(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: 'javascript',
headers: {
'Content-Type': 'application/javascript'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.equal(await result2.text(), 'javascript')
})
test('removeContentTypeParser should support arrays of content types to remove', t => {
t.plan(8)
test('removeContentTypeParser should support arrays of content types to remove', async (t) => {
t.plan(7)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
payload.on('data', () => {})
@@ -89,52 +81,48 @@ test('removeContentTypeParser should support arrays of content types to remove',
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'xml')
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '',
headers: {
'Content-Type': 'image/png'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{test: "test"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
const result1 = await fetch(fastifyServer, {
method: 'POST',
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
})
t.assert.ok(result1.ok)
t.assert.strictEqual(result1.status, 200)
t.assert.equal(await result1.text(), 'xml')
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: '',
headers: {
'Content-Type': 'image/png'
}
})
t.assert.ok(!result2.ok)
t.assert.strictEqual(result2.status, 415)
const result3 = await fetch(fastifyServer, {
method: 'POST',
body: '{test: "test"}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(!result3.ok)
t.assert.strictEqual(result3.status, 415)
})
test('removeContentTypeParser should support encapsulation', t => {
t.plan(6)
test('removeContentTypeParser should support encapsulation', async (t) => {
t.plan(5)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
payload.on('data', () => {})
@@ -157,41 +145,37 @@ test('removeContentTypeParser should support encapsulation', t => {
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify) + '/encapsulated',
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'xml')
fastify.close()
})
const result1 = await fetch(fastifyServer + '/encapsulated', {
method: 'POST',
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
})
t.assert.ok(!result1.ok)
t.assert.strictEqual(result1.status, 415)
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: '<?xml version="1.0">',
headers: {
'Content-Type': 'application/xml'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.equal(await result2.text(), 'xml')
})
test('removeAllContentTypeParsers should support encapsulation', t => {
t.plan(6)
test('removeAllContentTypeParsers should support encapsulation', async (t) => {
t.plan(5)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
@@ -207,33 +191,28 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify) + '/encapsulated',
body: '{}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"test":1}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body.toString()).test, 1)
fastify.close()
})
const result1 = await fetch(fastifyServer + '/encapsulated', {
method: 'POST',
body: '{}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(!result1.ok)
t.assert.strictEqual(result1.status, 415)
const result2 = await fetch(fastifyServer, {
method: 'POST',
body: '{"test":1}',
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result2.ok)
t.assert.strictEqual(result2.status, 200)
t.assert.equal(JSON.parse(await result2.text()).test, 1)
})

View File

@@ -1,44 +1,35 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl, plainTextParser } = require('./helper')
const { plainTextParser } = require('./helper')
process.removeAllListeners('warning')
test('cannot remove all content type parsers after binding', t => {
t.plan(2)
test('cannot remove all content type parsers after binding', async (t) => {
t.plan(1)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
t.after(() => fastify.close())
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.throws(() => fastify.removeAllContentTypeParsers())
})
await fastify.listen({ port: 0 })
t.assert.throws(() => fastify.removeAllContentTypeParsers())
})
test('cannot remove content type parsers after binding', t => {
t.plan(2)
test('cannot remove content type parsers after binding', async (t) => {
t.plan(1)
const fastify = Fastify()
t.after(() => fastify.close())
t.teardown(fastify.close.bind(fastify))
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.throws(() => fastify.removeContentTypeParser('application/json'))
})
await fastify.listen({ port: 0 })
t.assert.throws(() => fastify.removeContentTypeParser('application/json'))
})
test('should be able to override the default json parser after removeAllContentTypeParsers', t => {
t.plan(5)
test('should be able to override the default json parser after removeAllContentTypeParsers', async (t) => {
t.plan(4)
const fastify = Fastify()
@@ -49,33 +40,30 @@ test('should be able to override the default json parser after removeAllContentT
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
await fastify.close()
})
test('should be able to override the default plain text parser after removeAllContentTypeParsers', t => {
t.plan(5)
test('should be able to override the default plain text parser after removeAllContentTypeParsers', async (t) => {
t.plan(4)
const fastify = Fastify()
@@ -86,33 +74,30 @@ test('should be able to override the default plain text parser after removeAllCo
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), 'hello world')
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: 'hello world',
headers: {
'Content-Type': 'text/plain'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(await result.text(), 'hello world')
await fastify.close()
})
test('should be able to add a custom content type parser after removeAllContentTypeParsers', t => {
t.plan(5)
test('should be able to add a custom content type parser after removeAllContentTypeParsers', async (t) => {
t.plan(4)
const fastify = Fastify()
@@ -121,29 +106,25 @@ test('should be able to add a custom content type parser after removeAllContentT
})
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
t.ok('called')
t.assert.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'POST',
url: getServerUrl(fastify),
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/jsoff'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
fastify.close()
})
const result = await fetch(fastifyServer, {
method: 'POST',
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/jsoff'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
await fastify.close()
})

View File

@@ -1,123 +1,97 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const querystring = require('node:querystring')
const sget = require('simple-get').concat
const Fastify = require('..')
test('Custom querystring parser', t => {
t.plan(9)
test('Custom querystring parser', async t => {
t.plan(7)
const fastify = Fastify({
querystringParser: function (str) {
t.equal(str, 'foo=bar&baz=faz')
t.assert.strictEqual(str, 'foo=bar&baz=faz')
return querystring.parse(str)
}
})
fastify.get('/', (req, reply) => {
t.same(req.query, {
t.assert.deepEqual(req.query, {
foo: 'bar',
baz: 'faz'
})
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.teardown(() => fastify.close())
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'GET',
url: `${address}?foo=bar&baz=faz`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const result = await fetch(`${fastifyServer}?foo=bar&baz=faz`)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
fastify.inject({
method: 'GET',
url: `${address}?foo=bar&baz=faz`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const injectResponse = await fastify.inject({
method: 'GET',
url: `${fastifyServer}?foo=bar&baz=faz`
})
t.assert.strictEqual(injectResponse.statusCode, 200)
})
test('Custom querystring parser should be called also if there is nothing to parse', t => {
t.plan(9)
test('Custom querystring parser should be called also if there is nothing to parse', async t => {
t.plan(7)
const fastify = Fastify({
querystringParser: function (str) {
t.equal(str, '')
t.assert.strictEqual(str, '')
return querystring.parse(str)
}
})
fastify.get('/', (req, reply) => {
t.same(req.query, {})
t.assert.deepEqual(req.query, {})
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.teardown(() => fastify.close())
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'GET',
url: address
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const result = await fetch(fastifyServer)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
fastify.inject({
method: 'GET',
url: address
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const injectResponse = await fastify.inject({
method: 'GET',
url: fastifyServer
})
t.assert.strictEqual(injectResponse.statusCode, 200)
})
test('Querystring without value', t => {
t.plan(9)
test('Querystring without value', async t => {
t.plan(7)
const fastify = Fastify({
querystringParser: function (str) {
t.equal(str, 'foo')
t.assert.strictEqual(str, 'foo')
return querystring.parse(str)
}
})
fastify.get('/', (req, reply) => {
t.same(req.query, { foo: '' })
t.assert.deepEqual(req.query, { foo: '' })
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(err)
t.teardown(() => fastify.close())
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => fastify.close())
sget({
method: 'GET',
url: `${address}?foo`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const result = await fetch(`${fastifyServer}?foo`)
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
fastify.inject({
method: 'GET',
url: `${address}?foo`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
})
const injectResponse = await fastify.inject({
method: 'GET',
url: `${fastifyServer}?foo`
})
t.assert.strictEqual(injectResponse.statusCode, 200)
})
test('Custom querystring parser should be a function', t => {
@@ -127,9 +101,27 @@ test('Custom querystring parser should be a function', t => {
Fastify({
querystringParser: 10
})
t.fail('Should throw')
t.assert.fail('Should throw')
} catch (err) {
t.equal(
t.assert.strictEqual(
err.message,
"querystringParser option should be a function, instead got 'number'"
)
}
})
test('Custom querystring parser should be a function', t => {
t.plan(1)
try {
Fastify({
routerOptions: {
querystringParser: 10
}
})
t.fail('Should throw')
} catch (err) {
t.assert.equal(
err.message,
"querystringParser option should be a function, instead got 'number'"
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const { FSTDEP014 } = require('../lib/warnings')
// Silence the standard warning logs. We will test the messages explicitly.
process.removeAllListeners('warning')
test('setDefaultRoute should emit a deprecation warning', t => {
t.plan(2)
const fastify = Fastify()
const defaultRoute = (req, res) => {
res.end('hello from defaultRoute')
}
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'DeprecationWarning')
t.equal(warning.code, FSTDEP014.code)
}
t.teardown(() => {
process.removeListener('warning', onWarning)
FSTDEP014.emitted = false
})
fastify.setDefaultRoute(defaultRoute)
})
test('getDefaultRoute should emit a deprecation warning', t => {
t.plan(2)
const fastify = Fastify()
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'DeprecationWarning')
t.equal(warning.code, FSTDEP014.code)
}
t.teardown(() => {
process.removeListener('warning', onWarning)
FSTDEP014.emitted = false
})
fastify.getDefaultRoute()
})
test('should fail if defaultRoute is not a function', t => {
t.plan(1)
const fastify = Fastify()
const defaultRoute = {}
fastify.get('/', () => {})
try {
fastify.setDefaultRoute(defaultRoute)
} catch (error) {
t.equal(error.code, 'FST_ERR_DEFAULT_ROUTE_INVALID_TYPE')
}
})
test('correctly sets, returns, and calls defaultRoute', t => {
t.plan(3)
const fastify = Fastify()
const defaultRoute = (req, res) => {
res.end('hello from defaultRoute')
}
fastify.setDefaultRoute(defaultRoute)
const returnedDefaultRoute = fastify.getDefaultRoute()
t.equal(returnedDefaultRoute, defaultRoute)
fastify.get('/', () => {})
fastify.inject({
method: 'GET',
url: '/random'
}, (err, res) => {
t.error(err)
t.equal(res.body, 'hello from defaultRoute')
})
})

View File

@@ -1,8 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const { test } = require('node:test')
const fastify = require('..')()
const schema = {
@@ -85,15 +83,17 @@ const bodySchema = {
}
}
test('shorthand - delete', t => {
test('shorthand - delete', (t, done) => {
t.plan(1)
try {
fastify.delete('/', schema, function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
} finally {
done()
}
})
@@ -103,9 +103,9 @@ test('shorthand - delete params', t => {
fastify.delete('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.code(200).send(req.params)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -115,9 +115,9 @@ test('shorthand - delete, querystring schema', t => {
fastify.delete('/query', querySchema, function (req, reply) {
reply.send(req.query)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -127,9 +127,9 @@ test('shorthand - get, headers schema', t => {
fastify.delete('/headers', headersSchema, function (req, reply) {
reply.code(200).send(req.headers)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -139,9 +139,9 @@ test('missing schema - delete', t => {
fastify.delete('/missing', function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -151,161 +151,184 @@ test('body - delete', t => {
fastify.delete('/body', bodySchema, function (req, reply) {
reply.send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('delete tests', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
test('shorthand - request delete', t => {
await t.test('shorthand - request delete', async t => {
t.plan(4)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
const response = await fetch(fastifyServer, {
method: 'DELETE'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
test('shorthand - request delete params schema', t => {
await t.test('shorthand - request delete params schema', async t => {
t.plan(4)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/123'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { foo: 'world', test: 123 })
const response = await fetch(fastifyServer + '/params/world/123', {
method: 'DELETE'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { foo: 'world', test: 123 })
})
test('shorthand - request delete params schema error', t => {
await t.test('shorthand - request delete params schema error', async t => {
t.plan(3)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/string'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'params/test must be integer',
statusCode: 400
})
const response = await fetch(fastifyServer + '/params/world/string', {
method: 'DELETE'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
t.assert.deepStrictEqual(await response.json(), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'params/test must be integer',
statusCode: 400
})
})
test('shorthand - request delete headers schema', t => {
await t.test('shorthand - request delete headers schema', async t => {
t.plan(4)
sget({
const response = await fetch(fastifyServer + '/headers', {
method: 'DELETE',
headers: {
'x-test': 1
},
url: 'http://localhost:' + fastify.server.address().port + '/headers'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(JSON.parse(body)['x-test'], 1)
'x-test': '1'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.strictEqual(JSON.parse(body)['x-test'], 1)
})
test('shorthand - request delete headers schema error', t => {
await t.test('shorthand - request delete headers schema error', async t => {
t.plan(3)
sget({
const response = await fetch(fastifyServer + '/headers', {
method: 'DELETE',
headers: {
'x-test': 'abc'
},
url: 'http://localhost:' + fastify.server.address().port + '/headers'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'headers/x-test must be number',
statusCode: 400
})
}
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'headers/x-test must be number',
statusCode: 400
})
})
test('shorthand - request delete querystring schema', t => {
await t.test('shorthand - request delete querystring schema', async t => {
t.plan(4)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=123'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 123 })
const response = await fetch(fastifyServer + '/query?hello=123', {
method: 'DELETE'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 123 })
})
test('shorthand - request delete querystring schema error', t => {
await t.test('shorthand - request delete querystring schema error', async t => {
t.plan(3)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=world'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'querystring/hello must be integer',
statusCode: 400
})
const response = await fetch(fastifyServer + '/query?hello=world', {
method: 'DELETE'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'querystring/hello must be integer',
statusCode: 400
})
})
test('shorthand - request delete missing schema', t => {
await t.test('shorthand - request delete missing schema', async t => {
t.plan(4)
sget({
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/missing'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
const response = await fetch(fastifyServer + '/missing', {
method: 'DELETE'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
test('shorthand - delete with body', t => {
await t.test('shorthand - delete with body', async t => {
t.plan(3)
sget({
const response = await fetch(fastifyServer + '/body', {
method: 'DELETE',
url: 'http://localhost:' + fastify.server.address().port + '/body',
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'world' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.json()
t.assert.deepStrictEqual(body, { hello: 'world' })
})
})
test('shorthand - delete with application/json Content-Type header and null body', (t, done) => {
t.plan(4)
const fastify = require('..')()
fastify.delete('/', {}, (req, reply) => {
t.assert.strictEqual(req.body, null)
reply.send(req.body)
})
fastify.inject({
method: 'DELETE',
url: '/',
headers: { 'Content-Type': 'application/json' },
body: 'null'
}, (err, response) => {
t.assert.ifError(err)
t.assert.strictEqual(response.statusCode, 200)
t.assert.strictEqual(response.payload.toString(), 'null')
done()
})
})
// https://github.com/fastify/fastify/issues/936
test('shorthand - delete with application/json Content-Type header and without body', t => {
// Skip this test because this is an invalid request
test('shorthand - delete with application/json Content-Type header and without body', { skip: 'https://github.com/fastify/fastify/pull/5419' }, t => {
t.plan(4)
const fastify = require('..')()
fastify.delete('/', {}, (req, reply) => {
t.equal(req.body, undefined)
t.assert.strictEqual(req.body, undefined)
reply.send(req.body)
})
fastify.inject({
@@ -314,8 +337,8 @@ test('shorthand - delete with application/json Content-Type header and without b
headers: { 'Content-Type': 'application/json' },
body: null
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '')
t.assert.ifError(err)
t.assert.strictEqual(response.statusCode, 200)
t.assert.strictEqual(response.payload.toString(), '')
})
})

View File

@@ -1,61 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const proxyquire = require('proxyquire')
test('diagnostics_channel when present and subscribers', t => {
t.plan(3)
let fastifyInHook
const dc = {
channel (name) {
t.equal(name, 'fastify.initialization')
return {
hasSubscribers: true,
publish (event) {
t.ok(event.fastify)
fastifyInHook = event.fastify
}
}
},
'@noCallThru': true
}
const fastify = proxyquire('../fastify', {
'node:diagnostics_channel': dc
})()
t.equal(fastifyInHook, fastify)
})
test('diagnostics_channel when present and no subscribers', t => {
t.plan(1)
const dc = {
channel (name) {
t.equal(name, 'fastify.initialization')
return {
hasSubscribers: false,
publish () {
t.fail('publish should not be called')
}
}
},
'@noCallThru': true
}
proxyquire('../fastify', {
'node:diagnostics_channel': dc
})()
})
test('diagnostics_channel when not present', t => {
t.plan(1)
t.doesNotThrow(() => {
proxyquire('../fastify', {
'node:diagnostics_channel': null
})()
})
})

View File

@@ -0,0 +1,49 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel sync events fire in expected order', async t => {
t.plan(9)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
reply.callNotFound()
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const response = await fetch(fastifyServer, {
method: 'GET'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 404)
})

View File

@@ -0,0 +1,65 @@
'use strict'
const diagnostics = require('node:diagnostics_channel')
const { test } = require('node:test')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel async events fire in expected order', async t => {
t.plan(19)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.strictEqual(callOrder++, 1)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
t.assert.strictEqual(msg.async, true)
})
diagnostics.subscribe('tracing:fastify.request.handler:asyncStart', (msg) => {
t.assert.strictEqual(callOrder++, 2)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:asyncEnd', (msg) => {
t.assert.strictEqual(callOrder++, 3)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: async function (req, reply) {
setImmediate(() => reply.send({ hello: 'world' }))
return reply
}
})
t.after(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(fastifyServer + '/')
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})

View File

@@ -0,0 +1,64 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel async events fire in expected order', async t => {
t.plan(18)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.strictEqual(callOrder++, 1)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:asyncStart', (msg) => {
t.assert.strictEqual(callOrder++, 2)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:asyncEnd', (msg) => {
t.assert.strictEqual(callOrder++, 3)
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: async function (req, reply) {
return { hello: 'world' }
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const response = await fetch(fastifyServer)
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})

View File

@@ -0,0 +1,35 @@
'use strict'
const diagnostics = require('node:diagnostics_channel')
const { test } = require('node:test')
require('../../lib/hooks').onSendHookRunner = function Stub () {}
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
const symbols = require('../../lib/symbols.js')
const { preHandlerCallback } = require('../../lib/handle-request')[Symbol.for('internals')]
test('diagnostics channel handles an error before calling context handler', t => {
t.plan(3)
let callOrder = 0
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg.error.message, 'oh no')
})
const error = new Error('oh no')
const request = new Request()
const reply = new Reply({}, request)
request[symbols.kRouteContext] = {
config: {
url: '/foo',
method: 'GET'
}
}
preHandlerCallback(error, request, reply)
})

View File

@@ -0,0 +1,53 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel events report on errors', async t => {
t.plan(14)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(callOrder++, 2)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.ok(msg.error instanceof Error)
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg.error.message, 'borked')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
throw new Error('borked')
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const response = await fetch(fastifyServer, {
method: 'GET'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 500)
})

View File

@@ -0,0 +1,123 @@
'use strict'
const { test } = require('node:test')
const Fastify = require('../..')
const statusCodes = require('node:http').STATUS_CODES
const diagnostics = require('node:diagnostics_channel')
test('diagnostics channel error event should report correct status code', async (t) => {
t.plan(3)
const fastify = Fastify()
t.after(() => fastify.close())
let diagnosticsStatusCode
const channel = diagnostics.channel('tracing:fastify.request.handler:error')
const handler = (msg) => {
diagnosticsStatusCode = msg.reply.statusCode
}
channel.subscribe(handler)
t.after(() => channel.unsubscribe(handler))
fastify.get('/', async () => {
const err = new Error('test error')
err.statusCode = 503
throw err
})
const res = await fastify.inject('/')
t.assert.strictEqual(res.statusCode, 503)
t.assert.strictEqual(diagnosticsStatusCode, 503, 'diagnostics channel should report correct status code')
t.assert.strictEqual(diagnosticsStatusCode, res.statusCode, 'diagnostics status should match response status')
})
test('diagnostics channel error event should report 500 for errors without status', async (t) => {
t.plan(3)
const fastify = Fastify()
t.after(() => fastify.close())
let diagnosticsStatusCode
const channel = diagnostics.channel('tracing:fastify.request.handler:error')
const handler = (msg) => {
diagnosticsStatusCode = msg.reply.statusCode
}
channel.subscribe(handler)
t.after(() => channel.unsubscribe(handler))
fastify.get('/', async () => {
throw new Error('plain error without status')
})
const res = await fastify.inject('/')
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(diagnosticsStatusCode, 500, 'diagnostics channel should report 500 for plain errors')
t.assert.strictEqual(diagnosticsStatusCode, res.statusCode, 'diagnostics status should match response status')
})
test('diagnostics channel error event should report correct status with custom error handler', async (t) => {
t.plan(3)
const fastify = Fastify()
t.after(() => fastify.close())
let diagnosticsStatusCode
const channel = diagnostics.channel('tracing:fastify.request.handler:error')
const handler = (msg) => {
diagnosticsStatusCode = msg.reply.statusCode
}
channel.subscribe(handler)
t.after(() => channel.unsubscribe(handler))
fastify.setErrorHandler((error, request, reply) => {
reply.status(503).send({ error: error.message })
})
fastify.get('/', async () => {
throw new Error('handler error')
})
const res = await fastify.inject('/')
// Note: The diagnostics channel fires before the custom error handler runs,
// so it reports 500 (default) rather than 503 (set by custom handler).
// This is expected behavior - the error channel reports the initial error state.
t.assert.strictEqual(res.statusCode, 503)
t.assert.strictEqual(diagnosticsStatusCode, 500, 'diagnostics channel reports status before custom handler')
t.assert.notStrictEqual(diagnosticsStatusCode, res.statusCode, 'custom handler can change status after diagnostics')
})
test('Error.status property support', (t, done) => {
t.plan(4)
const fastify = Fastify()
t.after(() => fastify.close())
const err = new Error('winter is coming')
err.status = 418
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.strictEqual(msg.error.message, 'winter is coming')
})
fastify.get('/', () => {
return Promise.reject(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()
})
})

View File

@@ -0,0 +1,50 @@
'use strict'
const { test } = require('node:test')
const proxyquire = require('proxyquire')
test('diagnostics_channel when present and subscribers', t => {
t.plan(3)
let fastifyInHook
const diagnostics = {
channel (name) {
t.assert.strictEqual(name, 'fastify.initialization')
return {
hasSubscribers: true,
publish (event) {
t.assert.ok(event.fastify)
fastifyInHook = event.fastify
}
}
},
'@noCallThru': true
}
const fastify = proxyquire('../../fastify', {
'node:diagnostics_channel': diagnostics
})()
t.assert.strictEqual(fastifyInHook, fastify)
})
test('diagnostics_channel when present and no subscribers', t => {
t.plan(1)
const diagnostics = {
channel (name) {
t.assert.strictEqual(name, 'fastify.initialization')
return {
hasSubscribers: false,
publish () {
t.assert.fail('publish should not be called')
}
}
},
'@noCallThru': true
}
proxyquire('../../fastify', {
'node:diagnostics_channel': diagnostics
})()
})

View File

@@ -0,0 +1,49 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel sync events fire in expected order', async t => {
t.plan(10)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
setImmediate(() => reply.send({ hello: 'world' }))
}
})
t.after(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(fastifyServer + '/')
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})

View File

@@ -0,0 +1,51 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel sync events fire in expected order', async t => {
t.plan(10)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
reply.send({ hello: 'world' })
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const response = await fetch(fastifyServer, {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})

View File

@@ -0,0 +1,54 @@
'use strict'
const { test } = require('node:test')
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('../..')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('diagnostics channel sync events fire in expected order', async t => {
t.plan(13)
let callOrder = 0
let firstEncounteredMessage
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
t.assert.strictEqual(callOrder++, 0)
firstEncounteredMessage = msg
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.ok(msg.route)
t.assert.strictEqual(msg.route.url, '/:id')
t.assert.strictEqual(msg.route.method, 'GET')
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
t.assert.ok(msg.request instanceof Request)
t.assert.ok(msg.reply instanceof Reply)
t.assert.strictEqual(callOrder++, 1)
t.assert.strictEqual(msg, firstEncounteredMessage)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
t.assert.fail('should not trigger error channel')
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/:id',
handler: function (req, reply) {
return { hello: 'world' }
}
})
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const response = await fetch(fastifyServer + '/7', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})

View File

@@ -1,6 +1,6 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
const fp = require('fastify-plugin')
@@ -12,7 +12,7 @@ test('encapsulates an child logger factory', async t => {
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.customLog = function (message) {
t.equal(message, 'custom')
t.assert.strictEqual(message, 'custom')
}
return child
})
@@ -24,7 +24,7 @@ test('encapsulates an child logger factory', async t => {
fastify.setChildLoggerFactory(function globalFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.globalLog = function (message) {
t.equal(message, 'global')
t.assert.strictEqual(message, 'global')
}
return child
})
@@ -33,10 +33,10 @@ test('encapsulates an child logger factory', async t => {
})
const res1 = await fastify.inject('/encapsulated')
t.equal(res1.statusCode, 200)
t.assert.strictEqual(res1.statusCode, 200)
const res2 = await fastify.inject('/not-encapsulated')
t.equal(res2.statusCode, 200)
t.assert.strictEqual(res2.statusCode, 200)
})
test('child logger factory set on root scope when using fastify-plugin', async t => {
@@ -48,7 +48,7 @@ test('child logger factory set on root scope when using fastify-plugin', async t
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.customLog = function (message) {
t.equal(message, 'custom')
t.assert.strictEqual(message, 'custom')
}
return child
})
@@ -62,8 +62,8 @@ test('child logger factory set on root scope when using fastify-plugin', async t
})
const res1 = await fastify.inject('/not-encapsulated-1')
t.equal(res1.statusCode, 200)
t.assert.strictEqual(res1.statusCode, 200)
const res2 = await fastify.inject('/not-encapsulated-2')
t.equal(res2.statusCode, 200)
t.assert.strictEqual(res2.statusCode, 200)
})

View File

@@ -1,27 +1,68 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
test('encapsulates an error handler', async t => {
// Because of how error handlers wrap things, following the control flow can be tricky
// In this test file numbered comments indicate the order statements are expected to execute
test('encapsulates an asynchronous error handler', async t => {
t.plan(3)
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setErrorHandler(async function a (err) {
t.equal(err.message, 'kaboom')
throw new Error('caught')
// 3. the inner error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_endpoint')
throw new Error('from_inner')
})
fastify.get('/encapsulated', async () => {
// 2. the endpoint throws an error
throw new Error('from_endpoint')
})
fastify.get('/encapsulated', async () => { throw new Error('kaboom') })
})
fastify.setErrorHandler(async function b (err) {
t.equal(err.message, 'caught')
throw new Error('wrapped')
// 4. the outer error handler catches the error thrown by the inner error handler
t.assert.strictEqual(err.message, 'from_inner')
// 5. the outer error handler throws a new error
throw new Error('from_outer')
})
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
t.equal(res.json().message, 'wrapped')
// 6. the default error handler returns the error from the outer error handler
t.assert.strictEqual(res.json().message, 'from_outer')
})
// See discussion in https://github.com/fastify/fastify/pull/5222#discussion_r1432573655
test('encapsulates a synchronous error handler', async t => {
t.plan(3)
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setErrorHandler(function a (err) {
// 3. the inner error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_endpoint')
throw new Error('from_inner')
})
fastify.get('/encapsulated', async () => {
// 2. the endpoint throws an error
throw new Error('from_endpoint')
})
})
fastify.setErrorHandler(async function b (err) {
// 4. the outer error handler catches the error thrown by the inner error handler
t.assert.strictEqual(err.message, 'from_inner')
// 5. the outer error handler throws a new error
throw new Error('from_outer')
})
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
// 6. the default error handler returns the error from the outer error handler
t.assert.strictEqual(res.json().message, 'from_outer')
})
test('onError hook nested', async t => {
@@ -30,21 +71,167 @@ test('onError hook nested', async t => {
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setErrorHandler(async function a (err) {
t.equal(err.message, 'kaboom')
throw new Error('caught')
// 4. the inner error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_endpoint')
throw new Error('from_inner')
})
fastify.get('/encapsulated', async () => {
// 2. the endpoint throws an error
throw new Error('from_endpoint')
})
fastify.get('/encapsulated', async () => { throw new Error('kaboom') })
})
fastify.setErrorHandler(async function b (err) {
t.equal(err.message, 'caught')
throw new Error('wrapped')
// 5. the outer error handler catches the error thrown by the inner error handler
t.assert.strictEqual(err.message, 'from_inner')
// 6. the outer error handler throws a new error
throw new Error('from_outer')
})
fastify.addHook('onError', async function (request, reply, err) {
t.equal(err.message, 'kaboom')
// 3. the hook receives the error
t.assert.strictEqual(err.message, 'from_endpoint')
})
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
t.equal(res.json().message, 'wrapped')
// 7. the default error handler returns the error from the outer error handler
t.assert.strictEqual(res.json().message, 'from_outer')
})
// See https://github.com/fastify/fastify/issues/5220
test('encapuslates an error handler, for errors thrown in hooks', async t => {
t.plan(3)
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setErrorHandler(function a (err) {
// 3. the inner error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_hook')
throw new Error('from_inner')
})
fastify.addHook('onRequest', async () => {
// 2. the hook throws an error
throw new Error('from_hook')
})
fastify.get('/encapsulated', async () => {})
})
fastify.setErrorHandler(function b (err) {
// 4. the outer error handler catches the error thrown by the inner error handler
t.assert.strictEqual(err.message, 'from_inner')
// 5. the outer error handler throws a new error
throw new Error('from_outer')
})
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
// 6. the default error handler returns the error from the outer error handler
t.assert.strictEqual(res.json().message, 'from_outer')
})
// See https://github.com/fastify/fastify/issues/5220
test('encapuslates many synchronous error handlers that rethrow errors', async t => {
const DEPTH = 100
t.plan(DEPTH + 2)
/**
* This creates a very nested set of error handlers, that looks like:
* plugin
* - error handler
* - plugin
* - error handler
* - plugin
* ... {to DEPTH levels}
* - plugin
* - error handler
* - GET /encapsulated
*/
const createNestedRoutes = (fastify, depth) => {
if (depth < 0) {
throw new Error('Expected depth >= 0')
} else if (depth === 0) {
fastify.setErrorHandler(function a (err) {
// 3. innermost error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_route')
throw new Error(`from_handler_${depth}`)
})
fastify.get('/encapsulated', async () => {
// 2. the endpoint throws an error
throw new Error('from_route')
})
} else {
fastify.setErrorHandler(function d (err) {
// 4 to {DEPTH+4}. error handlers each catch errors, and then throws a new error
t.assert.strictEqual(err.message, `from_handler_${depth - 1}`)
throw new Error(`from_handler_${depth}`)
})
fastify.register(async function (fastify) {
createNestedRoutes(fastify, depth - 1)
})
}
}
const fastify = Fastify()
createNestedRoutes(fastify, DEPTH)
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
// {DEPTH+5}. the default error handler returns the error from the outermost error handler
t.assert.strictEqual(res.json().message, `from_handler_${DEPTH}`)
})
// See https://github.com/fastify/fastify/issues/5220
// This was not failing previously, but we want to make sure the behavior continues to work in the same way across async and sync handlers
// Plus, the current setup is somewhat fragile to tweaks to wrapThenable as that's what retries (by calling res.send(err) again)
test('encapuslates many asynchronous error handlers that rethrow errors', async t => {
const DEPTH = 100
t.plan(DEPTH + 2)
/**
* This creates a very nested set of error handlers, that looks like:
* plugin
* - error handler
* - plugin
* - error handler
* - plugin
* ... {to DEPTH levels}
* - plugin
* - error handler
* - GET /encapsulated
*/
const createNestedRoutes = (fastify, depth) => {
if (depth < 0) {
throw new Error('Expected depth >= 0')
} else if (depth === 0) {
fastify.setErrorHandler(async function a (err) {
// 3. innermost error handler catches the error, and throws a new error
t.assert.strictEqual(err.message, 'from_route')
throw new Error(`from_handler_${depth}`)
})
fastify.get('/encapsulated', async () => {
// 2. the endpoint throws an error
throw new Error('from_route')
})
} else {
fastify.setErrorHandler(async function m (err) {
// 4 to {DEPTH+4}. error handlers each catch errors, and then throws a new error
t.assert.strictEqual(err.message, `from_handler_${depth - 1}`)
throw new Error(`from_handler_${depth}`)
})
fastify.register(async function (fastify) {
createNestedRoutes(fastify, depth - 1)
})
}
}
const fastify = Fastify()
createNestedRoutes(fastify, DEPTH)
// 1. the endpoint is called
const res = await fastify.inject('/encapsulated')
// {DEPTH+5}. the default error handler returns the error from the outermost error handler
t.assert.strictEqual(res.json().message, `from_handler_${DEPTH}`)
})

View File

@@ -1,10 +1,10 @@
import { errorCodes } from '../../fastify.js'
import t from 'tap'
import { test } from 'node:test'
t.test('errorCodes in ESM', async t => {
test('errorCodes in ESM', async t => {
// test a custom fastify error using errorCodes with ESM
const customError = errorCodes.FST_ERR_VALIDATION('custom error message')
t.ok(typeof customError !== 'undefined')
t.ok(customError instanceof errorCodes.FST_ERR_VALIDATION)
t.equal(customError.message, 'custom error message')
t.assert.ok(typeof customError !== 'undefined')
t.assert.ok(customError instanceof errorCodes.FST_ERR_VALIDATION)
t.assert.strictEqual(customError.message, 'custom error message')
})

View File

@@ -1,7 +1,7 @@
import t from 'tap'
import { test } from 'node:test'
import Fastify from '../../fastify.js'
t.test('esm support', async t => {
test('esm support', async t => {
const fastify = Fastify()
fastify.register(import('./plugin.mjs'), { foo: 'bar' })
@@ -9,5 +9,5 @@ t.test('esm support', async t => {
await fastify.ready()
t.equal(fastify.foo, 'bar')
t.assert.strictEqual(fastify.foo, 'bar')
})

View File

@@ -1,18 +1,8 @@
'use strict'
const t = require('tap')
const semver = require('semver')
if (semver.lt(process.versions.node, '14.13.0')) {
t.skip('Skip named exports because Node version < 14.13.0')
} else {
// Node v8 throw a `SyntaxError: Unexpected token import`
// even if this branch is never touch in the code,
// by using `eval` we can avoid this issue.
// eslint-disable-next-line
new Function('module', 'return import(module)')('./named-exports.mjs').catch((err) => {
import('./named-exports.mjs')
.catch(err => {
process.nextTick(() => {
throw err
})
})
}

View File

@@ -1,8 +1,8 @@
import t from 'tap'
import { test } from 'node:test'
import { fastify } from '../../fastify.js'
// This test is executed in index.test.js
t.test('named exports support', async t => {
test('named exports support', async t => {
const app = fastify()
app.register(import('./plugin.mjs'), { foo: 'bar' })
@@ -10,5 +10,5 @@ t.test('named exports support', async t => {
await app.ready()
t.equal(app.foo, 'bar')
t.assert.strictEqual(app.foo, 'bar')
})

View File

@@ -1,8 +1,8 @@
// Imported in both index.test.js & esm.test.mjs
import t from 'tap'
import { strictEqual } from 'node:assert'
async function other (fastify, opts) {
t.equal(fastify.foo, 'bar')
strictEqual(fastify.foo, 'bar')
}
export default other

View File

@@ -1,19 +1,21 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const os = require('node:os')
const {
kOptions,
kErrorHandler,
kChildLoggerFactory
kChildLoggerFactory,
kState
} = require('../lib/symbols')
const isIPv6Missing = !Object.values(os.networkInterfaces()).flat().some(({ family }) => family === 'IPv6')
test('root fastify instance is an object', t => {
t.plan(1)
t.type(Fastify(), 'object')
t.assert.strictEqual(typeof Fastify(), 'object')
})
test('fastify instance should contains ajv options', t => {
@@ -25,7 +27,7 @@ test('fastify instance should contains ajv options', t => {
}
}
})
t.same(fastify[kOptions].ajv, {
t.assert.deepStrictEqual(fastify[kOptions].ajv, {
customOptions: {
nullable: false
},
@@ -43,7 +45,7 @@ test('fastify instance should contains ajv options.plugins nested arrays', t =>
plugins: [[]]
}
})
t.same(fastify[kOptions].ajv, {
t.assert.deepStrictEqual(fastify[kOptions].ajv, {
customOptions: {
nullable: false
},
@@ -53,7 +55,7 @@ test('fastify instance should contains ajv options.plugins nested arrays', t =>
test('fastify instance get invalid ajv options', t => {
t.plan(1)
t.throws(() => Fastify({
t.assert.throws(() => Fastify({
ajv: {
customOptions: 8
}
@@ -62,7 +64,7 @@ test('fastify instance get invalid ajv options', t => {
test('fastify instance get invalid ajv options.plugins', t => {
t.plan(1)
t.throws(() => Fastify({
t.assert.throws(() => Fastify({
ajv: {
customOptions: {},
plugins: 8
@@ -73,9 +75,9 @@ test('fastify instance get invalid ajv options.plugins', t => {
test('fastify instance should contain default errorHandler', t => {
t.plan(3)
const fastify = Fastify()
t.ok(fastify[kErrorHandler].func instanceof Function)
t.same(fastify.errorHandler, fastify[kErrorHandler].func)
t.same(Object.getOwnPropertyDescriptor(fastify, 'errorHandler').set, undefined)
t.assert.ok(fastify[kErrorHandler].func instanceof Function)
t.assert.deepStrictEqual(fastify.errorHandler, fastify[kErrorHandler].func)
t.assert.deepStrictEqual(Object.getOwnPropertyDescriptor(fastify, 'errorHandler').set, undefined)
})
test('errorHandler in plugin should be separate from the external one', async t => {
@@ -89,24 +91,24 @@ test('errorHandler in plugin should be separate from the external one', async t
instance.setErrorHandler(inPluginErrHandler)
t.notSame(instance.errorHandler, fastify.errorHandler)
t.equal(instance.errorHandler.name, 'bound inPluginErrHandler')
t.assert.notDeepStrictEqual(instance.errorHandler, fastify.errorHandler)
t.assert.strictEqual(instance.errorHandler.name, 'bound inPluginErrHandler')
done()
})
await fastify.ready()
t.ok(fastify[kErrorHandler].func instanceof Function)
t.same(fastify.errorHandler, fastify[kErrorHandler].func)
t.assert.ok(fastify[kErrorHandler].func instanceof Function)
t.assert.deepStrictEqual(fastify.errorHandler, fastify[kErrorHandler].func)
})
test('fastify instance should contain default childLoggerFactory', t => {
t.plan(3)
const fastify = Fastify()
t.ok(fastify[kChildLoggerFactory] instanceof Function)
t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
t.same(Object.getOwnPropertyDescriptor(fastify, 'childLoggerFactory').set, undefined)
t.assert.ok(fastify[kChildLoggerFactory] instanceof Function)
t.assert.deepStrictEqual(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
t.assert.deepStrictEqual(Object.getOwnPropertyDescriptor(fastify, 'childLoggerFactory').set, undefined)
})
test('childLoggerFactory in plugin should be separate from the external one', async t => {
@@ -120,16 +122,16 @@ test('childLoggerFactory in plugin should be separate from the external one', as
instance.setChildLoggerFactory(inPluginLoggerFactory)
t.notSame(instance.childLoggerFactory, fastify.childLoggerFactory)
t.equal(instance.childLoggerFactory.name, 'inPluginLoggerFactory')
t.assert.notDeepStrictEqual(instance.childLoggerFactory, fastify.childLoggerFactory)
t.assert.strictEqual(instance.childLoggerFactory.name, 'inPluginLoggerFactory')
done()
})
await fastify.ready()
t.ok(fastify[kChildLoggerFactory] instanceof Function)
t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
t.assert.ok(fastify[kChildLoggerFactory] instanceof Function)
t.assert.deepStrictEqual(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
})
test('ready should resolve in order when called multiply times (promises only)', async (t) => {
@@ -142,7 +144,7 @@ test('ready should resolve in order when called multiply times (promises only)',
await Promise.all(promises)
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (promises only)', async (t) => {
@@ -159,7 +161,7 @@ test('ready should reject in order when called multiply times (promises only)',
await Promise.all(promises)
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (callbacks only)', async (t) => {
@@ -174,10 +176,10 @@ test('ready should reject in order when called multiply times (callbacks only)',
expectedOrder.map((id) => app.ready(() => result.push(id)))
await app.ready().catch(err => {
t.equal(err.message, 'test')
t.assert.strictEqual(err.message, 'test')
})
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (callbacks only)', async (t) => {
@@ -189,7 +191,7 @@ test('ready should resolve in order when called multiply times (callbacks only)'
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (mixed)', async (t) => {
@@ -207,7 +209,7 @@ test('ready should resolve in order when called multiply times (mixed)', async (
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (mixed)', async (t) => {
@@ -228,10 +230,10 @@ test('ready should reject in order when called multiply times (mixed)', async (t
}
await app.ready().catch(err => {
t.equal(err.message, 'test')
t.assert.strictEqual(err.message, 'test')
})
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (mixed)', async (t) => {
@@ -249,7 +251,7 @@ test('ready should resolve in order when called multiply times (mixed)', async (
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
t.assert.deepStrictEqual(result, expectedOrder, 'Should resolve in order')
})
test('fastify instance should contains listeningOrigin property (with port and host)', async t => {
@@ -258,7 +260,7 @@ test('fastify instance should contains listeningOrigin property (with port and h
const host = '127.0.0.1'
const fastify = Fastify()
await fastify.listen({ port, host })
t.same(fastify.listeningOrigin, `http://${host}:${port}`)
t.assert.deepStrictEqual(fastify.listeningOrigin, `http://${host}:${port}`)
await fastify.close()
})
@@ -268,7 +270,7 @@ test('fastify instance should contains listeningOrigin property (with port and h
const host = '127.0.0.1'
const fastify = Fastify({ https: {} })
await fastify.listen({ port, host })
t.same(fastify.listeningOrigin, `https://${host}:${port}`)
t.assert.deepStrictEqual(fastify.listeningOrigin, `https://${host}:${port}`)
await fastify.close()
})
@@ -276,16 +278,23 @@ test('fastify instance should contains listeningOrigin property (unix socket)',
const fastify = Fastify()
const path = `fastify.${Date.now()}.sock`
await fastify.listen({ path })
t.same(fastify.listeningOrigin, path)
t.assert.deepStrictEqual(fastify.listeningOrigin, path)
await fastify.close()
})
test('fastify instance should contains listeningOrigin property (IPv6)', async t => {
test('fastify instance should contains listeningOrigin property (IPv6)', { skip: isIPv6Missing }, async t => {
t.plan(1)
const port = 3000
const host = '::1'
const fastify = Fastify()
await fastify.listen({ port, host })
t.same(fastify.listeningOrigin, `http://[::1]:${port}`)
t.assert.deepStrictEqual(fastify.listeningOrigin, `http://[::1]:${port}`)
await fastify.close()
})
test('fastify instance should ensure ready promise cleanup on ready', async t => {
t.plan(1)
const fastify = Fastify()
await fastify.ready()
t.assert.strictEqual(fastify[kState].readyResolver, null)
})

View File

@@ -1,6 +1,6 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const Fastify = require('..')
const fastifyPlugin = require('fastify-plugin')
@@ -15,7 +15,7 @@ test('findRoute should return null when route cannot be found due to a different
handler: (req, reply) => reply.send(typeof req.params.artistId)
})
t.equal(fastify.findRoute({
t.assert.strictEqual(fastify.findRoute({
method: 'POST',
url: '/artists/:artistId'
}), null)
@@ -46,7 +46,24 @@ test('findRoute should return an immutable route to avoid leaking and runtime ro
method: 'GET',
url: '/artists/:artistId'
})
t.same(route.params, { artistId: ':artistId' })
t.assert.strictEqual(route.params.artistId, ':artistId')
})
test('findRoute should return null when when url is not passed', t => {
t.plan(1)
const fastify = Fastify()
fastify.get('/artists/:artistId', {
schema: {
params: { artistId: { type: 'integer' } }
},
handler: (req, reply) => reply.send(typeof req.params.artistId)
})
t.assert.strictEqual(fastify.findRoute({
method: 'POST'
}), null)
})
test('findRoute should return null when route cannot be found due to a different path', t => {
@@ -60,7 +77,7 @@ test('findRoute should return null when route cannot be found due to a different
handler: (req, reply) => reply.send(typeof req.params.artistId)
})
t.equal(fastify.findRoute({
t.assert.strictEqual(fastify.findRoute({
method: 'GET',
url: '/books/:bookId'
}), null)
@@ -83,11 +100,10 @@ test('findRoute should return the route when found', t => {
method: 'GET',
url: '/artists/:artistId'
})
t.same(route.params, { artistId: ':artistId' })
t.assert.strictEqual(route.params.artistId, ':artistId')
})
test('findRoute should work correctly when used within plugins', t => {
test('findRoute should work correctly when used within plugins', (t, done) => {
t.plan(1)
const fastify = Fastify()
const handler = (req, reply) => reply.send(typeof req.params.artistId)
@@ -112,7 +128,8 @@ test('findRoute should work correctly when used within plugins', t => {
fastify.register(fastifyPlugin(validateRoutePlugin))
fastify.ready(() => {
t.equal(fastify.validateRoutes.validateParams(), true)
t.assert.strictEqual(fastify.validateRoutes.validateParams(), true)
done()
})
})
@@ -131,5 +148,5 @@ test('findRoute should not expose store', t => {
method: 'GET',
url: '/artists/:artistId'
})
t.equal(route.store, undefined)
t.assert.strictEqual(route.store, undefined)
})

View File

@@ -1,12 +1,11 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const S = require('fluent-json-schema')
test('use fluent-json-schema object', t => {
t.plan(15)
test('use fluent-json-schema object', async (t) => {
t.plan(10)
const fastify = Fastify()
fastify.post('/:id', {
@@ -24,73 +23,62 @@ test('use fluent-json-schema object', t => {
}
})
// check params
fastify.inject({
const res1 = await fastify.inject({
method: 'POST',
url: '/1',
headers: { 'x-custom': 'me@me.me' },
query: { surname: 'bar' },
payload: { name: 'foo' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'params/id must be >= 42' })
})
t.assert.strictEqual(res1.statusCode, 400)
t.assert.deepStrictEqual(res1.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'params/id must be >= 42' })
// check header
fastify.inject({
const res2 = await fastify.inject({
method: 'POST',
url: '/42',
headers: { 'x-custom': 'invalid' },
query: { surname: 'bar' },
payload: { name: 'foo' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'headers/x-custom must match format "email"' })
})
t.assert.strictEqual(res2.statusCode, 400)
t.assert.deepStrictEqual(res2.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'headers/x-custom must match format "email"' })
// check query
fastify.inject({
const res3 = await fastify.inject({
method: 'POST',
url: '/42',
headers: { 'x-custom': 'me@me.me' },
query: { },
payload: { name: 'foo' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'querystring must have required property \'surname\'' })
})
t.assert.strictEqual(res3.statusCode, 400)
t.assert.deepStrictEqual(res3.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'querystring must have required property \'surname\'' })
// check body
fastify.inject({
const res4 = await fastify.inject({
method: 'POST',
url: '/42',
headers: { 'x-custom': 'me@me.me' },
query: { surname: 'bar' },
payload: { name: [1, 2, 3] }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'body/name must be string' })
})
t.assert.strictEqual(res4.statusCode, 400)
t.assert.deepStrictEqual(res4.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'body/name must be string' })
// check response
fastify.inject({
const res5 = await fastify.inject({
method: 'POST',
url: '/42',
headers: { 'x-custom': 'me@me.me' },
query: { surname: 'bar' },
payload: { name: 'foo' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.same(res.json(), { name: 'a', surname: 'b' })
})
t.assert.strictEqual(res5.statusCode, 200)
t.assert.deepStrictEqual(res5.json(), { name: 'a', surname: 'b' })
})
test('use complex fluent-json-schema object', t => {
test('use complex fluent-json-schema object', (t, done) => {
t.plan(1)
const fastify = Fastify()
@@ -113,10 +101,13 @@ test('use complex fluent-json-schema object', t => {
.prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required()
fastify.post('/the/url', { schema: { body: bodyJsonSchema } }, () => { })
fastify.ready(err => t.error(err))
fastify.ready(err => {
t.assert.ifError(err)
done()
})
})
test('use fluent schema and plain JSON schema', t => {
test('use fluent schema and plain JSON schema', (t, done) => {
t.plan(1)
const fastify = Fastify()
@@ -154,10 +145,13 @@ test('use fluent schema and plain JSON schema', t => {
.prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required()
fastify.post('/the/url', { schema: { body: bodyJsonSchema } }, () => { })
fastify.ready(err => t.error(err))
fastify.ready(err => {
t.assert.ifError(err)
done()
})
})
test('Should call valueOf internally', t => {
test('Should call valueOf internally', (t, done) => {
t.plan(1)
const fastify = new Fastify()
@@ -208,5 +202,8 @@ test('Should call valueOf internally', t => {
}
})
fastify.ready(t.error)
fastify.ready(err => {
t.assert.ifError(err)
done()
})
})

View File

@@ -1,11 +1,11 @@
'use strict'
const { Readable } = require('node:stream')
const { test } = require('tap')
const { test } = require('node:test')
const fp = require('fastify-plugin')
const Fastify = require('..')
test('Should accept a custom genReqId function', t => {
test('Should accept a custom genReqId function', (t, done) => {
t.plan(4)
const fastify = Fastify({
@@ -14,67 +14,78 @@ test('Should accept a custom genReqId function', t => {
}
})
t.after(() => fastify.close())
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
url: `http://localhost:${fastify.server.address().port}`
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'a')
fastify.close()
t.assert.strictEqual(payload.id, 'a')
done()
})
})
})
test('Custom genReqId function gets raw request as argument', t => {
test('Custom genReqId function gets raw request as argument', (t, done) => {
t.plan(9)
const REQUEST_ID = 'REQ-1234'
const fastify = Fastify({
genReqId: function (req) {
t.notOk('id' in req)
t.notOk('raw' in req)
t.ok(req instanceof Readable)
t.assert.strictEqual('id' in req, false)
t.assert.strictEqual('raw' in req, false)
t.assert.ok(req instanceof Readable)
// http.IncomingMessage does have `rawHeaders` property, but FastifyRequest does not
const index = req.rawHeaders.indexOf('x-request-id')
const xReqId = req.rawHeaders[index + 1]
t.equal(xReqId, REQUEST_ID)
t.equal(req.headers['x-request-id'], REQUEST_ID)
t.assert.strictEqual(xReqId, REQUEST_ID)
t.assert.strictEqual(req.headers['x-request-id'], REQUEST_ID)
return xReqId
}
})
t.after(() => fastify.close())
fastify.get('/', (req, reply) => {
t.equal(req.id, REQUEST_ID)
t.assert.strictEqual(req.id, REQUEST_ID)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.inject({
method: 'GET',
headers: {
'x-request-id': REQUEST_ID
},
url: 'http://localhost:' + fastify.server.address().port
url: `http://localhost:${fastify.server.address().port}`
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, REQUEST_ID)
fastify.close()
t.assert.strictEqual(payload.id, REQUEST_ID)
done()
})
})
})
test('Should accept option to set genReqId with setGenReqId option', t => {
test('Should handle properly requestIdHeader option', t => {
t.plan(4)
t.assert.strictEqual(Fastify({ requestIdHeader: '' }).initialConfig.requestIdHeader, false)
t.assert.strictEqual(Fastify({ requestIdHeader: false }).initialConfig.requestIdHeader, false)
t.assert.strictEqual(Fastify({ requestIdHeader: true }).initialConfig.requestIdHeader, 'request-id')
t.assert.strictEqual(Fastify({ requestIdHeader: 'x-request-id' }).initialConfig.requestIdHeader, 'x-request-id')
})
test('Should accept option to set genReqId with setGenReqId option', (t, done) => {
t.plan(9)
const fastify = Fastify({
@@ -83,12 +94,14 @@ test('Should accept option to set genReqId with setGenReqId option', t => {
}
})
t.after(() => fastify.close())
fastify.register(function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -99,49 +112,57 @@ test('Should accept option to set genReqId with setGenReqId option', t => {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
}, { prefix: 'bar' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
let pending = 3
function completed () {
if (--pending === 0) {
done()
}
}
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
t.assert.strictEqual(payload.id, 'base')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
t.assert.strictEqual(payload.id, 'foo')
completed()
})
fastify.inject({
method: 'GET',
url: '/bar'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
t.assert.strictEqual(payload.id, 'bar')
completed()
})
})
test('Should encapsulate setGenReqId', t => {
test('Should encapsulate setGenReqId', (t, done) => {
t.plan(12)
const fastify = Fastify({
@@ -150,6 +171,7 @@ test('Should encapsulate setGenReqId', t => {
}
})
t.after(() => fastify.close())
const bazInstance = function (instance, opts, next) {
instance.register(barInstance, { prefix: 'baz' })
@@ -157,7 +179,7 @@ test('Should encapsulate setGenReqId', t => {
return 'baz'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -168,7 +190,7 @@ test('Should encapsulate setGenReqId', t => {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -183,7 +205,7 @@ test('Should encapsulate setGenReqId', t => {
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -192,159 +214,71 @@ test('Should encapsulate setGenReqId', t => {
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
let pending = 4
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/bar'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/baz'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'baz')
fastify.close()
})
})
test('Should encapsulate setGenReqId', t => {
t.plan(12)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
function completed () {
if (--pending === 0) {
done()
}
})
const bazInstance = function (instance, opts, next) {
instance.register(barInstance, { prefix: 'baz' })
instance.setGenReqId(function (req) {
return 'baz'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const barInstance = function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const fooInstance = function (instance, opts, next) {
instance.register(bazInstance, { prefix: 'baz' })
instance.register(barInstance, { prefix: 'bar' })
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
t.assert.strictEqual(payload.id, 'base')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
t.assert.strictEqual(payload.id, 'foo')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo/bar'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
t.assert.strictEqual(payload.id, 'bar')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo/baz'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'baz')
fastify.close()
t.assert.strictEqual(payload.id, 'baz')
completed()
})
})
test('Should not alter parent of genReqId', t => {
test('Should not alter parent of genReqId', (t, done) => {
t.plan(6)
const fastify = Fastify()
t.after(() => fastify.close())
const fooInstance = function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -353,32 +287,40 @@ test('Should not alter parent of genReqId', t => {
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
let pending = 2
function completed () {
if (--pending === 0) {
done()
}
}
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'req-1')
fastify.close()
t.assert.strictEqual(payload.id, 'req-1')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
t.assert.strictEqual(payload.id, 'foo')
completed()
})
})
test('Should have child instance user parent genReqId', t => {
test('Should have child instance user parent genReqId', (t, done) => {
t.plan(6)
const fastify = Fastify({
@@ -386,10 +328,11 @@ test('Should have child instance user parent genReqId', t => {
return 'foo'
}
})
t.after(() => fastify.close())
const fooInstance = function (instance, opts, next) {
instance.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
next()
@@ -398,69 +341,86 @@ test('Should have child instance user parent genReqId', t => {
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
let pending = 2
function completed () {
if (--pending === 0) {
done()
}
}
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
t.assert.strictEqual(payload.id, 'foo')
completed()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
t.assert.strictEqual(payload.id, 'foo')
completed()
})
})
test('genReqId set on root scope when using fastify-plugin', t => {
test('genReqId set on root scope when using fastify-plugin', (t, done) => {
t.plan(6)
const fastify = Fastify()
t.after(() => fastify.close())
fastify.register(fp(function (fastify, options, done) {
fastify.setGenReqId(function (req) {
return 'not-encapsulated'
})
fastify.get('/not-encapsulated-1', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
done()
}))
fastify.get('/not-encapsulated-2', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
let pending = 2
function completed () {
if (--pending === 0) {
done()
}
}
fastify.inject({
method: 'GET',
url: '/not-encapsulated-1'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
t.assert.strictEqual(payload.id, 'not-encapsulated')
completed()
})
fastify.inject({
method: 'GET',
url: '/not-encapsulated-2'
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
t.assert.strictEqual(payload.id, 'not-encapsulated')
completed()
})
})

View File

@@ -1,383 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
const schema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
const nullSchema = {
schema: {
response: {
'2xx': {
type: 'null'
}
}
}
}
const numberSchema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'number'
}
}
}
}
}
}
const querySchema = {
schema: {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
}
const paramsSchema = {
schema: {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
}
const headersSchema = {
schema: {
headers: {
type: 'object',
properties: {
'x-test': {
type: 'number'
},
'Y-Test': {
type: 'number'
}
}
}
}
}
test('shorthand - get', t => {
t.plan(1)
try {
fastify.get('/', schema, function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - get (return null)', t => {
t.plan(1)
try {
fastify.get('/null', nullSchema, function (req, reply) {
reply.code(200).send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - get params', t => {
t.plan(1)
try {
fastify.get('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.code(200).send(req.params)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - get, querystring schema', t => {
t.plan(1)
try {
fastify.get('/query', querySchema, function (req, reply) {
reply.code(200).send(req.query)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - get, headers schema', t => {
t.plan(1)
try {
fastify.get('/headers', headersSchema, function (req, reply) {
reply.code(200).send(req.headers)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('missing schema - get', t => {
t.plan(1)
try {
fastify.get('/missing', function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
} catch (e) {
t.fail()
}
})
test('custom serializer - get', t => {
t.plan(1)
function customSerializer (data) {
return JSON.stringify(data)
}
try {
fastify.get('/custom-serializer', numberSchema, function (req, reply) {
reply.code(200).serializer(customSerializer).send({ hello: 'world' })
})
t.pass()
} catch (e) {
t.fail()
}
})
test('empty response', t => {
t.plan(1)
try {
fastify.get('/empty', function (req, reply) {
reply.code(200).send()
})
t.pass()
} catch (e) {
t.fail()
}
})
test('send a falsy boolean', t => {
t.plan(1)
try {
fastify.get('/boolean', function (req, reply) {
reply.code(200).send(false)
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('shorthand - request get', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
})
})
test('shorthand - request get params schema', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/123'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { foo: 'world', test: 123 })
})
})
test('shorthand - request get params schema error', t => {
t.plan(3)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/string'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'params/test must be integer',
statusCode: 400
})
})
})
test('shorthand - request get headers schema', t => {
t.plan(4)
sget({
method: 'GET',
headers: {
'x-test': '1',
'Y-Test': '3'
},
json: true,
url: 'http://localhost:' + fastify.server.address().port + '/headers'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body['x-test'], 1)
t.equal(body['y-test'], 3)
})
})
test('shorthand - request get headers schema error', t => {
t.plan(3)
sget({
method: 'GET',
headers: {
'x-test': 'abc'
},
url: 'http://localhost:' + fastify.server.address().port + '/headers'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'headers/x-test must be number',
statusCode: 400
})
})
})
test('shorthand - request get querystring schema', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=123'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 123 })
})
})
test('shorthand - request get querystring schema error', t => {
t.plan(3)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=world'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'querystring/hello must be integer',
statusCode: 400
})
})
})
test('shorthand - request get missing schema', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/missing'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
})
})
test('shorthand - custom serializer', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/custom-serializer'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
})
})
test('shorthand - empty response', t => {
t.plan(4)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/empty'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '0')
t.same(body.toString(), '')
})
})
test('shorthand - send a falsy boolean', t => {
t.plan(3)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/boolean'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'false')
})
})
test('shorthand - send null value', t => {
t.plan(3)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/null'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), 'null')
})
})
})

View File

@@ -1,7 +1,7 @@
'use strict'
const test = require('tap').test
const { test } = require('node:test')
const { kRouteContext } = require('../lib/symbols')
const fastify = require('../')
const fastify = require('..')
test('handlers receive correct `this` context', async (t) => {
t.plan(4)
@@ -17,15 +17,15 @@ test('handlers receive correct `this` context', async (t) => {
instance.register(plugin)
instance.get('/', function (req, reply) {
t.ok(this.foo)
t.equal(this.foo, 'foo')
t.assert.ok(this.foo)
t.assert.strictEqual(this.foo, 'foo')
reply.send()
})
await instance.inject('/')
t.ok(instance.foo)
t.equal(instance.foo, 'foo')
t.assert.ok(instance.foo)
t.assert.strictEqual(instance.foo, 'foo')
})
test('handlers have access to the internal context', async (t) => {
@@ -33,11 +33,11 @@ test('handlers have access to the internal context', async (t) => {
const instance = fastify()
instance.get('/', { config: { foo: 'bar' } }, function (req, reply) {
t.ok(reply[kRouteContext])
t.ok(reply[kRouteContext].config)
t.type(reply[kRouteContext].config, Object)
t.ok(reply[kRouteContext].config.foo)
t.equal(reply[kRouteContext].config.foo, 'bar')
t.assert.ok(reply[kRouteContext])
t.assert.ok(reply[kRouteContext].config)
t.assert.ok(typeof reply[kRouteContext].config, Object)
t.assert.ok(reply[kRouteContext].config.foo)
t.assert.strictEqual(reply[kRouteContext].config.foo, 'bar')
reply.send()
})

View File

@@ -1,22 +1,17 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../fastify')
const { test, describe } = require('node:test')
const Fastify = require('..')
test('hasRoute', t => {
t.plan(5)
const test = t.test
const fastify = Fastify()
const fastify = Fastify()
describe('hasRoute', async t => {
test('hasRoute - invalid options', t => {
t.plan(3)
t.equal(fastify.hasRoute({ }), false)
t.equal(fastify.hasRoute({ method: 'GET' }), false)
t.equal(fastify.hasRoute({ constraints: [] }), false)
t.assert.strictEqual(fastify.hasRoute({ }), false)
t.assert.strictEqual(fastify.hasRoute({ method: 'GET' }), false)
t.assert.strictEqual(fastify.hasRoute({ constraints: [] }), false)
})
test('hasRoute - primitive method', t => {
@@ -29,12 +24,12 @@ test('hasRoute', t => {
}
})
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'GET',
url: '/'
}), true)
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'POST',
url: '/'
}), false)
@@ -51,13 +46,13 @@ test('hasRoute', t => {
}
})
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' }
}), true)
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'GET',
url: '/',
constraints: { version: '1.3.0' }
@@ -69,9 +64,9 @@ test('hasRoute', t => {
// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'GET',
url: '/example/12345.png'
url: '/example/:file(^\\d+).png'
}), true)
})
@@ -85,7 +80,7 @@ test('hasRoute', t => {
}
})
t.equal(fastify.hasRoute({
t.assert.strictEqual(fastify.hasRoute({
method: 'get',
url: '/equal'
}), true)

View File

@@ -1,368 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
const schema = {
schema: {
response: {
'2xx': {
type: 'null'
}
}
}
}
const querySchema = {
schema: {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
}
const paramsSchema = {
schema: {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
}
test('shorthand - head', t => {
t.plan(1)
try {
fastify.head('/', schema, function (req, reply) {
reply.code(200).send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - custom head', t => {
t.plan(1)
try {
fastify.get('/proxy/*', function (req, reply) {
reply.code(200).send(null)
})
fastify.head('/proxy/*', function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - custom head with constraints', t => {
t.plan(1)
try {
fastify.get('/proxy/*', { constraints: { version: '1.0.0' } }, function (req, reply) {
reply.code(200).send(null)
})
fastify.head('/proxy/*', { constraints: { version: '1.0.0' } }, function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - should not reset a head route', t => {
t.plan(1)
try {
fastify.get('/query1', function (req, reply) {
reply.code(200).send(null)
})
fastify.put('/query1', function (req, reply) {
reply.code(200).send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - should override head route when setting multiple routes', t => {
t.plan(1)
try {
fastify.route({
method: 'GET',
url: '/query2',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
fastify.route({
method: ['POST', 'PUT', 'HEAD'],
url: '/query2',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
t.pass()
} catch (e) {
console.log(e)
t.fail()
}
})
test('shorthand - should override head route when setting multiple routes', t => {
t.plan(1)
try {
fastify.route({
method: ['GET'],
url: '/query3',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
fastify.route({
method: ['POST', 'PUT', 'HEAD'],
url: '/query3',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
t.pass()
} catch (e) {
console.log(e)
t.fail()
}
})
test('shorthand - should set get and head route in the same api call', t => {
t.plan(1)
try {
fastify.route({
method: ['HEAD', 'GET'],
url: '/query4',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
t.pass()
} catch (e) {
console.log(e)
t.fail()
}
})
test('shorthand - head params', t => {
t.plan(1)
try {
fastify.head('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.send(null)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - head, querystring schema', t => {
t.plan(1)
try {
fastify.head('/query', querySchema, function (req, reply) {
reply.code(200).send(null)
})
t.pass()
} catch (e) {
console.log(e)
t.fail()
}
})
test('missing schema - head', t => {
t.plan(1)
try {
fastify.head('/missing', function (req, reply) {
reply.code(200).send(null)
})
t.pass()
} catch (e) {
console.log(e)
t.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('shorthand - request head', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
test('shorthand - request head params schema', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/123'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
test('shorthand - request head params schema error', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/params/world/string'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 400)
})
})
test('shorthand - request head querystring schema', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=123'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
test('shorthand - request head querystring schema error', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query?hello=world'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 400)
})
})
test('shorthand - request head missing schema', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/missing'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
test('shorthand - request head custom head', t => {
t.plan(3)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/proxy/test'
}, (err, response) => {
t.error(err)
t.equal(response.headers['x-foo'], 'bar')
t.equal(response.statusCode, 200)
})
})
test('shorthand - request head custom head with constraints', t => {
t.plan(3)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/proxy/test',
headers: {
version: '1.0.0'
}
}, (err, response) => {
t.error(err)
t.equal(response.headers['x-foo'], 'bar')
t.equal(response.statusCode, 200)
})
})
test('shorthand - should not reset a head route', t => {
t.plan(2)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query1'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
})
test('shorthand - should override head route when setting multiple routes', t => {
t.plan(3)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query2'
}, (err, response) => {
t.error(err)
t.equal(response.headers['x-foo'], 'bar')
t.equal(response.statusCode, 200)
})
})
test('shorthand - should override head route when setting multiple routes', t => {
t.plan(3)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query3'
}, (err, response) => {
t.error(err)
t.equal(response.headers['x-foo'], 'bar')
t.equal(response.statusCode, 200)
})
})
test('shorthand - should set get and head route in the same api call', t => {
t.plan(3)
sget({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/query4'
}, (err, response) => {
t.error(err)
t.equal(response.headers['x-foo'], 'bar')
t.equal(response.statusCode, 200)
})
})
})

View File

@@ -1,14 +1,12 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('..')
const sget = require('simple-get').concat
const maxHeaderSize = 1024
test('Should return 431 if request header fields are too large', t => {
t.plan(3)
test('Should return 431 if request header fields are too large', async (t) => {
t.plan(2)
const fastify = Fastify({ http: { maxHeaderSize } })
fastify.route({
@@ -19,26 +17,23 @@ test('Should return 431 if request header fields are too large', t => {
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Large-Header': 'a'.repeat(maxHeaderSize)
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 431)
})
const result = await fetch(fastifyServer, {
method: 'GET',
headers: {
'Large-Header': 'a'.repeat(maxHeaderSize)
}
})
t.teardown(() => fastify.close())
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 431)
t.after(() => fastify.close())
})
test('Should return 431 if URI is too long', t => {
t.plan(3)
test('Should return 431 if URI is too long', async (t) => {
t.plan(2)
const fastify = Fastify({ http: { maxHeaderSize } })
fastify.route({
@@ -49,17 +44,12 @@ test('Should return 431 if URI is too long', t => {
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
const fastifyServer = await fastify.listen({ port: 0 })
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + `/${'a'.repeat(maxHeaderSize)}`
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 431)
})
})
const result = await fetch(`${fastifyServer}/${'a'.repeat(maxHeaderSize)}`)
t.teardown(() => fastify.close())
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 431)
t.after(() => fastify.close())
})

View File

@@ -1,16 +1,17 @@
'use strict'
const sget = require('simple-get').concat
const dns = require('node:dns').promises
const stream = require('node:stream')
const { promisify } = require('node:util')
const symbols = require('../lib/symbols')
const { waitForCb } = require('./toolkit')
const assert = require('node:assert')
module.exports.sleep = promisify(setTimeout)
/**
* @param method HTTP request method
* @param t tap instance
* @param t node:test instance
* @param isSetErrorHandler true: using setErrorHandler
*/
module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
@@ -19,8 +20,8 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
if (isSetErrorHandler) {
fastify.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
assert.ok(request instanceof fastify[symbols.kRequest].parent)
assert.strictEqual(typeof request, 'object')
reply
.code(err.statusCode)
.type('application/json; charset=utf-8')
@@ -52,9 +53,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/', schema, function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -64,9 +65,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/missing', function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -77,9 +78,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
req.body.hello = req.body.hello + req.query.foo
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -89,111 +90,114 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/with-limit', { bodyLimit: 1 }, function (req, reply) {
reply.send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, function (err) {
if (err) {
t.error(err)
t.assert.ifError(err)
return
}
t.teardown(() => { fastify.close() })
t.after(() => { fastify.close() })
test(`${upMethod} - correctly replies`, t => {
test(`${upMethod} - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'world' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
test(`${upMethod} - correctly replies with very large body`, t => {
test(`${upMethod} - correctly replies with very large body`, async (t) => {
t.plan(3)
const largeString = 'world'.repeat(13200)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: { hello: largeString },
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: largeString })
body: JSON.stringify({ hello: largeString }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: largeString })
})
test(`${upMethod} - correctly replies if the content type has the charset`, t => {
test(`${upMethod} - correctly replies if the content type has the charset`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: JSON.stringify({ hello: 'world' }),
headers: {
'content-type': 'application/json; charset=utf-8'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
})
test(`${upMethod} without schema - correctly replies`, t => {
test(`${upMethod} without schema - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'world' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
test(`${upMethod} with body and querystring - correctly replies`, t => {
test(`${upMethod} with body and querystring - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/with-query?foo=hello', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/with-query?foo=hello',
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'worldhello' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'worldhello' })
})
test(`${upMethod} with no body - correctly replies`, t => {
t.plan(6)
sget({
const { stepIn, patience } = waitForCb({ steps: 2 })
fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
headers: { 'Content-Length': '0' }
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '')
}).then(async (response) => {
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
t.assert.strictEqual(await response.text(), '')
stepIn()
})
// Must use inject to make a request without a Content-Length header
@@ -201,91 +205,109 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
method: upMethod,
url: '/missing'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), '')
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload.toString(), '')
stepIn()
})
return patience
})
test(`${upMethod} returns 415 - incorrect media type if body is not json`, t => {
test(`${upMethod} returns 415 - incorrect media type if body is not json`, async (t) => {
t.plan(2)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: 'hello world'
}, (err, response, body) => {
t.error(err)
if (upMethod === 'OPTIONS') {
t.equal(response.statusCode, 200)
} else {
t.equal(response.statusCode, 415)
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
body: 'hello world',
headers: {
'Content-Type': undefined
}
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
})
if (loMethod === 'options') {
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', t => {
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', async (t) => {
t.plan(2)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: 'hello world',
headers: {
'Content-Type': 'text/xml'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
})
}
test(`${upMethod} returns 400 - Bad Request`, t => {
t.plan(4)
const isOptions = upMethod === 'OPTIONS'
t.plan(isOptions ? 2 : 4)
sget({
const { stepIn, patience } = waitForCb({ steps: isOptions ? 1 : 2 })
fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: 'hello world',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
stepIn()
})
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Content-Type': 'application/json',
'Content-Length': '0'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
})
if (!isOptions) {
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
headers: {
'Content-Type': 'application/json',
'Content-Length': '0'
}
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
stepIn()
})
}
return patience
})
test(`${upMethod} returns 413 - Payload Too Large`, t => {
t.plan(upMethod === 'OPTIONS' ? 4 : 6)
const isOptions = upMethod === 'OPTIONS'
t.plan(isOptions ? 3 : 5)
sget({
const { stepIn, patience } = waitForCb({ steps: isOptions ? 2 : 3 })
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: JSON.stringify({ w: 'w'.repeat(1024 * 1024 + 1) }),
headers: {
'Content-Type': 'application/json',
'Content-Length': 1024 * 1024 + 1
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
}).then((response) => {
t.assert.strictEqual(response.status, 413)
stepIn()
}).catch((err) => {
// Handle EPIPE error - server closed connection after sending 413
if (err.cause?.code === 'EPIPE' || err.message.includes('fetch failed')) {
t.assert.ok(true, 'Expected EPIPE error due to server closing connection on 413')
} else {
throw err
}
stepIn()
})
// Node errors for OPTIONS requests with a stream body and no Content-Length header
if (upMethod !== 'OPTIONS') {
if (!isOptions) {
let chunk = Buffer.alloc(1024 * 1024 + 1, 0)
const largeStream = new stream.Readable({
read () {
@@ -293,34 +315,46 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
chunk = null
}
})
sget({
fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
headers: { 'Content-Type': 'application/json' },
body: largeStream
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
body: largeStream,
duplex: 'half'
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 413)
stepIn()
}).catch((err) => {
// Handle EPIPE error - server closed connection after sending 413
if (err.cause?.code === 'EPIPE' || err.message.includes('fetch failed')) {
t.assert.ok(true, 'Expected EPIPE error due to server closing connection on 413')
} else {
throw err
}
stepIn()
})
}
sget({
fetch(`http://localhost:${fastify.server.address().port}/with-limit`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}/with-limit`,
headers: { 'Content-Type': 'application/json' },
body: {},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
body: JSON.stringify({})
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 413)
stepIn()
})
return patience
})
test(`${upMethod} should fail with empty body and application/json content-type`, t => {
if (upMethod === 'OPTIONS') return t.end()
if (upMethod === 'OPTIONS') return
t.plan(12)
const { stepIn, patience } = waitForCb({ steps: 5 })
fastify.inject({
method: `${upMethod}`,
url: '/',
@@ -328,8 +362,8 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
'Content-Type': 'application/json'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
@@ -337,20 +371,20 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
})
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
}
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
fastify.inject({
@@ -361,30 +395,31 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
},
payload: null
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
},
payload: null
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
body: null
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
fastify.inject({
@@ -395,31 +430,34 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
},
payload: undefined
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
},
payload: undefined
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
body: undefined
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
return patience
})
})
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,44 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const Fastify = require('../fastify')
const immediate = require('node:util').promisify(setImmediate)
t.test('onReady should be called in order', t => {
test('onReady should be called in order', (t, done) => {
t.plan(7)
const fastify = Fastify()
let order = 0
fastify.addHook('onReady', function (done) {
t.equal(order++, 0, 'called in root')
t.equal(this.pluginName, fastify.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 0, 'called in root')
t.assert.strictEqual(this.pluginName, fastify.pluginName, 'the this binding is the right instance')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', function (done) {
t.equal(order++, 1, 'called in childOne')
t.equal(this.pluginName, childOne.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 1, 'called in childOne')
t.assert.strictEqual(this.pluginName, childOne.pluginName, 'the this binding is the right instance')
done()
})
childOne.register(async (childTwo, o) => {
childTwo.addHook('onReady', async function () {
await immediate()
t.equal(order++, 2, 'called in childTwo')
t.equal(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 2, 'called in childTwo')
t.assert.strictEqual(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')
})
})
})
fastify.ready(err => t.error(err))
fastify.ready(err => {
t.assert.ifError(err)
done()
})
})
t.test('onReady should be called once', async (t) => {
test('onReady should be called once', async (t) => {
const app = Fastify()
let counter = 0
@@ -47,11 +50,11 @@ t.test('onReady should be called once', async (t) => {
const result = await Promise.race(promises)
t.strictSame(result, 1, 'Should resolve in order')
t.equal(counter, 1, 'Should call onReady only once')
t.assert.strictEqual(result, 1, 'Should resolve in order')
t.assert.strictEqual(counter, 1, 'Should call onReady only once')
})
t.test('async onReady should be called in order', async t => {
test('async onReady should be called in order', async t => {
t.plan(7)
const fastify = Fastify()
@@ -59,31 +62,31 @@ t.test('async onReady should be called in order', async t => {
fastify.addHook('onReady', async function () {
await immediate()
t.equal(order++, 0, 'called in root')
t.equal(this.pluginName, fastify.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 0, 'called in root')
t.assert.strictEqual(this.pluginName, fastify.pluginName, 'the this binding is the right instance')
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', async function () {
await immediate()
t.equal(order++, 1, 'called in childOne')
t.equal(this.pluginName, childOne.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 1, 'called in childOne')
t.assert.strictEqual(this.pluginName, childOne.pluginName, 'the this binding is the right instance')
})
childOne.register(async (childTwo, o) => {
childTwo.addHook('onReady', async function () {
await immediate()
t.equal(order++, 2, 'called in childTwo')
t.equal(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')
t.assert.strictEqual(order++, 2, 'called in childTwo')
t.assert.strictEqual(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')
})
})
})
await fastify.ready()
t.pass('ready')
t.assert.ok('ready')
})
t.test('mix ready and onReady', async t => {
test('mix ready and onReady', async t => {
t.plan(2)
const fastify = Fastify()
let order = 0
@@ -94,13 +97,13 @@ t.test('mix ready and onReady', async t => {
})
await fastify.ready()
t.equal(order, 1)
t.assert.strictEqual(order, 1)
await fastify.ready()
t.equal(order, 1, 'ready hooks execute once')
t.assert.strictEqual(order, 1, 'ready hooks execute once')
})
t.test('listen and onReady order', async t => {
test('listen and onReady order', async t => {
t.plan(9)
const fastify = Fastify()
@@ -127,19 +130,19 @@ t.test('listen and onReady order', async t => {
fastify.addHook('onReady', checkOrder.bind(null, 3))
await fastify.ready()
t.pass('trigger the onReady')
t.assert.ok('trigger the onReady')
await fastify.listen({ port: 0 })
t.pass('do not trigger the onReady')
t.assert.ok('do not trigger the onReady')
await fastify.close()
function checkOrder (shouldbe) {
t.equal(order, shouldbe)
t.assert.strictEqual(order, shouldbe)
order++
}
})
t.test('multiple ready calls', async t => {
test('multiple ready calls', async t => {
t.plan(11)
const fastify = Fastify()
@@ -154,7 +157,7 @@ t.test('multiple ready calls', async t => {
subinstance.addHook('onReady', checkOrder.bind(null, 7))
})
t.equal(order, 0, 'ready and hooks not triggered yet')
t.assert.strictEqual(order, 0, 'ready and hooks not triggered yet')
order++
})
@@ -163,139 +166,145 @@ t.test('multiple ready calls', async t => {
fastify.addHook('onReady', checkOrder.bind(null, 5))
await fastify.ready()
t.pass('trigger the onReady')
t.assert.ok('trigger the onReady')
await fastify.ready()
t.pass('do not trigger the onReady')
t.assert.ok('do not trigger the onReady')
await fastify.ready()
t.pass('do not trigger the onReady')
t.assert.ok('do not trigger the onReady')
function checkOrder (shouldbe) {
t.equal(order, shouldbe)
t.assert.strictEqual(order, shouldbe)
order++
}
})
t.test('onReady should manage error in sync', t => {
test('onReady should manage error in sync', (t, done) => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
t.assert.ok('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', function (done) {
t.pass('called in childOne')
t.assert.ok('called in childOne')
done(new Error('FAIL ON READY'))
})
childOne.register(async (childTwo, o) => {
childTwo.addHook('onReady', async function () {
t.fail('should not be called')
t.assert.fail('should not be called')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL ON READY')
t.assert.ok(err)
t.assert.strictEqual(err.message, 'FAIL ON READY')
done()
})
})
t.test('onReady should manage error in async', t => {
test('onReady should manage error in async', (t, done) => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
t.assert.ok('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', async function () {
t.pass('called in childOne')
t.assert.ok('called in childOne')
throw new Error('FAIL ON READY')
})
childOne.register(async (childTwo, o) => {
childTwo.addHook('onReady', async function () {
t.fail('should not be called')
t.assert.fail('should not be called')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL ON READY')
t.assert.ok(err)
t.assert.strictEqual(err.message, 'FAIL ON READY')
done()
})
})
t.test('onReady should manage sync error', t => {
test('onReady should manage sync error', (t, done) => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
t.assert.ok('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', function (done) {
t.pass('called in childOne')
t.assert.ok('called in childOne')
throw new Error('FAIL UNWANTED SYNC EXCEPTION')
})
childOne.register(async (childTwo, o) => {
childTwo.addHook('onReady', async function () {
t.fail('should not be called')
t.assert.fail('should not be called')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL UNWANTED SYNC EXCEPTION')
t.assert.ok(err)
t.assert.strictEqual(err.message, 'FAIL UNWANTED SYNC EXCEPTION')
done()
})
})
t.test('onReady can not add decorators or application hooks', t => {
test('onReady can not add decorators or application hooks', (t, done) => {
t.plan(3)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
t.assert.ok('called in root')
fastify.decorate('test', () => {})
fastify.addHook('onReady', async function () {
t.fail('it will be not called')
t.assert.fail('it will be not called')
})
done()
})
fastify.addHook('onReady', function (done) {
t.ok(this.hasDecorator('test'))
t.assert.ok(this.hasDecorator('test'))
done()
})
fastify.ready(err => { t.error(err) })
fastify.ready(err => {
t.assert.ifError(err)
done()
})
})
t.test('onReady cannot add lifecycle hooks', t => {
test('onReady cannot add lifecycle hooks', (t, done) => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
t.assert.ok('called in root')
try {
fastify.addHook('onRequest', (request, reply, done) => {})
} catch (error) {
t.ok(error)
t.equal(error.message, 'Root plugin has already booted')
t.assert.ok(error)
t.assert.strictEqual(error.message, 'Root plugin has already booted')
// TODO: look where the error pops up
t.equal(error.code, 'AVV_ERR_ROOT_PLG_BOOTED')
t.assert.strictEqual(error.code, 'AVV_ERR_ROOT_PLG_BOOTED')
done(error)
}
})
@@ -303,93 +312,102 @@ t.test('onReady cannot add lifecycle hooks', t => {
fastify.addHook('onRequest', (request, reply, done) => {})
fastify.get('/', async () => 'hello')
fastify.ready((err) => { t.ok(err) })
fastify.ready((err) => {
t.assert.ok(err)
done()
})
})
t.test('onReady throw loading error', t => {
test('onReady throw loading error', t => {
t.plan(2)
const fastify = Fastify()
try {
fastify.addHook('onReady', async function (done) {})
} catch (e) {
t.ok(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
t.ok(e.message === 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
t.assert.ok(e.message === 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
}
})
t.test('onReady does not call done', t => {
test('onReady does not call done', (t, done) => {
t.plan(6)
const fastify = Fastify({ pluginTimeout: 500 })
fastify.addHook('onReady', function (done) {
t.pass('called in root')
fastify.addHook('onReady', function someHookName (done) {
t.assert.ok('called in root')
// done() // don't call done to test timeout
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, "A callback for 'onReady' hook timed out. You may have forgotten to call 'done' function or to resolve a Promise")
t.equal(err.code, 'FST_ERR_HOOK_TIMEOUT')
t.ok(err.cause)
t.equal(err.cause.code, 'AVV_ERR_READY_TIMEOUT')
t.assert.ok(err)
t.assert.strictEqual(err.message, 'A callback for \'onReady\' hook "someHookName" timed out. You may have forgotten to call \'done\' function or to resolve a Promise')
t.assert.strictEqual(err.code, 'FST_ERR_HOOK_TIMEOUT')
t.assert.ok(err.cause)
t.assert.strictEqual(err.cause.code, 'AVV_ERR_READY_TIMEOUT')
done()
})
})
t.test('onReady execution order', t => {
test('onReady execution order', (t, done) => {
t.plan(3)
const fastify = Fastify({ })
let i = 0
fastify.ready(() => { i++; t.equal(i, 1) })
fastify.ready(() => { i++; t.equal(i, 2) })
fastify.ready(() => { i++; t.equal(i, 3) })
fastify.ready(() => { i++; t.assert.strictEqual(i, 1) })
fastify.ready(() => { i++; t.assert.strictEqual(i, 2) })
fastify.ready(() => {
i++
t.assert.strictEqual(i, 3)
done()
})
})
t.test('ready return the server with callback', t => {
test('ready return the server with callback', (t, done) => {
t.plan(2)
const fastify = Fastify()
fastify.ready((err, instance) => {
t.error(err)
t.same(instance, fastify)
t.assert.ifError(err)
t.assert.deepStrictEqual(instance, fastify)
done()
})
})
t.test('ready return the server with Promise', t => {
test('ready return the server with Promise', async t => {
t.plan(1)
const fastify = Fastify()
fastify.ready()
.then(instance => { t.same(instance, fastify) })
.catch(err => { t.fail(err) })
await fastify.ready()
.then(instance => { t.assert.deepStrictEqual(instance, fastify) })
.catch(err => { t.assert.fail(err) })
})
t.test('ready return registered', t => {
test('ready return registered', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register((one, opts, done) => {
one.ready().then(itself => { t.same(itself, one) })
one.ready().then(itself => { t.assert.deepStrictEqual(itself, one) })
done()
})
fastify.register((two, opts, done) => {
two.ready().then(itself => { t.same(itself, two) })
two.ready().then(itself => { t.assert.deepStrictEqual(itself, two) })
two.register((twoDotOne, opts, done) => {
twoDotOne.ready().then(itself => { t.same(itself, twoDotOne) })
twoDotOne.ready().then(itself => { t.assert.deepStrictEqual(itself, twoDotOne) })
done()
})
done()
})
fastify.ready()
.then(instance => { t.same(instance, fastify) })
.catch(err => { t.fail(err) })
await fastify.ready()
.then(instance => { t.assert.deepStrictEqual(instance, fastify) })
.catch(err => { t.assert.fail(err) })
})
t.test('do not crash with error in follow up onReady hook', async t => {
test('do not crash with error in follow up onReady hook', async t => {
const fastify = Fastify()
fastify.addHook('onReady', async function () {
@@ -399,5 +417,5 @@ t.test('do not crash with error in follow up onReady hook', async t => {
throw new Error('kaboom')
})
await t.rejects(fastify.ready())
await t.assert.rejects(fastify.ready())
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('COPY')
test('can be created - copy', async t => {
t.plan(3)
t.after(() => fastify.close())
try {
fastify.route({
method: 'COPY',
url: '*',
handler: function (req, reply) {
reply.code(204).send()
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(`${fastifyServer}/test.txt`, {
method: 'COPY',
headers: {
Destination: '/test2.txt'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 204)
})

View File

@@ -0,0 +1,114 @@
'use strict'
const http = require('node:http')
const { test } = require('node:test')
const Fastify = require('../../fastify')
function addEcho (fastify, method) {
fastify.route({
method,
url: '/',
handler: function (req, reply) {
reply.send(req.body)
}
})
}
test('missing method from http client', (t, done) => {
t.plan(2)
const fastify = Fastify()
fastify.listen({ port: 3000 }, (err) => {
t.assert.ifError(err)
const port = fastify.server.address().port
const req = http.request({
port,
method: 'REBIND',
path: '/'
}, (res) => {
t.assert.strictEqual(res.statusCode, 404)
fastify.close()
done()
})
req.end()
})
})
test('addHttpMethod increase the supported HTTP methods supported', (t, done) => {
t.plan(8)
const app = Fastify()
t.assert.throws(() => { addEcho(app, 'REBIND') }, /REBIND method is not supported./)
t.assert.ok(!app.supportedMethods.includes('REBIND'))
t.assert.ok(!app.rebind)
app.addHttpMethod('REBIND')
t.assert.doesNotThrow(() => { addEcho(app, 'REBIND') }, 'REBIND method is supported.')
t.assert.ok(app.supportedMethods.includes('REBIND'))
t.assert.ok(app.rebind)
app.rebind('/foo', () => 'hello')
app.inject({
method: 'REBIND',
url: '/foo'
}, (err, response) => {
t.assert.ifError(err)
t.assert.strictEqual(response.payload, 'hello')
done()
})
})
test('addHttpMethod adds a new custom method without body', t => {
t.plan(3)
const app = Fastify()
t.assert.throws(() => { addEcho(app, 'REBIND') }, /REBIND method is not supported./)
app.addHttpMethod('REBIND')
t.assert.doesNotThrow(() => { addEcho(app, 'REBIND') }, 'REBIND method is supported.')
t.assert.throws(() => {
app.route({
url: '/',
method: 'REBIND',
schema: {
body: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
},
handler: function (req, reply) {
reply.send(req.body)
}
})
}, /Body validation schema for REBIND:\/ route is not supported!/)
})
test('addHttpMethod adds a new custom method with body', (t, done) => {
t.plan(3)
const app = Fastify()
app.addHttpMethod('REBIND', { hasBody: true })
t.assert.doesNotThrow(() => { addEcho(app, 'REBIND') }, 'REBIND method is supported.')
app.inject({
method: 'REBIND',
url: '/',
payload: { hello: 'world' }
}, (err, response) => {
t.assert.ifError(err)
t.assert.deepStrictEqual(response.json(), { hello: 'world' })
done()
})
})
test('addHttpMethod rejects fake http method', t => {
t.plan(1)
const fastify = Fastify()
t.assert.throws(() => { fastify.addHttpMethod('FOOO') }, /Provided method is invalid!/)
})

View File

@@ -0,0 +1,412 @@
'use strict'
const { test } = require('node:test')
const { Client } = require('undici')
const fastify = require('../../fastify')()
const schema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
const nullSchema = {
schema: {
response: {
'2xx': {
type: 'null'
}
}
}
}
const numberSchema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'number'
}
}
}
}
}
}
const querySchema = {
schema: {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
}
const paramsSchema = {
schema: {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
}
const headersSchema = {
schema: {
headers: {
type: 'object',
properties: {
'x-test': {
type: 'number'
},
'Y-Test': {
type: 'number'
}
}
}
}
}
test('shorthand - get', t => {
t.plan(1)
try {
fastify.get('/', schema, function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - get (return null)', t => {
t.plan(1)
try {
fastify.get('/null', nullSchema, function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - get params', t => {
t.plan(1)
try {
fastify.get('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.code(200).send(req.params)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - get, querystring schema', t => {
t.plan(1)
try {
fastify.get('/query', querySchema, function (req, reply) {
reply.code(200).send(req.query)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - get, headers schema', t => {
t.plan(1)
try {
fastify.get('/headers', headersSchema, function (req, reply) {
reply.code(200).send(req.headers)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('missing schema - get', t => {
t.plan(1)
try {
fastify.get('/missing', function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('custom serializer - get', t => {
t.plan(1)
function customSerializer (data) {
return JSON.stringify(data)
}
try {
fastify.get('/custom-serializer', numberSchema, function (req, reply) {
reply.code(200).serializer(customSerializer).send({ hello: 'world' })
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('empty response', t => {
t.plan(1)
try {
fastify.get('/empty', function (req, reply) {
reply.code(200).send()
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('send a falsy boolean', t => {
t.plan(1)
try {
fastify.get('/boolean', function (req, reply) {
reply.code(200).send(false)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - get, set port', t => {
t.plan(1)
try {
fastify.get('/port', headersSchema, function (req, reply) {
reply.code(200).send({ port: req.port })
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('get test', async t => {
t.after(() => { fastify.close() })
await fastify.listen({ port: 0 })
await t.test('shorthand - request get', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port, {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
await t.test('shorthand - request get params schema', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/params/world/123', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { foo: 'world', test: 123 })
})
await t.test('shorthand - request get params schema error', async t => {
t.plan(3)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/params/world/string', {
method: 'GET'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'params/test must be integer',
statusCode: 400
})
})
await t.test('shorthand - request get headers schema', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/headers', {
method: 'GET',
headers: {
'x-test': '1',
'Y-Test': '3'
}
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.json()
t.assert.strictEqual(body['x-test'], 1)
t.assert.strictEqual(body['y-test'], 3)
})
await t.test('shorthand - request get headers schema error', async t => {
t.plan(3)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/headers', {
method: 'GET',
headers: {
'x-test': 'abc'
}
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'headers/x-test must be number',
statusCode: 400
})
})
await t.test('shorthand - request get querystring schema', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/query?hello=123', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 123 })
})
await t.test('shorthand - request get querystring schema error', async t => {
t.plan(3)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/query?hello=world', {
method: 'GET'
})
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
const body = await response.text()
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'querystring/hello must be integer',
statusCode: 400
})
})
await t.test('shorthand - request get missing schema', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
await t.test('shorthand - custom serializer', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/custom-serializer', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
await t.test('shorthand - empty response', async t => {
t.plan(4)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/empty', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.strictEqual(response.headers.get('content-length'), '0')
t.assert.deepStrictEqual(body.toString(), '')
})
await t.test('shorthand - send a falsy boolean', async t => {
t.plan(3)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/boolean', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(body.toString(), 'false')
})
await t.test('shorthand - send null value', async t => {
t.plan(3)
const response = await fetch('http://localhost:' + fastify.server.address().port + '/null', {
method: 'GET'
})
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
const body = await response.text()
t.assert.deepStrictEqual(body.toString(), 'null')
})
await t.test('shorthand - request get headers - test fall back port', async t => {
t.plan(2)
const instance = new Client('http://localhost:' + fastify.server.address().port)
const response = await instance.request({
path: '/port',
method: 'GET',
headers: {
host: 'example.com'
}
})
t.assert.strictEqual(response.statusCode, 200)
const body = JSON.parse(await response.body.text())
t.assert.strictEqual(body.port, null)
})
})

View File

@@ -0,0 +1,263 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../fastify')()
const schema = {
schema: {
response: {
'2xx': {
type: 'null'
}
}
}
}
const querySchema = {
schema: {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
}
const paramsSchema = {
schema: {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
}
test('shorthand - head', t => {
t.plan(1)
try {
fastify.head('/', schema, function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - custom head', t => {
t.plan(1)
try {
fastify.head('/proxy/*', function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
})
fastify.get('/proxy/*', function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - custom head with constraints', t => {
t.plan(1)
try {
fastify.head('/proxy/*', { constraints: { version: '1.0.0' } }, function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
})
fastify.get('/proxy/*', { constraints: { version: '1.0.0' } }, function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - should not reset a head route', t => {
t.plan(1)
try {
fastify.get('/query1', function (req, reply) {
reply.code(200).send(null)
})
fastify.put('/query1', function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - should set get and head route in the same api call', t => {
t.plan(1)
try {
fastify.route({
method: ['HEAD', 'GET'],
url: '/query4',
handler: function (req, reply) {
reply.headers({ 'x-foo': 'bar' })
reply.code(200).send(null)
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - head params', t => {
t.plan(1)
try {
fastify.head('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('shorthand - head, querystring schema', t => {
t.plan(1)
try {
fastify.head('/query', querySchema, function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('missing schema - head', t => {
t.plan(1)
try {
fastify.head('/missing', function (req, reply) {
reply.code(200).send(null)
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('head test', async t => {
t.after(() => { fastify.close() })
const fastifyServer = await fastify.listen({ port: 0 })
await t.test('shorthand - request head', async t => {
t.plan(2)
const result = await fetch(fastifyServer, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - request head params schema', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/params/world/123`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - request head params schema error', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/params/world/string`, {
method: 'HEAD'
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 400)
})
await t.test('shorthand - request head querystring schema', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/query?hello=123`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - request head querystring schema error', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/query?hello=world`, {
method: 'HEAD'
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 400)
})
await t.test('shorthand - request head missing schema', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/missing`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - request head custom head', async t => {
t.plan(3)
const result = await fetch(`${fastifyServer}/proxy/test`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.headers.get('x-foo'), 'bar')
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - request head custom head with constraints', async t => {
t.plan(3)
const result = await fetch(`${fastifyServer}/proxy/test`, {
method: 'HEAD',
headers: {
version: '1.0.0'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.headers.get('x-foo'), 'bar')
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - should not reset a head route', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/query1`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
})
await t.test('shorthand - should set get and head route in the same api call', async t => {
t.plan(3)
const result = await fetch(`${fastifyServer}/query4`, {
method: 'HEAD'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.headers.get('x-foo'), 'bar')
t.assert.strictEqual(result.status, 200)
})
})

View File

@@ -1,9 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('LOCK', { hasBody: true })
const bodySample = `<?xml version="1.0" encoding="utf-8" ?>
<D:lockinfo xmlns:D='DAV:'>
@@ -50,57 +49,60 @@ test('can be created - lock', t => {
)
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => {
test('lock test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => {
fastify.close()
})
// the body test uses a text/plain content type instead of application/xml because it requires
// a specific content type parser
test('request with body - lock', t => {
await t.test('request with body - lock', async (t) => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'LOCK',
headers: { 'content-type': 'text/plain' },
body: bodySample,
method: 'LOCK'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
body: bodySample
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with body and no content type (415 error) - lock', t => {
await t.test('request with body and no content type (415 error) - lock', async (t) => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'LOCK',
body: bodySample,
method: 'LOCK'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
t.equal(response.headers['content-length'], '' + body.length)
headers: { 'content-type': undefined }
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request without body - lock', t => {
await t.test('request without body - lock', async (t) => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
headers: { 'content-type': 'text/plain' },
method: 'LOCK'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'LOCK',
headers: { 'content-type': 'text/plain' }
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
})

View File

@@ -1,9 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('../fastify')()
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('MKCALENDAR', { hasBody: true })
const bodySample = `<?xml version="1.0" encoding="UTF-8"?>
<B:mkcalendar xmlns:B="urn:ietf:params:xml:ns:caldav">
@@ -71,95 +70,74 @@ test('can be created - mkcalendar', (t) => {
</D:multistatus>`)
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => {
test('mkcalendar test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => {
fastify.close()
})
test('request - mkcalendar', (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/`,
method: 'MKCALENDAR'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
await t.test('request - mkcalendar', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/`, {
method: 'MKCALENDAR'
})
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with other path - mkcalendar', (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
method: 'MKCALENDAR'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
await t.test('request with other path - mkcalendar', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/test`, {
method: 'MKCALENDAR'
})
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
// the body test uses a text/plain content type instead of application/xml because it requires
// a specific content type parser
test('request with body - mkcalendar', (t) => {
await t.test('request with body - mkcalendar', async t => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
headers: { 'content-type': 'text/plain' },
body: bodySample,
method: 'MKCALENDAR'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`${fastifyServer}/test`, {
method: 'MKCALENDAR',
headers: { 'content-type': 'text/plain' },
body: bodySample
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with body and no content type (415 error) - mkcalendar', (t) => {
await t.test('request with body and no content type (415 error) - mkcalendar', async t => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
body: bodySample,
method: 'MKCALENDAR'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`${fastifyServer}/test`, {
method: 'MKCALENDAR',
body: bodySample,
headers: { 'content-type': undefined }
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request without body - mkcalendar', (t) => {
await t.test('request without body - mkcalendar', async t => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
method: 'MKCALENDAR'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`${fastifyServer}/test`, {
method: 'MKCALENDAR'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
})

View File

@@ -0,0 +1,35 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../')()
fastify.addHttpMethod('MKCOL')
test('can be created - mkcol', t => {
t.plan(1)
try {
fastify.route({
method: 'MKCOL',
url: '*',
handler: function (req, reply) {
reply.code(201).send()
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('mkcol test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
await t.test('request - mkcol', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/test/`, {
method: 'MKCOL'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 201)
})
})

View File

@@ -0,0 +1,42 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../')()
fastify.addHttpMethod('MOVE')
test('shorthand - move', t => {
t.plan(1)
try {
fastify.route({
method: 'MOVE',
url: '*',
handler: function (req, reply) {
const destination = req.headers.destination
reply.code(201)
.header('location', destination)
.send()
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('move test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
await t.test('request - move', async t => {
t.plan(3)
const result = await fetch(`${fastifyServer}/test.txt`, {
method: 'MOVE',
headers: {
Destination: '/test2.txt'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 201)
t.assert.strictEqual(result.headers.get('location'), '/test2.txt')
})
})

View File

@@ -1,9 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
const { test } = require('node:test')
const fastify = require('../../')()
fastify.addHttpMethod('PROPFIND', { hasBody: true })
const bodySample = `<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
@@ -61,80 +60,77 @@ test('can be created - propfind', t => {
)
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => {
test('propfind test', async t => {
await fastify.listen({ port: 0 })
t.after(() => {
fastify.close()
})
test('request - propfind', t => {
await t.test('request - propfind', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/`,
const result = await fetch(`http://localhost:${fastify.server.address().port}/`, {
method: 'PROPFIND'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with other path - propfind', t => {
await t.test('request with other path - propfind', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test`,
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'PROPFIND'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
// the body test uses a text/plain content type instead of application/xml because it requires
// a specific content type parser
test('request with body - propfind', t => {
await t.test('request with body - propfind', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test`,
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'PROPFIND',
headers: { 'content-type': 'text/plain' },
body: bodySample,
method: 'PROPFIND'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
body: bodySample
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with body and no content type (415 error) - propfind', t => {
await t.test('request with body and no content type (415 error) - propfind', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test`,
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'PROPFIND',
body: bodySample,
method: 'PROPFIND'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
t.equal(response.headers['content-length'], '' + body.length)
headers: { 'content-type': '' }
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request without body - propfind', t => {
await t.test('request without body - propfind', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test`,
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'PROPFIND'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
})

View File

@@ -1,9 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()
const { test } = require('node:test')
const fastify = require('../../')()
fastify.addHttpMethod('PROPPATCH', { hasBody: true })
const bodySample = `<?xml version="1.0" encoding="utf-8" ?>
<D:propertyupdate xmlns:D="DAV:"
@@ -55,54 +54,52 @@ test('shorthand - proppatch', t => {
)
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('proppatch test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
// the body test uses a text/plain content type instead of application/xml because it requires
// a specific content type parser
test('request with body - proppatch', t => {
await t.test('request with body - proppatch', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'PROPPATCH',
headers: { 'content-type': 'text/plain' },
body: bodySample,
method: 'PROPPATCH'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
body: bodySample
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request with body and no content type (415 error) - proppatch', t => {
await t.test('request with body and no content type (415 error) - proppatch', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'PROPPATCH',
body: bodySample,
method: 'PROPPATCH'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
t.equal(response.headers['content-length'], '' + body.length)
headers: { 'content-type': undefined }
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
test('request without body - proppatch', t => {
await t.test('request without body - proppatch', async t => {
t.plan(3)
sget({
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'PROPPATCH'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
const body = await result.text()
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
})
})

View File

@@ -1,9 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('../fastify')()
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('REPORT', { hasBody: true })
const bodySample = `<?xml version="1.0" encoding="UTF-8"?>
<B:calendar-query xmlns:B="urn:ietf:params:xml:ns:caldav">
@@ -67,95 +66,77 @@ test('can be created - report', (t) => {
</D:multistatus>`)
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => {
test('report test', async t => {
await fastify.listen({ port: 0 })
t.after(() => {
fastify.close()
})
test('request - report', (t) => {
await t.test('request - report', async (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/`,
method: 'REPORT'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`http://localhost:${fastify.server.address().port}/`, {
method: 'REPORT'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
t.assert.strictEqual(result.headers.get('content-length'), '' + (await result.text()).length)
})
test('request with other path - report', (t) => {
await t.test('request with other path - report', async (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
method: 'REPORT'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'REPORT'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
t.assert.strictEqual(result.headers.get('content-length'), '' + (await result.text()).length)
})
// the body test uses a text/plain content type instead of application/xml because it requires
// a specific content type parser
test('request with body - report', (t) => {
await t.test('request with body - report', async (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
headers: { 'content-type': 'text/plain' },
body: bodySample,
method: 'REPORT'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'REPORT',
headers: { 'content-type': 'text/plain' },
body: bodySample
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
t.assert.strictEqual(result.headers.get('content-length'), '' + (await result.text()).length)
})
test('request with body and no content type (415 error) - report', (t) => {
await t.test('request with body and no content type (415 error) - report', async (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
body: bodySample,
method: 'REPORT'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'REPORT',
body: bodySample,
headers: { 'content-type': '' }
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
t.assert.strictEqual(result.headers.get('content-length'), '' + (await result.text()).length)
})
test('request without body - report', (t) => {
await t.test('request without body - report', async (t) => {
t.plan(3)
sget(
{
url: `http://localhost:${fastify.server.address().port}/test`,
method: 'REPORT'
},
(err, response, body) => {
t.error(err)
t.equal(response.statusCode, 207)
t.equal(response.headers['content-length'], '' + body.length)
}
)
const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
method: 'REPORT'
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 207)
t.assert.strictEqual(result.headers.get('content-length'), '' + (await result.text()).length)
})
})

View File

@@ -0,0 +1,233 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('SEARCH', { hasBody: true })
const schema = {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
const querySchema = {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
const paramsSchema = {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
const bodySchema = {
body: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
test('search', t => {
t.plan(1)
try {
fastify.route({
method: 'SEARCH',
url: '/',
schema,
handler: function (request, reply) {
reply.code(200).send({ hello: 'world' })
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('search, params schema', t => {
t.plan(1)
try {
fastify.route({
method: 'SEARCH',
url: '/params/:foo/:test',
schema: paramsSchema,
handler: function (request, reply) {
reply.code(200).send(request.params)
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('search, querystring schema', t => {
t.plan(1)
try {
fastify.route({
method: 'SEARCH',
url: '/query',
schema: querySchema,
handler: function (request, reply) {
reply.code(200).send(request.query)
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('search, body schema', t => {
t.plan(1)
try {
fastify.route({
method: 'SEARCH',
url: '/body',
schema: bodySchema,
handler: function (request, reply) {
reply.code(200).send(request.body)
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('search test', async t => {
await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
const url = `http://localhost:${fastify.server.address().port}`
await t.test('request - search', async t => {
t.plan(4)
const result = await fetch(url, {
method: 'SEARCH'
})
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
})
await t.test('request search params schema', async t => {
t.plan(4)
const result = await fetch(`${url}/params/world/123`, {
method: 'SEARCH'
})
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { foo: 'world', test: 123 })
})
await t.test('request search params schema error', async t => {
t.plan(3)
const result = await fetch(`${url}/params/world/string`, {
method: 'SEARCH'
})
const body = await result.text()
t.assert.strictEqual(result.status, 400)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'params/test must be integer',
statusCode: 400
})
})
await t.test('request search querystring schema', async t => {
t.plan(4)
const result = await fetch(`${url}/query?hello=123`, {
method: 'SEARCH'
})
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), { hello: 123 })
})
await t.test('request search querystring schema error', async t => {
t.plan(3)
const result = await fetch(`${url}/query?hello=world`, {
method: 'SEARCH'
})
const body = await result.text()
t.assert.strictEqual(result.status, 400)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'querystring/hello must be integer',
statusCode: 400
})
})
await t.test('request search body schema', async t => {
t.plan(4)
const replyBody = { foo: 'bar', test: 5 }
const result = await fetch(`${url}/body`, {
method: 'SEARCH',
body: JSON.stringify(replyBody),
headers: { 'content-type': 'application/json' }
})
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), replyBody)
})
await t.test('request search body schema error', async t => {
t.plan(4)
const result = await fetch(`${url}/body`, {
method: 'SEARCH',
body: JSON.stringify({ foo: 'bar', test: 'test' }),
headers: { 'content-type': 'application/json' }
})
const body = await result.text()
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 400)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), {
error: 'Bad Request',
code: 'FST_ERR_VALIDATION',
message: 'body/test must be integer',
statusCode: 400
})
})
})

View File

@@ -1,8 +1,8 @@
'use strict'
const t = require('tap')
const test = t.test
const fastify = require('..')()
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('TRACE')
test('shorthand - trace', t => {
t.plan(1)
@@ -14,8 +14,8 @@ test('shorthand - trace', t => {
reply.code(200).send('TRACE OK')
}
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})

View File

@@ -0,0 +1,38 @@
'use strict'
const { test } = require('node:test')
const fastify = require('../../fastify')()
fastify.addHttpMethod('UNLOCK')
test('can be created - unlock', t => {
t.plan(1)
try {
fastify.route({
method: 'UNLOCK',
url: '*',
handler: function (req, reply) {
reply.code(204).send()
}
})
t.assert.ok(true)
} catch (e) {
t.assert.fail()
}
})
test('unlock test', async t => {
const fastifyServer = await fastify.listen({ port: 0 })
t.after(() => { fastify.close() })
await t.test('request - unlock', async t => {
t.plan(2)
const result = await fetch(`${fastifyServer}/test/a.txt`, {
method: 'UNLOCK',
headers: {
'Lock-Token': 'urn:uuid:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 204)
})
})

View File

@@ -1,6 +1,6 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const Fastify = require('../..')
const http2 = require('node:http2')
const { promisify } = require('node:util')
@@ -8,103 +8,114 @@ const connect = promisify(http2.connect)
const { once } = require('node:events')
const { buildCertificate } = require('../build-certificate')
const { getServerUrl } = require('../helper')
const { kHttp2ServerSessions } = require('../../lib/symbols')
t.before(buildCertificate)
test.before(buildCertificate)
t.test('http/2 request while fastify closing', t => {
let fastify
try {
fastify = Fastify({
http2: true
})
t.pass('http2 successfully loaded')
} catch (e) {
t.fail('http2 loading failed', e)
}
const isNode24OrGreater = Number(process.versions.node.split('.')[0]) >= 24
test('http/2 request while fastify closing Node <24', { skip: isNode24OrGreater }, (t, done) => {
const fastify = Fastify({
http2: true
})
t.assert.ok('http2 successfully loaded')
fastify.get('/', () => Promise.resolve({}))
t.after(() => { fastify.close() })
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.assert.ifError(err)
t.test('return 200', t => {
const url = getServerUrl(fastify)
const session = http2.connect(url, function () {
this.request({
':method': 'GET',
':path': '/'
}).on('response', headers => {
t.equal(headers[':status'], 503)
t.end()
this.destroy()
}).on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
})
fastify.close()
const url = getServerUrl(fastify)
const session = http2.connect(url, function () {
this.request({
':method': 'GET',
':path': '/'
}).on('response', headers => {
t.assert.strictEqual(headers[':status'], 503)
done()
this.destroy()
}).on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
})
session.on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
t.end()
done()
})
fastify.close()
})
t.end()
})
})
t.test('http/2 request while fastify closing - return503OnClosing: false', t => {
let fastify
try {
fastify = Fastify({
http2: true,
return503OnClosing: false
})
t.pass('http2 successfully loaded')
} catch (e) {
t.fail('http2 loading failed', e)
}
test('http/2 request while fastify closing Node >=24', { skip: !isNode24OrGreater }, (t, done) => {
const fastify = Fastify({
http2: true
})
t.assert.ok('http2 successfully loaded')
fastify.get('/', () => Promise.resolve({}))
t.after(() => { fastify.close() })
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.assert.ifError(err)
t.test('return 200', t => {
const url = getServerUrl(fastify)
const session = http2.connect(url, function () {
this.request({
':method': 'GET',
':path': '/'
}).on('response', headers => {
t.equal(headers[':status'], 200)
t.end()
this.destroy()
}).on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
})
fastify.close()
})
const url = getServerUrl(fastify)
const session = http2.connect(url, function () {
session.on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
t.end()
})
session.on('close', () => {
done()
})
fastify.close()
})
t.end()
})
})
t.test('http/2 closes successfully with async await', async t => {
test('http/2 request while fastify closing - return503OnClosing: false', { skip: isNode24OrGreater }, (t, done) => {
const fastify = Fastify({
http2: true,
return503OnClosing: false
})
t.after(() => { fastify.close() })
fastify.get('/', () => Promise.resolve({}))
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
const url = getServerUrl(fastify)
const session = http2.connect(url, function () {
this.request({
':method': 'GET',
':path': '/'
}).on('response', headers => {
t.assert.strictEqual(headers[':status'], 200)
done()
this.destroy()
}).on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
})
fastify.close()
})
session.on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
done()
})
})
})
test('http/2 closes successfully with async await', async t => {
const fastify = Fastify({
http2SessionTimeout: 100,
http2: true
@@ -119,7 +130,7 @@ t.test('http/2 closes successfully with async await', async t => {
await fastify.close()
})
t.test('https/2 closes successfully with async await', async t => {
test('https/2 closes successfully with async await', async t => {
const fastify = Fastify({
http2SessionTimeout: 100,
http2: true,
@@ -138,7 +149,7 @@ t.test('https/2 closes successfully with async await', async t => {
await fastify.close()
})
t.test('http/2 server side session emits a timeout event', async t => {
test('http/2 server side session emits a timeout event', async t => {
let _resolve
const p = new Promise((resolve) => { _resolve = resolve })
@@ -162,7 +173,7 @@ t.test('http/2 server side session emits a timeout event', async t => {
}).end()
const [headers] = await once(req, 'response')
t.equal(headers[':status'], 200)
t.assert.strictEqual(headers[':status'], 200)
req.resume()
// An error might or might not happen, as it's OS dependent.
@@ -170,3 +181,90 @@ t.test('http/2 server side session emits a timeout event', async t => {
await p
await fastify.close()
})
test('http/2 sessions closed after closing server', async t => {
t.plan(1)
const fastify = Fastify({
http2: true,
http2SessionTimeout: 100
})
await fastify.listen()
const url = getServerUrl(fastify)
const waitSessionConnect = once(fastify.server, 'session')
const session = http2.connect(url)
await once(session, 'connect')
await waitSessionConnect
const waitSessionClosed = once(session, 'close')
await fastify.close()
await waitSessionClosed
t.assert.strictEqual(session.closed, true)
})
test('http/2 sessions should be closed when setting forceClosedConnections to true', async t => {
t.plan(2)
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
fastify.get('/', () => 'hello world')
await fastify.listen()
const client = await connect(getServerUrl(fastify))
const req = client.request({
[http2.HTTP2_HEADER_PATH]: '/',
[http2.HTTP2_HEADER_METHOD]: 'GET'
})
await once(req, 'response')
fastify.close()
const r2 = client.request({
[http2.HTTP2_HEADER_PATH]: '/',
[http2.TTP2_HEADER_METHOD]: 'GET'
})
r2.on('error', (err) => {
t.assert.strictEqual(err.toString(), 'Error [ERR_HTTP2_STREAM_ERROR]: Stream closed with error code NGHTTP2_REFUSED_STREAM')
})
await once(r2, 'error')
r2.end()
t.assert.strictEqual(client.closed, true)
client.destroy()
})
test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on goaway', async t => {
t.plan(2)
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
await fastify.listen()
const waitSession = once(fastify.server, 'session')
const client = http2.connect(getServerUrl(fastify))
const [session] = await waitSession
const waitGoaway = once(session, 'goaway')
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
client.goaway()
await waitGoaway
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
client.destroy()
await fastify.close()
})
test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on frameError', async t => {
t.plan(2)
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
await fastify.listen()
const waitSession = once(fastify.server, 'session')
const client = http2.connect(getServerUrl(fastify))
const [session] = await waitSession
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
session.emit('frameError', 0, 0, 0)
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
client.destroy()
await fastify.close()
})
test('http/2 sessions should not be removed from server[kHttp2ServerSessions] from Set if stream id passed on frameError', async t => {
t.plan(2)
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
await fastify.listen()
const waitSession = once(fastify.server, 'session')
const client = http2.connect(getServerUrl(fastify))
const [session] = await waitSession
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
session.emit('frameError', 0, 0, 1)
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
client.destroy()
await fastify.close()
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
@@ -9,9 +8,9 @@ const alpha = { res: 'alpha' }
const beta = { res: 'beta' }
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
test.before(buildCertificate)
test('A route supports host constraints under http2 protocol and secure connection', (t) => {
test('A route supports host constraints under http2 protocol and secure connection', async (t) => {
t.plan(5)
let fastify
@@ -23,9 +22,9 @@ test('A route supports host constraints under http2 protocol and secure connecti
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
t.assert.ok(true, 'Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
t.assert.fail('Key/cert loading failed')
}
const constrain = 'fastify.dev'
@@ -45,47 +44,66 @@ test('A route supports host constraints under http2 protocol and secure connecti
reply.code(200).send(beta)
}
})
fastify.route({
method: 'GET',
url: '/hostname_port',
constraints: { host: constrain },
handler: function (req, reply) {
reply.code(200).send({ ...beta, hostname: req.hostname })
}
})
t.after(() => { fastify.close() })
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
await fastify.listen({ port: 0 })
t.test('https get request - no constrain', async (t) => {
t.plan(3)
await t.test('https get request - no constrain', async (t) => {
t.plan(3)
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(alpha).length)
t.same(JSON.parse(res.body), alpha)
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(alpha).length)
t.assert.deepStrictEqual(JSON.parse(res.body), alpha)
})
await t.test('https get request - constrain', async (t) => {
t.plan(3)
const url = `https://localhost:${fastify.server.address().port}/beta`
const res = await h2url.concat({
url,
headers: {
':authority': constrain
}
})
t.test('https get request - constrain', async (t) => {
t.plan(3)
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(beta).length)
t.assert.deepStrictEqual(JSON.parse(res.body), beta)
})
const url = `https://localhost:${fastify.server.address().port}/beta`
const res = await h2url.concat({
url,
headers: {
':authority': constrain
}
})
await t.test('https get request - constrain - not found', async (t) => {
t.plan(1)
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(beta).length)
t.same(JSON.parse(res.body), beta)
const url = `https://localhost:${fastify.server.address().port}/beta`
const res = await h2url.concat({
url
})
t.test('https get request - constrain - not found', async (t) => {
t.plan(1)
t.assert.strictEqual(res.headers[':status'], 404)
})
await t.test('https get request - constrain - verify hostname and port from request', async (t) => {
t.plan(1)
const url = `https://localhost:${fastify.server.address().port}/beta`
const res = await h2url.concat({
url
})
t.equal(res.headers[':status'], 404)
const url = `https://localhost:${fastify.server.address().port}/hostname_port`
const res = await h2url.concat({
url,
headers: {
':authority': constrain
}
})
const body = JSON.parse(res.body)
t.assert.strictEqual(body.hostname, constrain)
})
})

View File

@@ -1,35 +1,34 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
let fastify
try {
fastify = Fastify({
http2: true
test('http2 HEAD test', async (t) => {
let fastify
try {
fastify = Fastify({
http2: true
})
t.assert.ok(true, 'http2 successfully loaded')
} catch (e) {
t.assert.fail('http2 loading failed')
}
fastify.all('/', function (req, reply) {
reply.code(200).send(msg)
})
t.pass('http2 successfully loaded')
} catch (e) {
t.fail('http2 loading failed', e)
}
t.after(() => { fastify.close() })
fastify.all('/', function (req, reply) {
reply.code(200).send(msg)
})
await fastify.listen({ port: 0 })
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('http HEAD request', async (t) => {
await t.test('http HEAD request', async (t) => {
t.plan(1)
const url = `http://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url, method: 'HEAD' })
t.equal(res.headers[':status'], 200)
t.assert.strictEqual(res.headers[':status'], 200)
})
})

View File

@@ -1,18 +0,0 @@
'use strict'
const t = require('tap')
const test = t.test
const proxyquire = require('proxyquire')
const server = proxyquire('../../lib/server', { 'node:http2': null })
const Fastify = proxyquire('../..', { './lib/server.js': server })
test('should throw when http2 module cannot be found', t => {
t.plan(2)
try {
Fastify({ http2: true })
t.fail('fastify did not throw expected error')
} catch (err) {
t.equal(err.code, 'FST_ERR_HTTP2_INVALID_VERSION')
t.equal(err.message, 'HTTP2 is available only from node >= 8.8.1')
}
})

View File

@@ -1,53 +1,68 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
let fastify
try {
fastify = Fastify({
http2: true
test('http2 plain test', async t => {
let fastify
try {
fastify = Fastify({
http2: true
})
t.assert.ok(true, 'http2 successfully loaded')
} catch (e) {
t.assert.fail('http2 loading failed')
}
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
t.pass('http2 successfully loaded')
} catch (e) {
t.fail('http2 loading failed', e)
}
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.get('/host', function (req, reply) {
reply.code(200).send(req.host)
})
fastify.get('/hostname', function (req, reply) {
reply.code(200).send(req.hostname)
})
fastify.get('/hostname_port', function (req, reply) {
reply.code(200).send({ hostname: req.hostname, port: req.port })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.after(() => { fastify.close() })
test('http get request', async (t) => {
await fastify.listen({ port: 0 })
await t.test('http get request', async (t) => {
t.plan(3)
const url = `http://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.same(JSON.parse(res.body), msg)
t.assert.deepStrictEqual(JSON.parse(res.body), msg)
})
test('http hostname', async (t) => {
await t.test('http host', async (t) => {
t.plan(1)
const hostname = `localhost:${fastify.server.address().port}`
const host = `localhost:${fastify.server.address().port}`
const url = `http://${hostname}/hostname`
const url = `http://${host}/host`
const res = await h2url.concat({ url })
t.equal(res.body, hostname)
t.assert.strictEqual(res.body, host)
})
await t.test('http hostname and port', async (t) => {
t.plan(2)
const host = `localhost:${fastify.server.address().port}`
const url = `http://${host}/hostname_port`
const res = await h2url.concat({ url })
t.assert.strictEqual(JSON.parse(res.body).hostname, host.split(':')[0])
t.assert.strictEqual(JSON.parse(res.body).port, parseInt(host.split(':')[1]))
})
})

View File

@@ -1,17 +1,16 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
const sget = require('simple-get').concat
const msg = { hello: 'world' }
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
const { Agent } = require('undici')
test.before(buildCertificate)
test('secure with fallback', (t) => {
t.plan(7)
test('secure with fallback', async (t) => {
t.plan(6)
let fastify
try {
@@ -23,9 +22,9 @@ test('secure with fallback', (t) => {
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
t.assert.ok(true, 'Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
t.assert.fail('Key/cert loading failed')
}
fastify.get('/', function (req, reply) {
@@ -40,71 +39,75 @@ test('secure with fallback', (t) => {
throw new Error('kaboom')
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.after(() => fastify.close())
t.test('https get error', async (t) => {
t.plan(1)
const fastifyServer = await fastify.listen({ port: 0 })
const url = `https://localhost:${fastify.server.address().port}/error`
const res = await h2url.concat({ url })
await t.test('https get error', async (t) => {
t.plan(1)
t.equal(res.headers[':status'], 500)
const url = `${fastifyServer}/error`
const res = await h2url.concat({ url })
t.assert.strictEqual(res.headers[':status'], 500)
})
await t.test('https post', async (t) => {
t.plan(2)
const res = await h2url.concat({
url: fastifyServer,
method: 'POST',
body: JSON.stringify({ hello: 'http2' }),
headers: {
'content-type': 'application/json'
}
})
t.test('https post', async (t) => {
t.plan(2)
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.deepStrictEqual(JSON.parse(res.body), { hello: 'http2' })
})
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({
url,
method: 'POST',
body: JSON.stringify({ hello: 'http2' }),
headers: {
'content-type': 'application/json'
await t.test('https get request', async (t) => {
t.plan(3)
const res = await h2url.concat({ url: fastifyServer })
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.assert.deepStrictEqual(JSON.parse(res.body), msg)
})
await t.test('http1 get request', async t => {
t.plan(4)
const result = await fetch(fastifyServer, {
dispatcher: new Agent({
connect: {
rejectUnauthorized: false
}
})
t.equal(res.headers[':status'], 200)
t.same(JSON.parse(res.body), { hello: 'http2' })
})
t.test('https get request', async (t) => {
t.plan(3)
const body = await result.text()
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
t.assert.deepStrictEqual(JSON.parse(body), msg)
})
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
await t.test('http1 get error', async t => {
t.plan(2)
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.same(JSON.parse(res.body), msg)
})
t.test('http1 get request', t => {
t.plan(4)
sget({
method: 'GET',
url: 'https://localhost:' + fastify.server.address().port,
rejectUnauthorized: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
const result = await fetch(`${fastifyServer}/error`, {
dispatcher: new Agent({
connect: {
rejectUnauthorized: false
}
})
})
t.test('http1 get error', (t) => {
t.plan(2)
sget({
method: 'GET',
url: 'https://localhost:' + fastify.server.address().port + '/error',
rejectUnauthorized: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 500)
})
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 500)
})
})

View File

@@ -1,15 +1,14 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
test.before(buildCertificate)
test('secure', (t) => {
test('secure', async (t) => {
t.plan(4)
let fastify
@@ -21,9 +20,9 @@ test('secure', (t) => {
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
t.assert.ok(true, 'Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
t.assert.fail('Key/cert loading failed')
}
fastify.get('/', function (req, reply) {
@@ -32,28 +31,37 @@ test('secure', (t) => {
fastify.get('/proto', function (req, reply) {
reply.code(200).send({ proto: req.protocol })
})
fastify.get('/hostname_port', function (req, reply) {
reply.code(200).send({ hostname: req.hostname, port: req.port })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.after(() => { fastify.close() })
await fastify.listen({ port: 0 })
t.test('https get request', async (t) => {
t.plan(3)
await t.test('https get request', async (t) => {
t.plan(3)
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
const url = `https://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url })
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.same(JSON.parse(res.body), msg)
})
t.assert.strictEqual(res.headers[':status'], 200)
t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
t.assert.deepStrictEqual(JSON.parse(res.body), msg)
})
t.test('https get request without trust proxy - protocol', async (t) => {
t.plan(2)
await t.test('https get request without trust proxy - protocol', async (t) => {
t.plan(2)
const url = `https://localhost:${fastify.server.address().port}/proto`
t.same(JSON.parse((await h2url.concat({ url })).body), { proto: 'https' })
t.same(JSON.parse((await h2url.concat({ url, headers: { 'X-Forwarded-Proto': 'lorem' } })).body), { proto: 'https' })
})
const url = `https://localhost:${fastify.server.address().port}/proto`
t.assert.deepStrictEqual(JSON.parse((await h2url.concat({ url })).body), { proto: 'https' })
t.assert.deepStrictEqual(JSON.parse((await h2url.concat({ url, headers: { 'X-Forwarded-Proto': 'lorem' } })).body), { proto: 'https' })
})
await t.test('https get request - test hostname and port', async (t) => {
t.plan(2)
const url = `https://localhost:${fastify.server.address().port}/hostname_port`
const parsedbody = JSON.parse((await h2url.concat({ url })).body)
t.assert.strictEqual(parsedbody.hostname, 'localhost')
t.assert.strictEqual(parsedbody.port, fastify.server.address().port)
})
})

View File

@@ -1,31 +1,30 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
const fastify = Fastify({
http2: true
})
test('http2 unknown http method', async t => {
const fastify = Fastify({
http2: true
})
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.after(() => { fastify.close() })
await fastify.listen({ port: 0 })
test('http UNKNOWN_METHOD request', async (t) => {
await t.test('http UNKNOWN_METHOD request', async (t) => {
t.plan(2)
const url = `http://localhost:${fastify.server.address().port}`
const res = await h2url.concat({ url, method: 'UNKNOWN_METHOD' })
t.equal(res.headers[':status'], 404)
t.same(JSON.parse(res.body), {
t.assert.strictEqual(res.headers[':status'], 404)
t.assert.deepStrictEqual(JSON.parse(res.body), {
statusCode: 404,
code: 'FST_ERR_NOT_FOUND',
error: 'Not Found',

View File

@@ -1,12 +1,11 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const https = require('node:https')
const dns = require('node:dns').promises
const sget = require('simple-get').concat
const { buildCertificate } = require('../build-certificate')
const { Agent } = require('undici')
async function setup () {
await buildCertificate()
@@ -14,11 +13,11 @@ async function setup () {
const localAddresses = await dns.lookup('localhost', { all: true })
test('Should support a custom https server', { skip: localAddresses.length < 1 }, async t => {
t.plan(4)
t.plan(5)
const fastify = Fastify({
serverFactory: (handler, opts) => {
t.ok(opts.serverFactory, 'it is called once for localhost')
t.assert.ok(opts.serverFactory, 'it is called once for localhost')
const options = {
key: global.context.key,
@@ -34,29 +33,25 @@ async function setup () {
}
})
t.teardown(fastify.close.bind(fastify))
t.after(() => { fastify.close() })
fastify.get('/', (req, reply) => {
t.ok(req.raw.custom)
t.assert.ok(req.raw.custom)
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 0 })
await new Promise((resolve, reject) => {
sget({
method: 'GET',
url: 'https://localhost:' + fastify.server.address().port,
rejectUnauthorized: false
}, (err, response, body) => {
if (err) {
return reject(err)
const result = await fetch('https://localhost:' + fastify.server.address().port, {
dispatcher: new Agent({
connect: {
rejectUnauthorized: false
}
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), { hello: 'world' })
resolve()
})
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
}

Some files were not shown because too many files have changed in this diff Show More