First commit

This commit is contained in:
2026-01-12 13:12:46 +01:00
parent b2d9501f6d
commit a1fbd8acf5
4413 changed files with 1245183 additions and 0 deletions

2023
node_modules/fastify/test/404s.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

222
node_modules/fastify/test/500s.test.js generated vendored Normal file
View File

@@ -0,0 +1,222 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const symbols = require('../lib/symbols.js')
test('default 500', t => {
t.plan(4)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
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), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
})
})
test('custom 500', t => {
t.plan(6)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
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)
reply
.code(500)
.type('text/plain')
.send('an error happened: ' + err.message)
})
fastify.inject({
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')
})
})
test('encapsulated 500', t => {
t.plan(10)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
fastify.register(function (f, opts, done) {
f.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
f.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
reply
.code(500)
.type('text/plain')
.send('an error happened: ' + err.message)
})
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')
})
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), {
error: 'Internal Server Error',
message: 'kaboom',
statusCode: 500
})
})
})
test('custom 500 with hooks', t => {
t.plan(7)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.get('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
fastify.setErrorHandler(function (err, request, reply) {
reply
.code(500)
.type('text/plain')
.send('an error happened: ' + err.message)
})
fastify.addHook('onSend', (req, res, payload, done) => {
t.ok('called', 'onSend')
done()
})
fastify.addHook('onRequest', (req, res, done) => {
t.ok('called', 'onRequest')
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
t.ok('called', 'onResponse')
done()
})
fastify.inject({
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')
})
})
test('cannot set errorHandler after binding', t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.listen({ port: 0 }, err => {
t.error(err)
try {
fastify.setErrorHandler(() => { })
t.fail()
} catch (e) {
t.pass()
}
})
})
test('cannot set childLoggerFactory after binding', t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.listen({ port: 0 }, err => {
t.error(err)
try {
fastify.setChildLoggerFactory(() => { })
t.fail()
} catch (e) {
t.pass()
}
})
})
test('catch synchronous errors', t => {
t.plan(3)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.setErrorHandler((_, req, reply) => {
throw new Error('kaboom2')
})
fastify.post('/', function (req, reply) {
reply.send(new Error('kaboom'))
})
fastify.inject({
method: 'POST',
url: '/',
headers: {
'Content-Type': 'application/json'
},
payload: JSON.stringify({ hello: 'world' }).substring(0, 5)
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.same(res.json(), {
error: 'Internal Server Error',
message: 'kaboom2',
statusCode: 500
})
})
})

117
node_modules/fastify/test/allowUnsafeRegex.test.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
'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'
})
})
})
})

74
node_modules/fastify/test/als.test.js generated vendored Normal file
View File

@@ -0,0 +1,74 @@
'use strict'
const { AsyncLocalStorage } = require('node:async_hooks')
const t = require('tap')
const Fastify = require('..')
const sget = require('simple-get').concat
if (!AsyncLocalStorage) {
t.skip('AsyncLocalStorage not available, skipping test')
process.exit(0)
}
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)
})
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()
})
})
})
})

691
node_modules/fastify/test/async-await.test.js generated vendored Normal file
View File

@@ -0,0 +1,691 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('..')
const split = require('split2')
const pino = require('pino')
const { sleep } = require('./helper')
const statusCodes = require('node:http').STATUS_CODES
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
test('async await', t => {
t.plan(11)
const fastify = Fastify()
try {
fastify.get('/', opts, async function awaitMyFunc (req, reply) {
await sleep(200)
return { hello: 'world' }
})
t.pass()
} catch (e) {
t.fail()
}
try {
fastify.get('/no-await', opts, async function (req, reply) {
return { hello: 'world' }
})
t.pass()
} catch (e) {
t.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' })
})
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' })
})
})
})
test('ignore the result of the promise if reply.send is called beforehand (undefined)', t => {
t.plan(4)
const server = Fastify()
const payload = { hello: 'world' }
server.get('/', async function awaitMyFunc (req, reply) {
await reply.send(payload)
})
t.teardown(server.close.bind(server))
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)
})
})
})
test('ignore the result of the promise if reply.send is called beforehand (object)', t => {
t.plan(4)
const server = Fastify()
const payload = { hello: 'world2' }
server.get('/', async function awaitMyFunc (req, reply) {
await reply.send(payload)
return { hello: 'world' }
})
t.teardown(server.close.bind(server))
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)
})
})
})
test('server logs an error if reply.send is called and a value is returned via async/await', t => {
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())
})
const logger = pino(splitStream)
const fastify = Fastify({
logger
})
fastify.get('/', async (req, reply) => {
await reply.send({ hello: 'world' })
return { hello: 'world2' }
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
})
})
test('ignore the result of the promise if reply.send is called beforehand (undefined)', t => {
t.plan(4)
const server = Fastify()
const payload = { hello: 'world' }
server.get('/', async function awaitMyFunc (req, reply) {
await reply.send(payload)
})
t.teardown(server.close.bind(server))
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)
})
})
})
test('ignore the result of the promise if reply.send is called beforehand (object)', t => {
t.plan(4)
const server = Fastify()
const payload = { hello: 'world2' }
server.get('/', async function awaitMyFunc (req, reply) {
await reply.send(payload)
return { hello: 'world' }
})
t.teardown(server.close.bind(server))
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)
})
})
})
test('await reply if we will be calling reply.send in the future', t => {
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())
})
const server = Fastify({
logger: {
stream: splitStream
}
})
const payload = { hello: 'world' }
server.get('/', async function awaitMyFunc (req, reply) {
setImmediate(function () {
reply.send(payload)
})
await reply
})
server.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
})
})
test('await reply if we will be calling reply.send in the future (error case)', t => {
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())
})
const server = Fastify({
logger: {
stream: splitStream
}
})
server.get('/', async function awaitMyFunc (req, reply) {
setImmediate(function () {
reply.send(new Error('kaboom'))
})
await reply
})
server.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('support reply decorators with await', t => {
t.plan(2)
const fastify = Fastify()
fastify.decorateReply('wow', function () {
setImmediate(() => {
this.send({ hello: 'world' })
})
return this
})
fastify.get('/', async (req, reply) => {
await sleep(1)
await reply.wow()
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.same(payload, { hello: 'world' })
})
})
test('inject async await', async t => {
t.plan(1)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
}
})
test('inject async await - when the server equal up', async t => {
t.plan(2)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
}
await sleep(200)
try {
const res2 = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res2.payload))
} catch (err) {
t.fail(err)
}
})
test('async await plugin', async t => {
t.plan(1)
const fastify = Fastify()
fastify.register(async (fastify, opts) => {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await sleep(200)
})
try {
const res = await fastify.inject({ method: 'GET', url: '/' })
t.same({ hello: 'world' }, JSON.parse(res.payload))
} catch (err) {
t.fail(err)
}
})
test('does not call reply.send() twice if 204 response equal already sent', t => {
t.plan(2)
const fastify = Fastify()
fastify.get('/', async (req, reply) => {
reply.code(204).send()
reply.send = () => {
throw new Error('reply.send() was called twice')
}
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 204)
})
})
test('promise was fulfilled with undefined', t => {
t.plan(4)
let fastify = null
const stream = split(JSON.parse)
try {
fastify = Fastify({
logger: {
stream,
level: 'error'
}
})
} catch (e) {
t.fail()
}
t.teardown(fastify.close.bind(fastify))
fastify.get('/', async (req, reply) => {
})
stream.once('data', line => {
t.fail('should not log an error')
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => { fastify.close() })
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)
})
})
})
test('promise was fulfilled with undefined using inject', async (t) => {
const stream = split(JSON.parse)
const fastify = Fastify({
logger: {
stream,
level: 'error'
}
})
fastify.get('/', async (req, reply) => {
})
stream.once('data', line => {
t.fail('should not log an error')
})
const res = await fastify.inject('/')
t.equal(res.body, '')
t.equal(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)
let fastify = null
const stream = split(JSON.parse)
const payload = { hello: 'world' }
try {
fastify = Fastify({
logger: {
stream,
level: 'error'
}
})
} catch (e) {
t.fail()
}
t.teardown(fastify.close.bind(fastify))
fastify.get('/', async (req, reply) => {
reply.send(payload)
})
stream.once('data', line => {
t.fail('should not log an error')
})
fastify.listen({ port: 0 }, (err) => {
t.error(err)
t.teardown(() => { fastify.close() })
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)
)
})
})
})
test('Thrown Error instance sets HTTP status code', t => {
t.plan(3)
const fastify = Fastify()
const err = new Error('winter is coming')
err.statusCode = 418
fastify.get('/', async (req, reply) => {
throw err
})
fastify.inject({
method: 'GET',
url: '/'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 418)
t.same(
{
error: statusCodes['418'],
message: err.message,
statusCode: 418
},
JSON.parse(res.payload)
)
})
})
test('customErrorHandler support', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', async (req, reply) => {
const error = new Error('ouch')
error.statusCode = 400
throw error
})
fastify.setErrorHandler(async err => {
t.equal(err.message, 'ouch')
const error = new Error('kaboom')
error.statusCode = 401
throw error
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
{
error: statusCodes['401'],
message: 'kaboom',
statusCode: 401
},
JSON.parse(res.payload)
)
})
})
test('customErrorHandler support without throwing', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', async (req, reply) => {
const error = new Error('ouch')
error.statusCode = 400
throw error
})
fastify.setErrorHandler(async (err, req, reply) => {
t.equal(err.message, 'ouch')
await reply.code(401).send('kaboom')
reply.send = t.fail.bind(t, 'should not be called')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
'kaboom',
res.payload
)
})
})
// See https://github.com/fastify/fastify/issues/2653
test('customErrorHandler only called if reply not already sent', t => {
t.plan(3)
const fastify = Fastify()
fastify.get('/', async (req, reply) => {
await reply.send('success')
const error = new Error('ouch')
error.statusCode = 400
throw error
})
fastify.setErrorHandler(t.fail.bind(t, 'should not be called'))
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.same(
'success',
res.payload
)
})
})
// See https://github.com/fastify/fastify/issues/3209
test('setNotFoundHandler should accept return value', t => {
t.plan(3)
const fastify = Fastify()
fastify.get('/', async () => ({ hello: 'world' }))
fastify.setNotFoundHandler((req, reply) => {
reply.code(404)
return {
error: statusCodes['404'],
message: 'lost',
statusCode: 404
}
})
fastify.inject({
method: 'GET',
url: '/elsewhere'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
t.same(
{
error: statusCodes['404'],
message: 'lost',
statusCode: 404
},
JSON.parse(res.payload)
)
})
})
// See https://github.com/fastify/fastify/issues/3209
test('customErrorHandler should accept return value', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', async (req, reply) => {
const error = new Error('ouch')
error.statusCode = 400
throw error
})
fastify.setErrorHandler((err, req, reply) => {
t.equal(err.message, 'ouch')
reply.code(401)
return {
error: statusCodes['401'],
message: 'kaboom',
statusCode: 401
}
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 401)
t.same(
{
error: statusCodes['401'],
message: 'kaboom',
statusCode: 401
},
JSON.parse(res.payload)
)
})
})
test('await self', async t => {
const app = Fastify()
t.equal(await app, app)
})

21
node_modules/fastify/test/async-dispose.test.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
'use strict'
const t = require('tap')
const Fastify = require('../fastify')
// asyncDispose doesn't exist in node <= 16
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)
// the same as syntax sugar for
// await using app = fastify()
await fastify[Symbol.asyncDispose]()
t.equal(fastify.server.listening, false)
})

69
node_modules/fastify/test/async_hooks.test.js generated vendored Normal file
View File

@@ -0,0 +1,69 @@
'use strict'
const { createHook } = require('node:async_hooks')
const t = require('tap')
const Fastify = require('..')
const sget = require('simple-get').concat
const remainingIds = new Set()
createHook({
init (asyncId, type, triggerAsyncId, resource) {
if (type === 'content-type-parser:run') {
remainingIds.add(asyncId)
}
},
destroy (asyncId) {
remainingIds.delete(asyncId)
}
})
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()
})
})
})
})

194
node_modules/fastify/test/bodyLimit.test.js generated vendored Normal file
View File

@@ -0,0 +1,194 @@
'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)
})
})
})

53
node_modules/fastify/test/buffer.test.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
test('Buffer test', async t => {
const fastify = Fastify()
fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, fastify.getDefaultJsonParser('error', 'ignore'))
fastify.delete('/', async (request) => {
return request.body
})
test('should return 200 if the body is not empty', async t => {
t.plan(3)
const response = await fastify.inject({
method: 'DELETE',
url: '/',
payload: Buffer.from('{"hello":"world"}'),
headers: {
'content-type': 'application/json'
}
})
t.error(response.error)
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '{"hello":"world"}')
})
test('should return 400 if the body is empty', async t => {
t.plan(3)
const response = await fastify.inject({
method: 'DELETE',
url: '/',
payload: Buffer.alloc(0),
headers: {
'content-type': 'application/json'
}
})
t.error(response.error)
t.equal(response.statusCode, 400)
t.same(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
})
})
})

109
node_modules/fastify/test/build-certificate.js generated vendored Normal file
View File

@@ -0,0 +1,109 @@
'use strict'
const os = require('os')
const forge = require('node-forge')
// from self-cert module
function selfCert (opts) {
const options = opts || {}
const log = opts.logger || require('abstract-logging')
const now = new Date()
if (!options.attrs) options.attrs = {}
if (!options.expires) {
options.expires = new Date(
now.getFullYear() + 5, now.getMonth() + 1, now.getDate()
)
}
log.debug('generating key pair')
const keys = forge.pki.rsa.generateKeyPair(options.bits || 2048)
log.debug('key pair generated')
log.debug('generating self-signed certificate')
const cert = forge.pki.createCertificate()
cert.publicKey = keys.publicKey
cert.serialNumber = '01'
cert.validity.notBefore = now
cert.validity.notAfter = options.expires
const attrs = [
{ name: 'commonName', value: options.attrs.commonName || os.hostname() },
{ name: 'countryName', value: options.attrs.countryName || 'US' },
{ name: 'stateOrProvinceName', value: options.attrs.stateName || 'Georgia' },
{ name: 'localityName', value: options.attrs.locality || 'Atlanta' },
{ name: 'organizationName', value: options.attrs.orgName || 'None' },
{ shortName: 'OU', value: options.attrs.shortName || 'example' }
]
cert.setSubject(attrs)
cert.setIssuer(attrs)
cert.setExtensions([
{ name: 'basicConstraints', cA: true },
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true
},
{
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true
},
{ name: 'subjectKeyIdentifier' },
{
name: 'subjectAltName',
altNames: [{ type: 6 /* URI */, value: 'DNS: ' + attrs[0].value }].concat((function () {
const interfaces = os.networkInterfaces()
// fix citgm: skip invalid ips (aix72-ppc64)
const ips = Object.values(interfaces).flat()
.filter(i => !!forge.util.bytesFromIP(i.address))
.map(i => ({ type: 7 /* IP */, ip: i.address }))
return ips
}()))
}
])
cert.sign(keys.privateKey)
log.debug('certificate generated')
return {
privateKey: forge.pki.privateKeyToPem(keys.privateKey),
publicKey: forge.pki.publicKeyToPem(keys.publicKey),
certificate: forge.pki.certificateToPem(cert)
}
}
async 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.
if (!global.context || !global.context.cert || !global.context.key) {
const certs = selfCert({
expires: new Date(Date.now() + 86400000)
})
global.context = {
cert: certs.certificate,
key: certs.privateKey
}
}
}
module.exports = { buildCertificate }

View File

@@ -0,0 +1,35 @@
'use strict'
const t = require('tap')
const test = t.test
const fs = require('node:fs')
const path = require('node:path')
const { code } = require('../../build/build-error-serializer')
function unifyLineBreak (str) {
return str.toString().replace(/\r\n/g, '\n')
}
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)
// if there are any invalid syntax
// fatal count will be greater than 0
t.equal(result[0].fatalErrorCount, 0)
})
const isPrepublish = !!process.env.PREPUBLISH
test('ensure the current error serializer is latest', { skip: !isPrepublish }, async (t) => {
t.plan(1)
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))
})

15
node_modules/fastify/test/build/version.test.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const t = require('tap')
const test = t.test
const fastify = require('../../fastify')()
test('should be the same as package.json', t => {
t.plan(1)
const json = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json')).toString('utf8'))
t.equal(fastify.version, json.version)
})

29
node_modules/fastify/test/bundler/README.md generated vendored Normal file
View File

@@ -0,0 +1,29 @@
# 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.
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
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.
To run the bundler tests you will need to install the repository dependencies followed by the bundler
stack dependencies. See:
```bash
# path: root of repository /fastify
npm i
cd test/bundler/webpack
npm i
npm run test # test command runs bundle before of starting the test
```
## Bundler test development
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,
no need to go in deep testing unless an issue is raised.

View File

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

10
node_modules/fastify/test/bundler/esbuild/package.json generated vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"version": "0.0.1",
"scripts": {
"bundle": "esbuild success=src/index.js failPlugin=src/fail-plugin-version.js --bundle --outdir=dist --platform=node",
"test": "npm run bundle && node bundler-test.js"
},
"devDependencies": {
"esbuild": "^0.14.11"
}
}

View File

@@ -0,0 +1,14 @@
'use strict'
const fp = require('fastify-plugin')
const fastify = require('../../../../')()
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
fastify.register(fp((instance, opts, done) => {
done()
}, { fastify: '9.x' }))
module.exports = fastify

View File

@@ -0,0 +1,9 @@
'use strict'
const fastify = require('../../../../')()
// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
module.exports = fastify

View File

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

11
node_modules/fastify/test/bundler/webpack/package.json generated vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version":"0.0.1",
"scripts": {
"bundle": "webpack",
"test": "npm run bundle && node bundler-test.js"
},
"devDependencies": {
"webpack": "^5.49.0",
"webpack-cli": "^4.7.2"
}
}

View File

@@ -0,0 +1,14 @@
'use strict'
const fp = require('fastify-plugin')
const fastify = require('../../../../')()
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
fastify.register(fp((instance, opts, done) => {
done()
}, { fastify: '9.x' }))
module.exports = fastify

View File

@@ -0,0 +1,9 @@
'use strict'
const fastify = require('../../../../')()
// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
module.exports = fastify

View File

@@ -0,0 +1,15 @@
'use strict'
const path = require('node:path')
module.exports = {
entry: { success: './src/index.js', failPlugin: './src/fail-plugin-version.js' },
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: {
type: 'commonjs2'
}
}
}

120
node_modules/fastify/test/case-insensitive.test.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const sget = require('simple-get').concat
test('case insensitive', t => {
t.plan(4)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/foo', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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'
})
})
})
})
test('case insensitive inject', t => {
t.plan(4)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/foo', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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'
})
})
})
})
test('case insensitive (parametric)', t => {
t.plan(5)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/foo/:param', (req, reply) => {
t.equal(req.params.param, 'bAr')
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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'
})
})
})
})
test('case insensitive (wildcard)', t => {
t.plan(5)
const fastify = Fastify({
caseSensitive: false
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/foo/*', (req, reply) => {
t.equal(req.params['*'], 'bAr/baZ')
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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'
})
})
})
})

41
node_modules/fastify/test/chainable.test.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
'use strict'
const t = require('tap')
const test = t.test
const fastify = require('..')()
const noop = () => {}
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
test('chainable - get', t => {
t.plan(1)
t.type(fastify.get('/', opts, noop), fastify)
})
test('chainable - post', t => {
t.plan(1)
t.type(fastify.post('/', opts, noop), fastify)
})
test('chainable - route', t => {
t.plan(1)
t.type(fastify.route({
method: 'GET',
url: '/other',
schema: opts.schema,
handler: noop
}), fastify)
})

91
node_modules/fastify/test/childLoggerFactory.test.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
'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' })
})
})

38
node_modules/fastify/test/client-timeout.test.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict'
const { test } = require('tap')
const fastify = require('..')({ requestTimeout: 5, http: { connectionsCheckingInterval: 1000 } })
const { connect } = require('node:net')
test('requestTimeout should return 408', t => {
t.plan(1)
t.teardown(() => {
fastify.close()
})
fastify.post('/', async function (req, reply) {
await new Promise(resolve => setTimeout(resolve, 100))
return reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, err => {
if (err) {
throw err
}
let data = Buffer.alloc(0)
const socket = connect(fastify.server.address().port)
socket.write('POST / HTTP/1.1\r\nHost: example.com\r\nConnection-Length: 1\r\n')
socket.on('data', c => (data = Buffer.concat([data, c])))
socket.on('end', () => {
t.equal(
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()
})
})
})

109
node_modules/fastify/test/close-pipelining.test.js generated vendored Normal file
View File

@@ -0,0 +1,109 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const { Client } = require('undici')
const semver = require('semver')
test('Should return 503 while closing - pipelining', async t => {
const fastify = Fastify({
return503OnClosing: true,
forceCloseConnections: false
})
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()
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 0 })
const instance = new Client('http://localhost:' + fastify.server.address().port, {
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 actual = responses.map(r => r.statusCode)
t.same(actual, codes)
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()
// therefore our socket will be closed
const fastify = Fastify({
return503OnClosing: false,
forceCloseConnections: false
})
fastify.get('/', async (req, reply) => {
reply.send({ hello: 'world' })
fastify.close()
})
await fastify.listen({ port: 0 })
const instance = new Client('http://localhost:' + fastify.server.address().port, {
pipelining: 2
})
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' })
])
t.equal(responses[0].status, 'fulfilled')
t.equal(responses[1].status, 'fulfilled')
t.equal(responses[2].status, 'rejected')
t.equal(responses[3].status, 'rejected')
await instance.close()
})

729
node_modules/fastify/test/close.test.js generated vendored Normal file
View File

@@ -0,0 +1,729 @@
'use strict'
const net = require('node:net')
const http = require('node:http')
const { test } = require('tap')
const Fastify = require('..')
const { Client } = require('undici')
const semver = require('semver')
const split = require('split2')
const { sleep } = require('./helper')
test('close callback', t => {
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)
done()
}
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
})
})
})
test('inside register', t => {
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)
done()
}
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
})
})
})
test('close order', t => {
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)
done()
})
done()
})
fastify.addHook('onClose', (instance, done) => {
t.equal(order.shift(), 2)
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.close((err) => {
t.error(err)
t.equal(order.shift(), 3)
})
})
})
test('close order - async', async t => {
t.plan(3)
const fastify = Fastify()
const order = [1, 2, 3]
fastify.register(function (f, opts, done) {
f.addHook('onClose', async instance => {
t.equal(order.shift(), 1)
})
done()
})
fastify.addHook('onClose', () => {
t.equal(order.shift(), 2)
})
await fastify.listen({ port: 0 })
await fastify.close()
t.equal(order.shift(), 3)
})
test('should not throw an error if the server is not listening', t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
function onClose (instance, done) {
t.type(fastify, instance)
done()
}
fastify.close((err) => {
t.error(err)
})
})
test('onClose should keep the context', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(plugin)
function plugin (instance, opts, done) {
instance.decorate('test', true)
instance.addHook('onClose', onClose)
t.ok(instance.prototype === fastify.prototype)
function onClose (i, done) {
t.ok(i.test)
t.equal(i, instance)
done()
}
done()
}
fastify.close((err) => {
t.error(err)
})
})
test('Should return error while closing (promise) - injection', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onClose', (instance, done) => { done() })
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
fastify.close()
process.nextTick(() => {
fastify.inject({
method: 'GET',
url: '/'
}).catch(err => {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
})
}, 100)
})
})
test('Should return error while closing (callback) - injection', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onClose', (instance, done) => {
setTimeout(done, 150)
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
fastify.close()
setTimeout(() => {
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
})
}, 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 => {
t.plan(4)
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.on('error', function () {
// Depending on the Operating System
// the socket could error or not.
// However, it will always be closed.
})
client.on('close', function () {
t.pass('close')
})
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')
})
})
})
})
test('Current opened connection should not accept new incoming connections', t => {
t.plan(3)
const fastify = Fastify({ forceCloseConnections: false })
fastify.get('/', (req, reply) => {
fastify.close()
setTimeout(() => {
reply.send({ hello: 'world' })
}, 250)
})
fastify.listen({ port: 0 }, err => {
t.error(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)
})
})
})
test('rejected incoming connections should be logged', t => {
t.plan(2)
const stream = split(JSON.parse)
const fastify = Fastify({
forceCloseConnections: false,
logger: {
stream,
level: 'info'
}
})
const messages = []
stream.on('data', message => {
messages.push(message)
})
fastify.get('/', (req, reply) => {
fastify.close()
setTimeout(() => {
reply.send({ hello: 'world' })
}, 250)
})
fastify.listen({ port: 0 }, err => {
t.error(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')))
})
})
})
test('Cannot be reopened the closed server without listen callback', async t => {
t.plan(2)
const fastify = Fastify()
await fastify.listen({ port: 0 })
await fastify.close()
try {
await fastify.listen({ port: 0 })
} catch (err) {
t.ok(err)
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
}
})
test('Cannot be reopened the closed server has listen callback', async t => {
t.plan(2)
const fastify = Fastify()
await fastify.listen({ port: 0 })
await fastify.close()
await new Promise((resolve, reject) => {
fastify.listen({ port: 0 }, err => {
reject(err)
})
}).catch(err => {
t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.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 => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
const fastify = Fastify({ forceCloseConnections: true })
fastify.server.setTimeout(timeoutTime)
fastify.server.keepAliveTimeout = timeoutTime
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(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)
fastify.close((err) => {
t.error(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)
})
})
})
})
test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, t => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
const fastify = Fastify({ forceCloseConnections: 'idle' })
fastify.server.setTimeout(timeoutTime)
fastify.server.keepAliveTimeout = timeoutTime
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(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)
fastify.close((err) => {
t.error(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)
})
})
})
})
test('triggers on-close hook in the right order with multiple bindings', async t => {
const expectedOrder = [1, 2, 3]
const order = []
const fastify = Fastify()
t.plan(1)
// Follows LIFO
fastify.addHook('onClose', () => {
order.push(2)
})
fastify.addHook('onClose', () => {
order.push(1)
})
await fastify.listen({ port: 0 })
await new Promise((resolve, reject) => {
setTimeout(() => {
fastify.close(err => {
order.push(3)
t.match(order, expectedOrder)
if (err) t.error(err)
else resolve()
})
}, 2000)
})
})
test('triggers on-close hook in the right order with multiple bindings (forceCloseConnections - idle)', { skip: noSupport }, async t => {
const expectedPayload = { hello: 'world' }
const timeoutTime = 2 * 60 * 1000
const expectedOrder = [1, 2]
const order = []
const fastify = Fastify({ forceCloseConnections: 'idle' })
fastify.server.setTimeout(timeoutTime)
fastify.server.keepAliveTimeout = timeoutTime
fastify.get('/', async (req, reply) => {
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
return expectedPayload
})
fastify.addHook('onClose', () => {
order.push(1)
})
await fastify.listen({ port: 0 })
const addresses = fastify.addresses()
const testPlan = (addresses.length * 2) + 1
t.plan(testPlan)
for (const addr of addresses) {
const { family, address, port } = addr
const host = family === 'IPv6' ? `[${address}]` : address
const client = new Client(`http://${host}:${port}`, {
keepAliveTimeout: 1 * 60 * 1000
})
client.request({ path: '/', method: 'GET' })
.then((res) => res.body.json(), err => t.error(err))
.then(json => {
t.match(json, expectedPayload, 'should payload match')
t.notOk(client.closed, 'should client not be closed')
}, err => t.error(err))
}
await new Promise((resolve, reject) => {
setTimeout(() => {
fastify.close(err => {
order.push(2)
t.match(order, expectedOrder)
if (err) t.error(err)
else resolve()
})
}, 2000)
})
})
test('triggers on-close hook in the right order with multiple bindings (forceCloseConnections - true)', { skip: noSupport }, async t => {
const expectedPayload = { hello: 'world' }
const timeoutTime = 2 * 60 * 1000
const expectedOrder = [1, 2]
const order = []
const fastify = Fastify({ forceCloseConnections: true })
fastify.server.setTimeout(timeoutTime)
fastify.server.keepAliveTimeout = timeoutTime
fastify.get('/', async (req, reply) => {
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
return expectedPayload
})
fastify.addHook('onClose', () => {
order.push(1)
})
await fastify.listen({ port: 0 })
const addresses = fastify.addresses()
const testPlan = (addresses.length * 2) + 1
t.plan(testPlan)
for (const addr of addresses) {
const { family, address, port } = addr
const host = family === 'IPv6' ? `[${address}]` : address
const client = new Client(`http://${host}:${port}`, {
keepAliveTimeout: 1 * 60 * 1000
})
client.request({ path: '/', method: 'GET' })
.then((res) => res.body.json(), err => t.error(err))
.then(json => {
t.match(json, expectedPayload, 'should payload match')
t.notOk(client.closed, 'should client not be closed')
}, err => t.error(err))
}
await new Promise((resolve, reject) => {
setTimeout(() => {
fastify.close(err => {
order.push(2)
t.match(order, expectedOrder)
if (err) t.error(err)
else resolve()
})
}, 2000)
})
})
test('shutsdown while keep-alive connections are active (non-async, custom)', t => {
t.plan(5)
const timeoutTime = 2 * 60 * 1000
const fastify = Fastify({
forceCloseConnections: true,
serverFactory (handler) {
const server = http.createServer(handler)
server.closeAllConnections = null
return server
}
})
fastify.server.setTimeout(timeoutTime)
fastify.server.keepAliveTimeout = timeoutTime
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 0 }, (err, address) => {
t.error(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)
fastify.close((err) => {
t.error(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)
})
})
})
})
test('preClose callback', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
let preCloseCalled = false
function onClose (instance, done) {
t.equal(preCloseCalled, true)
done()
}
fastify.addHook('preClose', preClose)
function preClose (done) {
t.type(this, fastify)
preCloseCalled = true
done()
}
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
})
})
})
test('preClose async', async t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onClose', onClose)
let preCloseCalled = false
async function onClose () {
t.equal(preCloseCalled, true)
}
fastify.addHook('preClose', preClose)
async function preClose () {
preCloseCalled = true
t.type(this, fastify)
}
await fastify.listen({ port: 0 })
await fastify.close()
})
test('preClose execution order', t => {
t.plan(4)
const fastify = Fastify()
const order = []
fastify.addHook('onClose', onClose)
function onClose (instance, done) {
t.same(order, [1, 2, 3])
done()
}
fastify.addHook('preClose', (done) => {
setTimeout(function () {
order.push(1)
done()
}, 200)
})
fastify.addHook('preClose', async () => {
await sleep(100)
order.push(2)
})
fastify.addHook('preClose', (done) => {
setTimeout(function () {
order.push(3)
done()
}, 100)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.close((err) => {
t.error(err)
t.ok('close callback')
})
})
})

43
node_modules/fastify/test/connectionTimeout.test.js generated vendored Normal file
View File

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

900
node_modules/fastify/test/constrained-routes.test.js generated vendored Normal file
View File

@@ -0,0 +1,900 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../fastify')
test('Should register a host constrained route', t => {
t.plan(7)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ hello: 'world' })
}
})
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)
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
})
test('Should register the same route with host constraints', t => {
t.plan(8)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send('fastify.dev')
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'example.com' },
handler: (req, reply) => {
reply.send('example.com')
}
})
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')
})
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')
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'fancy.ca'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
})
test('Should allow registering custom constrained routes', 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) => {
return req.headers['x-secret']
},
validate () { return true }
}
const fastify = Fastify({ 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' })
}
})
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)
})
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)
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
})
test('Should allow registering custom constrained routes outside constructor', 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) => {
return req.headers['x-secret']
},
validate () { return true }
}
const fastify = Fastify()
fastify.addConstraintStrategy(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' })
}
})
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)
})
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)
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'X-Secret': 'gamma'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
})
test('Custom constrained routes registered also for HEAD method generated by fastify', t => {
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({ constraints: { secret: constraint } })
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'mySecret' },
handler: (req, reply) => {
reply.send('from mySecret - my length is 31')
}
})
fastify.inject({
method: 'HEAD',
url: '/',
headers: {
'X-Secret': 'mySecret'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '31')
t.equal(res.statusCode, 200)
})
})
test('Custom constrained routes registered with addConstraintStrategy apply also for HEAD method generated by fastify', t => {
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()
fastify.addConstraintStrategy(constraint)
fastify.route({
method: 'GET',
url: '/',
constraints: { secret: 'mySecret' },
handler: (req, reply) => {
reply.send('from mySecret - my length is 31')
}
})
fastify.inject({
method: 'HEAD',
url: '/',
headers: {
'X-Secret': 'mySecret'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '31')
t.equal(res.statusCode, 200)
})
})
test('Add a constraint strategy after fastify instance was started', t => {
t.plan(4)
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()
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => { reply.send('ok') }
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same(res.payload, 'ok')
t.equal(res.statusCode, 200)
t.throws(
() => fastify.addConstraintStrategy(constraint),
'Cannot add constraint strategy when fastify instance is already started!'
)
})
})
test('Add a constraint strategy should throw an error if there already exist custom strategy with the same name', t => {
t.plan(1)
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()
fastify.addConstraintStrategy(constraint)
t.throws(
() => fastify.addConstraintStrategy(constraint),
'There already exists a custom constraint with the name secret.'
)
})
test('Add a constraint strategy shouldn\'t throw an error if default constraint with the same name isn\'t used', t => {
t.plan(1)
const constraint = {
name: 'version',
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()
fastify.addConstraintStrategy(constraint)
t.pass()
})
test('Add a constraint strategy should throw an error if default constraint with the same name is used', t => {
t.plan(1)
const constraint = {
name: 'version',
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()
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.0.0' },
handler: (req, reply) => {
reply.send('ok')
}
})
t.throws(
() => fastify.addConstraintStrategy(constraint),
'There already exists a route with version constraint.'
)
})
test('The hasConstraintStrategy should return false for default constraints until they are used', t => {
t.plan(6)
const fastify = Fastify()
t.equal(fastify.hasConstraintStrategy('version'), false)
t.equal(fastify.hasConstraintStrategy('host'), false)
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ hello: 'from any other domain' })
}
})
t.equal(fastify.hasConstraintStrategy('version'), false)
t.equal(fastify.hasConstraintStrategy('host'), true)
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.0.0' },
handler: (req, reply) => {
reply.send({ hello: 'from any other domain' })
}
})
t.equal(fastify.hasConstraintStrategy('version'), true)
t.equal(fastify.hasConstraintStrategy('host'), true)
})
test('The hasConstraintStrategy should return true if there already exist a custom constraint with the same name', t => {
t.plan(2)
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()
t.equal(fastify.hasConstraintStrategy('secret'), false)
fastify.addConstraintStrategy(constraint)
t.equal(fastify.hasConstraintStrategy('secret'), true)
})
test('Should allow registering an unconstrained route after a constrained route', t => {
t.plan(6)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ hello: 'from fastify.dev' })
}
})
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send({ hello: 'from any other domain' })
}
})
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)
})
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)
})
})
test('Should allow registering constrained routes in a prefixed plugin', t => {
t.plan(3)
const fastify = Fastify()
fastify.register(async (scope, opts) => {
scope.route({
method: 'GET',
constraints: { host: 'fastify.dev' },
path: '/route',
handler: (req, reply) => {
reply.send({ ok: true })
}
})
}, { prefix: '/prefix' })
fastify.inject({
method: 'GET',
url: '/prefix/route',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), { ok: true })
t.equal(res.statusCode, 200)
})
})
test('Should allow registering a constrained GET route after a constrained HEAD route', t => {
t.plan(3)
const fastify = Fastify()
fastify.route({
method: 'HEAD',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('custom HEAD response')
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ hello: 'from any other domain' })
}
})
fastify.inject({
method: 'HEAD',
url: '/',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(res.payload, 'custom HEAD response')
t.equal(res.statusCode, 200)
})
})
test('Should allow registering a constrained GET route after an unconstrained HEAD route', t => {
t.plan(3)
const fastify = Fastify()
fastify.route({
method: 'HEAD',
url: '/',
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('HEAD response: length is about 33')
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('Hello from constrains: length is about 41')
}
})
fastify.inject({
method: 'HEAD',
url: '/',
headers: {
host: 'fastify.dev'
}
}, (err, res) => {
t.error(err)
t.same(res.headers['content-length'], '41')
t.equal(res.statusCode, 200)
})
})
test('Will not try to re-createprefixed HEAD route if it already exists and exposeHeadRoutes is true for constrained routes', async (t) => {
t.plan(1)
const fastify = Fastify({ exposeHeadRoutes: true })
fastify.register((scope, opts, next) => {
scope.route({
method: 'HEAD',
path: '/route',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('custom HEAD response')
}
})
scope.route({
method: 'GET',
path: '/route',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ ok: true })
}
})
next()
}, { prefix: '/prefix' })
await fastify.ready()
t.ok(true)
})
test('allows separate constrained and unconstrained HEAD routes', async (t) => {
t.plan(1)
const fastify = Fastify({ exposeHeadRoutes: true })
fastify.register((scope, opts, next) => {
scope.route({
method: 'HEAD',
path: '/route',
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('unconstrained HEAD response')
}
})
scope.route({
method: 'HEAD',
path: '/route',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.header('content-type', 'text/plain')
reply.send('constrained HEAD response')
}
})
scope.route({
method: 'GET',
path: '/route',
constraints: { host: 'fastify.dev' },
handler: (req, reply) => {
reply.send({ ok: true })
}
})
next()
}, { prefix: '/prefix' })
await fastify.ready()
t.ok(true)
})
test('allow async 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({ 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.same(JSON.parse(payload), { hello: 'from alpha' })
t.equal(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)
}
{
const { statusCode } = await fastify.inject({ method: 'GET', path: '/', headers: { 'X-Secret': 'gamma' } })
t.equal(statusCode, 404)
}
})
test('error in async 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({ 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.same(JSON.parse(payload), { error: 'Internal Server Error', message: 'Unexpected error from async constraint', statusCode: 500 })
t.equal(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)
}
{
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)
}
{
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)
}
})
test('Allow regex constraints in routes', t => {
t.plan(5)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.dev$/ },
handler: (req, reply) => {
reply.send({ hello: 'from fastify dev domain' })
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
host: 'dev.fastify.dev'
}
}, (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',
url: '/',
headers: {
host: 'google.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
})

189
node_modules/fastify/test/content-length.test.js generated vendored Normal file
View File

@@ -0,0 +1,189 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
test('default 413 with bodyLimit option', t => {
t.plan(4)
const fastify = Fastify({
bodyLimit: 10
})
fastify.post('/', function (req, reply) {
reply.send({ hello: 'world' })
})
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
})
})
})
test('default 400 with wrong content-length', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.inject({
method: 'POST',
url: '/',
headers: {
'content-length': 20
},
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
})
})
})
test('custom 413 with bodyLimit option', t => {
t.plan(4)
const fastify = Fastify({
bodyLimit: 10
})
fastify.post('/', function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.setErrorHandler(function (err, request, reply) {
reply
.code(err.statusCode)
.type('application/json; charset=utf-8')
.send(err)
})
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
})
})
})
test('custom 400 with wrong content-length', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.setErrorHandler(function (err, request, reply) {
reply
.code(err.statusCode)
.type('application/json; charset=utf-8')
.send(err)
})
fastify.inject({
method: 'POST',
url: '/',
headers: {
'content-length': 20
},
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
})
})
})
test('#2214 - wrong content-length', t => {
const fastify = Fastify()
fastify.get('/', async () => {
const error = new Error('MY_ERROR_MESSAGE')
error.headers = {
'content-length': 2
}
throw error
})
fastify.inject({
method: 'GET',
path: '/'
})
.then(response => {
t.equal(response.headers['content-length'], '' + response.rawPayload.length)
t.end()
})
})
test('#2543 - wrong content-length with errorHandler', t => {
const fastify = Fastify()
fastify.setErrorHandler((_error, _request, reply) => {
reply.code(500).send({ message: 'longer than 2 bytes' })
})
fastify.get('/', async () => {
const error = new Error('MY_ERROR_MESSAGE')
error.headers = {
'content-length': 2
}
throw error
})
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()
})
})

752
node_modules/fastify/test/content-parser.test.js generated vendored Normal file
View File

@@ -0,0 +1,752 @@
'use strict'
const t = require('tap')
const test = t.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')
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)
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'))
})
})
test('should work with string and RegExp', t => {
t.plan(7)
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.end()
})
test('getParser', t => {
test('should return matching parser', t => {
t.plan(3)
const fastify = Fastify()
fastify.addContentTypeParser(/^image\/.*/, first)
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)
})
test('should return matching parser with caching', 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)
})
test('should prefer content type parser with string value', t => {
t.plan(2)
const fastify = Fastify()
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)
})
test('should return parser that catches all if no other is set', t => {
t.plan(3)
const fastify = Fastify()
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)
})
test('should return undefined if no matching parser exist', t => {
t.plan(2)
const fastify = Fastify()
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.end()
})
test('existingParser', t => {
test('returns always false for "*"', t => {
t.plan(2)
const fastify = Fastify()
fastify.addContentTypeParser(/^image\/.*/, first)
fastify.addContentTypeParser(/^application\/.+\+xml/, first)
fastify.addContentTypeParser('text/html', first)
t.notOk(fastify[keys.kContentTypeParser].existingParser('*'))
fastify.addContentTypeParser('*', first)
t.notOk(fastify[keys.kContentTypeParser].existingParser('*'))
})
test('let you override the default parser once', t => {
t.plan(2)
const fastify = Fastify()
fastify.addContentTypeParser('application/json', first)
fastify.addContentTypeParser('text/plain', first)
t.throws(
() => fastify.addContentTypeParser('application/json', first),
FST_ERR_CTP_ALREADY_PRESENT,
"Content type parser 'application/json' already present"
)
t.throws(
() => fastify.addContentTypeParser('text/plain', first),
FST_ERR_CTP_ALREADY_PRESENT,
"Content type parser 'text/plain' already present"
)
})
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
fastify.addContentTypeParser(/^image\/.*/, first)
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()
})
test('add', 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(
() => contentTypeParser.add({}, {}, first),
FST_ERR_CTP_INVALID_TYPE,
'The content type should be a string or a RegExp'
)
t.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 => {
t.plan(1)
const fastify = Fastify()
const contentTypeParser = fastify[keys.kContentTypeParser]
contentTypeParser.add('*', {}, first)
t.equal(contentTypeParser.customParsers.get('').fn, first)
})
t.end()
})
test('non-Error thrown from content parser is properly handled', t => {
t.plan(3)
const fastify = Fastify()
const throwable = 'test'
const payload = 'error'
fastify.addContentTypeParser('text/test', (request, payload, done) => {
done(throwable)
})
fastify.post('/', (req, reply) => {
})
fastify.setErrorHandler((err, req, res) => {
t.equal(err, throwable)
res.send(payload)
})
fastify.inject({
method: 'POST',
url: '/',
headers: { 'Content-Type': 'text/test' },
body: 'some text'
}, (err, res) => {
t.error(err)
t.equal(res.payload, payload)
})
})
test('Error thrown 415 from content type is null and make post request to server', t => {
t.plan(3)
const fastify = Fastify()
const errMsg = new FST_ERR_CTP_INVALID_MEDIA_TYPE(undefined).message
fastify.post('/', (req, reply) => {
})
fastify.inject({
method: 'POST',
url: '/',
body: 'some text'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 415)
t.equal(JSON.parse(res.body).message, errMsg)
})
})
test('remove', t => {
test('should remove default parser', t => {
t.plan(3)
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'))
})
test('should remove RegExp parser', t => {
t.plan(3)
const fastify = Fastify()
fastify.addContentTypeParser(/^text\/*/, first)
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()))
})
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)
})
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'))
})
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.end()
})
test('remove all should remove all existing parsers and reset cache', t => {
t.plan(4)
const fastify = Fastify()
fastify.addContentTypeParser('application/xml', first)
fastify.addContentTypeParser(/^image\/.*/, first)
const contentTypeParser = fastify[keys.kContentTypeParser]
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)
})
test('Safeguard against malicious content-type / 1', async t => {
const badNames = Object.getOwnPropertyNames({}.__proto__) // eslint-disable-line
t.plan(badNames.length)
const fastify = Fastify()
fastify.post('/', async () => {
return 'ok'
})
for (const prop of badNames) {
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': prop
},
body: ''
})
t.same(response.statusCode, 415)
}
})
test('Safeguard against malicious content-type / 2', async t => {
t.plan(1)
const fastify = Fastify()
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': '\\u0063\\u006fnstructor'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('Safeguard against malicious content-type / 3', async t => {
t.plan(1)
const fastify = Fastify()
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'constructor; charset=utf-8'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('Safeguard against content-type spoofing - string', async t => {
t.plan(1)
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)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'text/plain; content-type="application/json"'
},
body: ''
})
})
test('Safeguard against content-type spoofing - regexp', async t => {
t.plan(1)
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)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'text/plain; content-type="application/json"'
},
body: ''
})
})
test('content-type match parameters - string 1', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain; charset=utf8', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.addContentTypeParser('application/json; charset=utf8', 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; charset=utf8'
},
body: ''
})
})
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')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
})
test('content-type fail when parameters not match - string 1', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('content-type fail when parameters not match - 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.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8; foo=baz'
},
body: ''
})
t.same(response.statusCode, 415)
})
test('content-type fail when parameters not match - regexp', async t => {
t.plan(1)
const fastify = Fastify()
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser(/application\/json; charset=utf8; foo=bar/, function (request, body, done) {
t.fail('shouldn\'t be called')
done(null, body)
})
fastify.post('/', async () => {
return 'ok'
})
const response = await fastify.inject({
method: 'POST',
path: '/',
headers: {
'content-type': 'application/json; charset=utf8'
},
body: ''
})
t.same(response.statusCode, 415)
})
// Refs: https://github.com/fastify/fastify/issues/4495
test('content-type regexp list should be cloned when plugin override', async t => {
t.plan(6)
const fastify = Fastify()
fastify.addContentTypeParser(/^image\/.*/, { parseAs: 'buffer' }, (req, payload, done) => {
done(null, payload)
})
fastify.register(function plugin (fastify, options, done) {
fastify.post('/', function (request, reply) {
reply.type(request.headers['content-type']).send(request.body)
})
done()
})
{
const { payload, headers, statusCode } = await fastify.inject({
method: 'POST',
path: '/',
payload: 'jpeg',
headers: { 'content-type': 'image/jpeg' }
})
t.same(statusCode, 200)
t.same(headers['content-type'], 'image/jpeg')
t.same(payload, 'jpeg')
}
{
const { payload, headers, statusCode } = await fastify.inject({
method: 'POST',
path: '/',
payload: 'png',
headers: { 'content-type': 'image/png' }
})
t.same(statusCode, 200)
t.same(headers['content-type'], 'image/png')
t.same(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')
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: ''
})
t.pass('end')
})

43
node_modules/fastify/test/content-type.test.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
test('should remove content-type for setErrorHandler', async t => {
t.plan(8)
let count = 0
const fastify = Fastify()
fastify.setErrorHandler(function (error, request, reply) {
t.same(error.message, 'kaboom')
t.same(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')
switch (count) {
case 1: {
// should guess the correct content-type based on payload
t.same(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')
return payload
}
default: {
t.fail('should not reach')
}
}
})
fastify.get('/', function (request, reply) {
reply.send('plain-text')
})
const { statusCode, body } = await fastify.inject({ method: 'GET', path: '/' })
t.same(statusCode, 400)
t.same(body, JSON.stringify({ foo: 'bar' }))
})

174
node_modules/fastify/test/context-config.test.js generated vendored Normal file
View File

@@ -0,0 +1,174 @@
'use strict'
const t = require('tap')
const test = t.test
const { kRouteContext } = require('../lib/symbols')
const Fastify = require('..')
const schema = {
schema: { },
config: {
value1: 'foo',
value2: true
}
}
function handler (req, reply) {
reply.send(reply[kRouteContext].config)
}
test('config', t => {
t.plan(9)
const fastify = Fastify()
fastify.get('/get', {
schema: schema.schema,
config: Object.assign({}, schema.config)
}, handler)
fastify.route({
method: 'GET',
url: '/route',
schema: schema.schema,
handler,
config: Object.assign({}, schema.config)
})
fastify.route({
method: 'GET',
url: '/no-config',
schema: schema.schema,
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({
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({
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' })
})
})
test('config with exposeHeadRoutes', t => {
t.plan(9)
const fastify = Fastify({ exposeHeadRoutes: true })
fastify.get('/get', {
schema: schema.schema,
config: Object.assign({}, schema.config)
}, handler)
fastify.route({
method: 'GET',
url: '/route',
schema: schema.schema,
handler,
config: Object.assign({}, schema.config)
})
fastify.route({
method: 'GET',
url: '/no-config',
schema: schema.schema,
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({
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({
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' })
})
})
test('config without exposeHeadRoutes', t => {
t.plan(9)
const fastify = Fastify({ exposeHeadRoutes: false })
fastify.get('/get', {
schema: schema.schema,
config: Object.assign({}, schema.config)
}, handler)
fastify.route({
method: 'GET',
url: '/route',
schema: schema.schema,
handler,
config: Object.assign({}, schema.config)
})
fastify.route({
method: 'GET',
url: '/no-config',
schema: schema.schema,
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({
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({
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' })
})
})

48
node_modules/fastify/test/copy.test.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
'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)
})
})
})

130
node_modules/fastify/test/custom-http-server.test.js generated vendored Normal file
View File

@@ -0,0 +1,130 @@
'use strict'
const t = require('tap')
const test = t.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')
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)
const fastify = Fastify({
serverFactory: (handler, opts) => {
t.ok(opts.serverFactory, 'it is called once for localhost')
const server = http.createServer((req, res) => {
req.custom = true
handler(req, res)
})
return server
}
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/', (req, reply) => {
t.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()
})
})
})
test('Should not allow forceCloseConnection=idle if the server does not support closeIdleConnections', t => {
t.plan(1)
t.throws(
() => {
Fastify({
forceCloseConnections: 'idle',
serverFactory (handler, opts) {
return {
on () {
}
}
}
})
},
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,
"Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
)
})
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({
serverFactory: () => server
})
t.resolves(async () => {
await app.listen({ port: 0 })
})
})
test('Should not call close on the server if it has not created it', async t => {
const server = http.createServer()
const serverFactory = (handler, opts) => {
server.on('request', handler)
return server
}
const fastify = Fastify({ serverFactory })
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await fastify.ready()
await new Promise((resolve, reject) => {
server.listen(0)
server.on('listening', resolve)
server.on('error', reject)
})
const address = server.address()
t.equal(server.listening, true)
await fastify.close()
t.equal(server.listening, true)
t.same(server.address(), address)
t.same(fastify.addresses(), [address])
await new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
return reject(err)
}
resolve()
})
})
t.equal(server.listening, false)
t.same(server.address(), null)
})
}
setup()

66
node_modules/fastify/test/custom-parser-async.test.js generated vendored Normal file
View File

@@ -0,0 +1,66 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
process.removeAllListeners('warning')
test('contentTypeParser should add a custom async parser', t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.options('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/jsoff', async function (req, payload) {
const res = await new Promise((resolve, reject) => resolve(payload))
return res
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => fastify.close())
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' }))
})
})
t.test('in OPTIONS', t => {
t.plan(3)
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' }))
})
})
})
})

751
node_modules/fastify/test/custom-parser.0.test.js generated vendored Normal file
View File

@@ -0,0 +1,751 @@
'use strict'
const fs = require('node:fs')
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl, plainTextParser } = require('./helper')
process.removeAllListeners('warning')
test('contentTypeParser method should exist', t => {
t.plan(1)
const fastify = Fastify()
t.ok(fastify.addContentTypeParser)
})
test('contentTypeParser should add a custom parser', t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.options('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => fastify.close())
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' }))
})
})
t.test('in OPTIONS', t => {
t.plan(3)
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' }))
})
})
})
})
test('contentTypeParser should handle multiple custom parsers', t => {
t.plan(7)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.post('/hello', (req, reply) => {
reply.send(req.body)
})
function customParser (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
}
fastify.addContentTypeParser('application/jsoff', customParser)
fastify.addContentTypeParser('application/ffosj', customParser)
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { 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' }))
})
})
})
test('contentTypeParser should handle an array of custom contentTypes', t => {
t.plan(7)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.post('/hello', (req, reply) => {
reply.send(req.body)
})
function customParser (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
}
fastify.addContentTypeParser(['application/jsoff', 'application/ffosj'], customParser)
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { 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' }))
})
})
})
test('contentTypeParser should handle errors', t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
done(new Error('kaboom!'), {})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('contentTypeParser should support encapsulation', t => {
t.plan(6)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
instance.addContentTypeParser('application/jsoff', () => {})
t.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'))
done()
})
done()
})
fastify.ready(err => {
t.error(err)
t.notOk(fastify.hasContentTypeParser('application/jsoff'))
t.notOk(fastify.hasContentTypeParser('application/ffosj'))
})
})
test('contentTypeParser should support encapsulation, second try', t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
instance.post('/', (req, reply) => {
reply.send(req.body)
})
instance.addContentTypeParser('application/jsoff', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('contentTypeParser shouldn\'t support request with undefined "Content-Type"', t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('the content type should be a string or RegExp', t => {
t.plan(2)
const fastify = Fastify()
try {
fastify.addContentTypeParser(null, () => {})
t.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')
}
})
test('the content type cannot be an empty string', t => {
t.plan(2)
const fastify = Fastify()
try {
fastify.addContentTypeParser('', () => {})
t.fail()
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_EMPTY_TYPE')
t.equal(err.message, 'The content type cannot be an empty string')
}
})
test('the content type handler should be a function', t => {
t.plan(2)
const fastify = Fastify()
try {
fastify.addContentTypeParser('aaa', null)
t.fail()
} catch (err) {
t.equal(err.code, 'FST_ERR_CTP_INVALID_HANDLER')
t.equal(err.message, 'The content type handler should be a function')
}
})
test('catch all content type parser', t => {
t.plan(7)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('*', function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
})
test('catch all content type parser should not interfere with other conte type parsers', t => {
t.plan(7)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('*', function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
})
// Issue 492 https://github.com/fastify/fastify/issues/492
test('\'*\' catch undefined Content-Type requests', t => {
t.plan(4)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.addContentTypeParser('*', function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
fastify.post('/', (req, res) => {
// Needed to avoid json stringify
res.type('text/plain').send(req.body)
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
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())
})
})
})
test('cannot add custom parser after binding', t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.post('/', (req, res) => {
res.type('text/plain').send(req.body)
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
try {
fastify.addContentTypeParser('*', () => {})
t.fail()
} catch (e) {
t.pass()
}
})
})
test('Can override the default json parser', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Can override the default plain text parser', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.ok('called')
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Can override the default json parser in a plugin', t => {
t.plan(5)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
instance.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
instance.post('/', (req, reply) => {
reply.send(req.body)
})
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Can\'t override the json parser multiple times', t => {
t.plan(2)
const fastify = Fastify()
fastify.addContentTypeParser('application/json', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
try {
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.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.')
}
})
test('Can\'t override the plain text parser multiple times', t => {
t.plan(2)
const fastify = Fastify()
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
try {
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.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.')
}
})
test('Should get the body as string', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
t.ok('called')
t.ok(typeof body === 'string')
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should return defined body with no custom parser defined and content type = \'text/plain\'', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should have typeof body object with no custom parser defined, no body defined and content type = \'text/plain\'', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})

298
node_modules/fastify/test/custom-parser.1.test.js generated vendored Normal file
View File

@@ -0,0 +1,298 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
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)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should have typeof body object with no custom parser defined, undefined body and content type = \'text/plain\'', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should get the body as string', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, function (req, body, done) {
t.ok('called')
t.ok(typeof body === 'string')
try {
const plainText = body
done(null, plainText)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should get the body as buffer', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, function (req, body, done) {
t.ok('called')
t.ok(body instanceof Buffer)
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should get the body as buffer', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('text/plain', { parseAs: 'buffer' }, function (req, body, done) {
t.ok('called')
t.ok(body instanceof Buffer)
try {
const plainText = body
done(null, plainText)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('Should parse empty bodies as a string', t => {
t.plan(9)
const fastify = Fastify()
fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, (req, body, done) => {
t.equal(body, '')
done(null, body)
})
fastify.route({
method: ['POST', 'DELETE'],
url: '/',
handler (request, reply) {
reply.send(request.body)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { 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(), '')
})
})
})
test('Should parse empty bodies as a buffer', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('text/plain', { parseAs: 'buffer' }, function (req, body, done) {
t.ok(body instanceof Buffer)
t.equal(body.length, 0)
done(null, body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('The charset should not interfere with the content type handling', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})

102
node_modules/fastify/test/custom-parser.2.test.js generated vendored Normal file
View File

@@ -0,0 +1,102 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
const { getServerUrl } = require('./helper')
process.removeAllListeners('warning')
test('Wrong parseAs parameter', t => {
t.plan(2)
const fastify = Fastify()
try {
fastify.addContentTypeParser('application/json', { parseAs: 'fireworks' }, () => {})
t.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.")
}
})
test('Should allow defining the bodyLimit per parser', t => {
t.plan(3)
const fastify = Fastify()
t.teardown(() => fastify.close())
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser(
'x/foo',
{ parseAs: 'string', bodyLimit: 5 },
function (req, body, done) {
t.fail('should not be invoked')
done()
}
)
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('route bodyLimit should take precedence over a custom parser bodyLimit', t => {
t.plan(3)
const fastify = Fastify()
t.teardown(() => fastify.close())
fastify.post('/', { bodyLimit: 5 }, (request, reply) => {
reply.send(request.body)
})
fastify.addContentTypeParser(
'x/foo',
{ parseAs: 'string', bodyLimit: 100 },
function (req, body, done) {
t.fail('should not be invoked')
done()
}
)
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})

245
node_modules/fastify/test/custom-parser.3.test.js generated vendored Normal file
View File

@@ -0,0 +1,245 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
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)
const fastify = Fastify()
t.teardown(() => fastify.close())
fastify.post('/', (request, reply) => {
reply.send(request.body)
})
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('contentTypeParser should add a custom parser with RegExp value', t => {
t.plan(3)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.options('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser(/.*\+json$/, function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => fastify.close())
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' }))
})
})
t.test('in OPTIONS', 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' }))
})
})
})
})
test('contentTypeParser should add multiple custom parsers with RegExp values', async t => {
t.plan(6)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser(/.*\+json$/, function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.addContentTypeParser(/.*\+xml$/, function (req, payload, done) {
done(null, 'xml')
})
fastify.addContentTypeParser(/.*\+myExtension$/i, function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data + 'myExtension')
})
})
await fastify.ready()
{
const response = await fastify.inject({
method: 'POST',
url: '/',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/vnd.hello+json'
}
})
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '{"hello":"world"}')
}
{
const response = await fastify.inject({
method: 'POST',
url: '/',
body: '{"hello":"world"}',
headers: {
'Content-Type': 'application/test+xml'
}
})
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), 'xml')
}
await fastify.inject({
method: 'POST',
path: '/',
payload: 'abcdefg',
headers: {
'Content-Type': 'application/+myExtension'
}
}).then((response) => {
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), 'abcdefgmyExtension')
}).catch((err) => {
t.error(err)
})
})
test('catch all content type parser should not interfere with content type parser', t => {
t.plan(10)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser('*', function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
fastify.addContentTypeParser(/^application\/.*/, function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.addContentTypeParser('text/html', function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data + 'html')
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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' }))
})
sget({
method: 'POST',
url: getServerUrl(fastify),
body: 'body',
headers: {
'Content-Type': 'very-weird-content-type'
}
}, (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')
})
})
})

239
node_modules/fastify/test/custom-parser.4.test.js generated vendored Normal file
View File

@@ -0,0 +1,239 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
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)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.addContentTypeParser(/^application\/.*/, function (req, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
fastify.addContentTypeParser('application/json', function (req, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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')
})
})
})
test('removeContentTypeParser should support arrays of content types to remove', t => {
t.plan(8)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
payload.on('data', () => {})
payload.on('end', () => {
done(null, 'xml')
})
})
fastify.addContentTypeParser(/^image\/.*/, function (req, payload, done) {
payload.on('data', () => {})
payload.on('end', () => {
done(null, 'image')
})
})
fastify.removeContentTypeParser([/^image\/.*/, 'application/json'])
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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)
})
})
})
test('removeContentTypeParser should support encapsulation', t => {
t.plan(6)
const fastify = Fastify()
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
payload.on('data', () => {})
payload.on('end', () => {
done(null, 'xml')
})
})
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.register(function (instance, options, done) {
instance.removeContentTypeParser('application/xml')
instance.post('/encapsulated', (req, reply) => {
reply.send(req.body)
})
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('removeAllContentTypeParsers should support encapsulation', t => {
t.plan(6)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.register(function (instance, options, done) {
instance.removeAllContentTypeParsers()
instance.post('/encapsulated', (req, reply) => {
reply.send(req.body)
})
done()
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})

149
node_modules/fastify/test/custom-parser.5.test.js generated vendored Normal file
View File

@@ -0,0 +1,149 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../fastify')
const jsonParser = require('fast-json-body')
const { getServerUrl, plainTextParser } = require('./helper')
process.removeAllListeners('warning')
test('cannot remove all content type parsers after binding', t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.throws(() => fastify.removeAllContentTypeParsers())
})
})
test('cannot remove content type parsers after binding', t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.listen({ port: 0 }, function (err) {
t.error(err)
t.throws(() => fastify.removeContentTypeParser('application/json'))
})
})
test('should be able to override the default json parser after removeAllContentTypeParsers', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/json', function (req, payload, done) {
t.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('should be able to override the default plain text parser after removeAllContentTypeParsers', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/plain', function (req, payload, done) {
t.ok('called')
plainTextParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})
test('should be able to add a custom content type parser after removeAllContentTypeParsers', t => {
t.plan(5)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {
t.ok('called')
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.listen({ port: 0 }, err => {
t.error(err)
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()
})
})
})

View File

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

1213
node_modules/fastify/test/decorator.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

88
node_modules/fastify/test/default-route.test.js generated vendored Normal file
View File

@@ -0,0 +1,88 @@
'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')
})
})

321
node_modules/fastify/test/delete.test.js generated vendored Normal file
View File

@@ -0,0 +1,321 @@
'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 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'
}
}
}
}
}
const bodySchema = {
schema: {
body: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
},
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
test('shorthand - delete', t => {
t.plan(1)
try {
fastify.delete('/', schema, function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - delete params', t => {
t.plan(1)
try {
fastify.delete('/params/:foo/:test', paramsSchema, function (req, reply) {
reply.code(200).send(req.params)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - delete, querystring schema', t => {
t.plan(1)
try {
fastify.delete('/query', querySchema, function (req, reply) {
reply.send(req.query)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('shorthand - get, headers schema', t => {
t.plan(1)
try {
fastify.delete('/headers', headersSchema, function (req, reply) {
reply.code(200).send(req.headers)
})
t.pass()
} catch (e) {
t.fail()
}
})
test('missing schema - delete', t => {
t.plan(1)
try {
fastify.delete('/missing', function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
t.pass()
} catch (e) {
t.fail()
}
})
test('body - delete', t => {
t.plan(1)
try {
fastify.delete('/body', bodySchema, function (req, reply) {
reply.send(req.body)
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
test('shorthand - request delete', 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' })
})
})
test('shorthand - request delete params schema', 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 })
})
})
test('shorthand - request delete params schema error', 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
})
})
})
test('shorthand - request delete headers schema', t => {
t.plan(4)
sget({
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)
})
})
test('shorthand - request delete headers schema error', t => {
t.plan(3)
sget({
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
})
})
})
test('shorthand - request delete querystring schema', 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 })
})
})
test('shorthand - request delete querystring schema error', 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
})
})
})
test('shorthand - request delete missing schema', 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' })
})
})
test('shorthand - delete with body', t => {
t.plan(3)
sget({
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' })
})
})
})
// https://github.com/fastify/fastify/issues/936
test('shorthand - delete with application/json Content-Type header and without body', t => {
t.plan(4)
const fastify = require('..')()
fastify.delete('/', {}, (req, reply) => {
t.equal(req.body, undefined)
reply.send(req.body)
})
fastify.inject({
method: 'DELETE',
url: '/',
headers: { 'Content-Type': 'application/json' },
body: null
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(response.payload.toString(), '')
})
})

61
node_modules/fastify/test/diagnostics-channel.test.js generated vendored Normal file
View File

@@ -0,0 +1,61 @@
'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,69 @@
'use strict'
const { test } = require('tap')
const Fastify = require('..')
const fp = require('fastify-plugin')
test('encapsulates an child logger factory', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.customLog = function (message) {
t.equal(message, 'custom')
}
return child
})
fastify.get('/encapsulated', async (req) => {
req.log.customLog('custom')
})
})
fastify.setChildLoggerFactory(function globalFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.globalLog = function (message) {
t.equal(message, 'global')
}
return child
})
fastify.get('/not-encapsulated', async (req) => {
req.log.globalLog('global')
})
const res1 = await fastify.inject('/encapsulated')
t.equal(res1.statusCode, 200)
const res2 = await fastify.inject('/not-encapsulated')
t.equal(res2.statusCode, 200)
})
test('child logger factory set on root scope when using fastify-plugin', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register(fp(async function (fastify) {
// Using fastify-plugin, the factory should be set on the root scope
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
const child = logger.child(bindings, opts)
child.customLog = function (message) {
t.equal(message, 'custom')
}
return child
})
fastify.get('/not-encapsulated-1', async (req) => {
req.log.customLog('custom')
})
}))
fastify.get('/not-encapsulated-2', async (req) => {
req.log.customLog('custom')
})
const res1 = await fastify.inject('/not-encapsulated-1')
t.equal(res1.statusCode, 200)
const res2 = await fastify.inject('/not-encapsulated-2')
t.equal(res2.statusCode, 200)
})

View File

@@ -0,0 +1,50 @@
'use strict'
const { test } = require('tap')
const Fastify = require('..')
test('encapsulates an 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')
})
fastify.get('/encapsulated', async () => { throw new Error('kaboom') })
})
fastify.setErrorHandler(async function b (err) {
t.equal(err.message, 'caught')
throw new Error('wrapped')
})
const res = await fastify.inject('/encapsulated')
t.equal(res.json().message, 'wrapped')
})
test('onError hook nested', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register(async function (fastify) {
fastify.setErrorHandler(async function a (err) {
t.equal(err.message, 'kaboom')
throw new Error('caught')
})
fastify.get('/encapsulated', async () => { throw new Error('kaboom') })
})
fastify.setErrorHandler(async function b (err) {
t.equal(err.message, 'caught')
throw new Error('wrapped')
})
fastify.addHook('onError', async function (request, reply, err) {
t.equal(err.message, 'kaboom')
})
const res = await fastify.inject('/encapsulated')
t.equal(res.json().message, 'wrapped')
})

10
node_modules/fastify/test/esm/errorCodes.test.mjs generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import { errorCodes } from '../../fastify.js'
import t from 'tap'
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')
})

13
node_modules/fastify/test/esm/esm.test.mjs generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import t from 'tap'
import Fastify from '../../fastify.js'
t.test('esm support', async t => {
const fastify = Fastify()
fastify.register(import('./plugin.mjs'), { foo: 'bar' })
fastify.register(import('./other.mjs'))
await fastify.ready()
t.equal(fastify.foo, 'bar')
})

18
node_modules/fastify/test/esm/index.test.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
'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) => {
process.nextTick(() => {
throw err
})
})
}

14
node_modules/fastify/test/esm/named-exports.mjs generated vendored Normal file
View File

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

8
node_modules/fastify/test/esm/other.mjs generated vendored Normal file
View File

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

8
node_modules/fastify/test/esm/plugin.mjs generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// Imported in both index.test.js & esm.test.mjs
async function plugin (fastify, opts) {
fastify.decorate('foo', opts.foo)
}
plugin[Symbol.for('skip-override')] = true
export default plugin

291
node_modules/fastify/test/fastify-instance.test.js generated vendored Normal file
View File

@@ -0,0 +1,291 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const os = require('node:os')
const {
kOptions,
kErrorHandler,
kChildLoggerFactory
} = require('../lib/symbols')
test('root fastify instance is an object', t => {
t.plan(1)
t.type(Fastify(), 'object')
})
test('fastify instance should contains ajv options', t => {
t.plan(1)
const fastify = Fastify({
ajv: {
customOptions: {
nullable: false
}
}
})
t.same(fastify[kOptions].ajv, {
customOptions: {
nullable: false
},
plugins: []
})
})
test('fastify instance should contains ajv options.plugins nested arrays', t => {
t.plan(1)
const fastify = Fastify({
ajv: {
customOptions: {
nullable: false
},
plugins: [[]]
}
})
t.same(fastify[kOptions].ajv, {
customOptions: {
nullable: false
},
plugins: [[]]
})
})
test('fastify instance get invalid ajv options', t => {
t.plan(1)
t.throws(() => Fastify({
ajv: {
customOptions: 8
}
}))
})
test('fastify instance get invalid ajv options.plugins', t => {
t.plan(1)
t.throws(() => Fastify({
ajv: {
customOptions: {},
plugins: 8
}
}))
})
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)
})
test('errorHandler in plugin should be separate from the external one', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
const inPluginErrHandler = (_, __, reply) => {
reply.send({ plugin: 'error-object' })
}
instance.setErrorHandler(inPluginErrHandler)
t.notSame(instance.errorHandler, fastify.errorHandler)
t.equal(instance.errorHandler.name, 'bound inPluginErrHandler')
done()
})
await fastify.ready()
t.ok(fastify[kErrorHandler].func instanceof Function)
t.same(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)
})
test('childLoggerFactory in plugin should be separate from the external one', async t => {
t.plan(4)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
const inPluginLoggerFactory = function (logger, bindings, opts) {
return logger.child(bindings, opts)
}
instance.setChildLoggerFactory(inPluginLoggerFactory)
t.notSame(instance.childLoggerFactory, fastify.childLoggerFactory)
t.equal(instance.childLoggerFactory.name, 'inPluginLoggerFactory')
done()
})
await fastify.ready()
t.ok(fastify[kChildLoggerFactory] instanceof Function)
t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
})
test('ready should resolve in order when called multiply times (promises only)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5]
const result = []
const promises = [1, 2, 3, 4, 5]
.map((id) => app.ready().then(() => result.push(id)))
await Promise.all(promises)
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (promises only)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5]
const result = []
app.register((instance, opts, done) => {
setTimeout(() => done(new Error('test')), 500)
})
const promises = [1, 2, 3, 4, 5]
.map((id) => app.ready().catch(() => result.push(id)))
await Promise.all(promises)
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (callbacks only)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5]
const result = []
app.register((instance, opts, done) => {
setTimeout(() => done(new Error('test')), 500)
})
expectedOrder.map((id) => app.ready(() => result.push(id)))
await app.ready().catch(err => {
t.equal(err.message, 'test')
})
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (callbacks only)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5]
const result = []
expectedOrder.map((id) => app.ready(() => result.push(id)))
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (mixed)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5, 6]
const result = []
for (const order of expectedOrder) {
if (order % 2) {
app.ready(() => result.push(order))
} else {
app.ready().then(() => result.push(order))
}
}
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should reject in order when called multiply times (mixed)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5, 6]
const result = []
app.register((instance, opts, done) => {
setTimeout(() => done(new Error('test')), 500)
})
for (const order of expectedOrder) {
if (order % 2) {
app.ready(() => result.push(order))
} else {
app.ready().then(null, () => result.push(order))
}
}
await app.ready().catch(err => {
t.equal(err.message, 'test')
})
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('ready should resolve in order when called multiply times (mixed)', async (t) => {
const app = Fastify()
const expectedOrder = [1, 2, 3, 4, 5, 6]
const result = []
for (const order of expectedOrder) {
if (order % 2) {
app.ready().then(() => result.push(order))
} else {
app.ready(() => result.push(order))
}
}
await app.ready()
t.strictSame(result, expectedOrder, 'Should resolve in order')
})
test('fastify instance should contains listeningOrigin property (with port and host)', async t => {
t.plan(1)
const port = 3000
const host = '127.0.0.1'
const fastify = Fastify()
await fastify.listen({ port, host })
t.same(fastify.listeningOrigin, `http://${host}:${port}`)
await fastify.close()
})
test('fastify instance should contains listeningOrigin property (with port and https)', async t => {
t.plan(1)
const port = 3000
const host = '127.0.0.1'
const fastify = Fastify({ https: {} })
await fastify.listen({ port, host })
t.same(fastify.listeningOrigin, `https://${host}:${port}`)
await fastify.close()
})
test('fastify instance should contains listeningOrigin property (unix socket)', { skip: os.platform() === 'win32' }, async t => {
const fastify = Fastify()
const path = `fastify.${Date.now()}.sock`
await fastify.listen({ path })
t.same(fastify.listeningOrigin, path)
await fastify.close()
})
test('fastify instance should contains listeningOrigin property (IPv6)', 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}`)
await fastify.close()
})

135
node_modules/fastify/test/findRoute.test.js generated vendored Normal file
View File

@@ -0,0 +1,135 @@
'use strict'
const { test } = require('tap')
const Fastify = require('..')
const fastifyPlugin = require('fastify-plugin')
test('findRoute should return null when route cannot be found due to a different method', 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.equal(fastify.findRoute({
method: 'POST',
url: '/artists/:artistId'
}), null)
})
test('findRoute should return an immutable route to avoid leaking and runtime route modifications', 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)
})
let route = fastify.findRoute({
method: 'GET',
url: '/artists/:artistId'
})
route.params = {
...route.params,
id: ':id'
}
route = fastify.findRoute({
method: 'GET',
url: '/artists/:artistId'
})
t.same(route.params, { artistId: ':artistId' })
})
test('findRoute should return null when route cannot be found due to a different path', 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.equal(fastify.findRoute({
method: 'GET',
url: '/books/:bookId'
}), null)
})
test('findRoute should return the route when found', t => {
t.plan(1)
const fastify = Fastify()
const handler = (req, reply) => reply.send(typeof req.params.artistId)
fastify.get('/artists/:artistId', {
schema: {
params: { artistId: { type: 'integer' } }
},
handler
})
const route = fastify.findRoute({
method: 'GET',
url: '/artists/:artistId'
})
t.same(route.params, { artistId: ':artistId' })
})
test('findRoute should work correctly when used within plugins', t => {
t.plan(1)
const fastify = Fastify()
const handler = (req, reply) => reply.send(typeof req.params.artistId)
fastify.get('/artists/:artistId', {
schema: {
params: { artistId: { type: 'integer' } }
},
handler
})
function validateRoutePlugin (instance, opts, done) {
const validateParams = function () {
return instance.findRoute({
method: 'GET',
url: '/artists/:artistId'
}) !== null
}
instance.decorate('validateRoutes', { validateParams })
done()
}
fastify.register(fastifyPlugin(validateRoutePlugin))
fastify.ready(() => {
t.equal(fastify.validateRoutes.validateParams(), true)
})
})
test('findRoute should not expose store', 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)
})
const route = fastify.findRoute({
method: 'GET',
url: '/artists/:artistId'
})
t.equal(route.store, undefined)
})

212
node_modules/fastify/test/fluent-schema.test.js generated vendored Normal file
View File

@@ -0,0 +1,212 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('..')
const S = require('fluent-json-schema')
test('use fluent-json-schema object', t => {
t.plan(15)
const fastify = Fastify()
fastify.post('/:id', {
handler: (req, reply) => { reply.send({ name: 'a', surname: 'b', dateOfBirth: '01-01-2020' }) },
schema: {
params: S.object().prop('id', S.integer().minimum(42)),
headers: S.object().prop('x-custom', S.string().format('email')),
query: S.object().prop('surname', S.string().required()),
body: S.object().prop('name', S.string().required()),
response: {
200: S.object()
.prop('name', S.string())
.prop('surname', S.string())
}
}
})
// check params
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' })
})
// check header
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"' })
})
// check query
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\'' })
})
// check body
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' })
})
// check response
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' })
})
})
test('use complex fluent-json-schema object', t => {
t.plan(1)
const fastify = Fastify()
const addressSchema = S.object()
.id('#address')
.prop('line1').required()
.prop('line2')
.prop('country').required()
.prop('city').required()
.prop('zipcode').required()
const commonSchemas = S.object()
.id('https://fastify/demo')
.definition('addressSchema', addressSchema)
fastify.addSchema(commonSchemas)
const bodyJsonSchema = S.object()
.prop('residence', S.ref('https://fastify/demo#address')).required()
.prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required()
fastify.post('/the/url', { schema: { body: bodyJsonSchema } }, () => { })
fastify.ready(err => t.error(err))
})
test('use fluent schema and plain JSON schema', t => {
t.plan(1)
const fastify = Fastify()
const addressSchema = S.object()
.id('#address')
.prop('line1').required()
.prop('line2')
.prop('country').required()
.prop('city').required()
.prop('zipcode').required()
const commonSchemas = S.object()
.id('https://fastify/demo')
.definition('addressSchema', addressSchema)
const sharedAddressSchema = {
$id: 'sharedAddress',
type: 'object',
required: ['line1', 'country', 'city', 'zipcode'],
properties: {
line1: { type: 'string' },
line2: { type: 'string' },
country: { type: 'string' },
city: { type: 'string' },
zipcode: { type: 'string' }
}
}
fastify.addSchema(commonSchemas)
fastify.addSchema(sharedAddressSchema)
const bodyJsonSchema = S.object()
.prop('residence', S.ref('https://fastify/demo#address')).required()
.prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required()
fastify.post('/the/url', { schema: { body: bodyJsonSchema } }, () => { })
fastify.ready(err => t.error(err))
})
test('Should call valueOf internally', t => {
t.plan(1)
const fastify = new Fastify()
const addressSchema = S.object()
.id('#address')
.prop('line1').required()
.prop('line2')
.prop('country').required()
.prop('city').required()
.prop('zipcode').required()
const commonSchemas = S.object()
.id('https://fastify/demo')
.definition('addressSchema', addressSchema)
fastify.addSchema(commonSchemas)
fastify.route({
method: 'POST',
url: '/query',
handler: () => {},
schema: {
query: S.object().prop('hello', S.string()).required(),
body: S.object().prop('hello', S.string()).required(),
params: S.object().prop('hello', S.string()).required(),
headers: S.object().prop('hello', S.string()).required(),
response: {
200: S.object().prop('hello', S.string()).required(),
201: S.object().prop('hello', S.string()).required()
}
}
})
fastify.route({
method: 'POST',
url: '/querystring',
handler: () => {},
schema: {
querystring: S.object().prop('hello', S.string()).required(),
body: S.object().prop('hello', S.string()).required(),
params: S.object().prop('hello', S.string()).required(),
headers: S.object().prop('hello', S.string()).required(),
response: {
200: S.object().prop('hello', S.string()).required(),
201: S.object().prop('hello', S.string()).required()
}
}
})
fastify.ready(t.error)
})

466
node_modules/fastify/test/genReqId.test.js generated vendored Normal file
View File

@@ -0,0 +1,466 @@
'use strict'
const { Readable } = require('node:stream')
const { test } = require('tap')
const fp = require('fastify-plugin')
const Fastify = require('..')
test('Should accept a custom genReqId function', t => {
t.plan(4)
const fastify = Fastify({
genReqId: function (req) {
return 'a'
}
})
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'a')
fastify.close()
})
})
})
test('Custom genReqId function gets raw request as argument', t => {
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)
// 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)
return xReqId
}
})
fastify.get('/', (req, reply) => {
t.equal(req.id, REQUEST_ID)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.inject({
method: 'GET',
headers: {
'x-request-id': REQUEST_ID
},
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, REQUEST_ID)
fastify.close()
})
})
})
test('Should accept option to set genReqId with setGenReqId option', t => {
t.plan(9)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
}
})
fastify.register(function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}, { prefix: 'foo' })
fastify.register(function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}, { prefix: 'bar' })
fastify.get('/', (req, reply) => {
t.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()
})
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: '/bar'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
})
})
test('Should encapsulate setGenReqId', t => {
t.plan(12)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
}
})
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)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
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'
}
})
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)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
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 not alter parent of genReqId', t => {
t.plan(6)
const fastify = Fastify()
const fooInstance = function (instance, opts, next) {
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)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'req-1')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
})
test('Should have child instance user parent genReqId', t => {
t.plan(6)
const fastify = Fastify({
genReqId: function (req) {
return 'foo'
}
})
const fooInstance = function (instance, opts, next) {
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)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
})
test('genReqId set on root scope when using fastify-plugin', t => {
t.plan(6)
const fastify = Fastify()
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)
reply.send({ id: req.id })
})
done()
}))
fastify.get('/not-encapsulated-2', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/not-encapsulated-1'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/not-encapsulated-2'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
})
})

383
node_modules/fastify/test/get.test.js generated vendored Normal file
View File

@@ -0,0 +1,383 @@
'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')
})
})
})

45
node_modules/fastify/test/handler-context.test.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
'use strict'
const test = require('tap').test
const { kRouteContext } = require('../lib/symbols')
const fastify = require('../')
test('handlers receive correct `this` context', async (t) => {
t.plan(4)
// simulate plugin that uses fastify-plugin
const plugin = function (instance, opts, done) {
instance.decorate('foo', 'foo')
done()
}
plugin[Symbol.for('skip-override')] = true
const instance = fastify()
instance.register(plugin)
instance.get('/', function (req, reply) {
t.ok(this.foo)
t.equal(this.foo, 'foo')
reply.send()
})
await instance.inject('/')
t.ok(instance.foo)
t.equal(instance.foo, 'foo')
})
test('handlers have access to the internal context', async (t) => {
t.plan(5)
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')
reply.send()
})
await instance.inject('/')
})

93
node_modules/fastify/test/has-route.test.js generated vendored Normal file
View File

@@ -0,0 +1,93 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../fastify')
test('hasRoute', t => {
t.plan(5)
const test = t.test
const fastify = Fastify()
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)
})
test('hasRoute - primitive method', t => {
t.plan(2)
fastify.route({
method: 'GET',
url: '/',
handler: function (req, reply) {
reply.send({ hello: 'world' })
}
})
t.equal(fastify.hasRoute({
method: 'GET',
url: '/'
}), true)
t.equal(fastify.hasRoute({
method: 'POST',
url: '/'
}), false)
})
test('hasRoute - with constraints', t => {
t.plan(2)
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' },
handler: (req, reply) => {
reply.send({ hello: 'world' })
}
})
t.equal(fastify.hasRoute({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' }
}), true)
t.equal(fastify.hasRoute({
method: 'GET',
url: '/',
constraints: { version: '1.3.0' }
}), false)
})
test('hasRoute - parametric route regexp with constraints', t => {
t.plan(1)
// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
t.equal(fastify.hasRoute({
method: 'GET',
url: '/example/12345.png'
}), true)
})
test('hasRoute - finds a route even if method is not uppercased', t => {
t.plan(1)
fastify.route({
method: 'GET',
url: '/equal',
handler: function (req, reply) {
reply.send({ hello: 'world' })
}
})
t.equal(fastify.hasRoute({
method: 'get',
url: '/equal'
}), true)
})
})

368
node_modules/fastify/test/head.test.js generated vendored Normal file
View File

@@ -0,0 +1,368 @@
'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)
})
})
})

65
node_modules/fastify/test/header-overflow.test.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
'use strict'
const t = require('tap')
const test = t.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)
const fastify = Fastify({ http: { maxHeaderSize } })
fastify.route({
method: 'GET',
url: '/',
handler: (_req, reply) => {
reply.send({ hello: 'world' })
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
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)
})
})
t.teardown(() => fastify.close())
})
test('Should return 431 if URI is too long', t => {
t.plan(3)
const fastify = Fastify({ http: { maxHeaderSize } })
fastify.route({
method: 'GET',
url: '/',
handler: (_req, reply) => {
reply.send({ hello: 'world' })
}
})
fastify.listen({ port: 0 }, function (err) {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + `/${'a'.repeat(maxHeaderSize)}`
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 431)
})
})
t.teardown(() => fastify.close())
})

458
node_modules/fastify/test/helper.js generated vendored Normal file
View File

@@ -0,0 +1,458 @@
'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')
module.exports.sleep = promisify(setTimeout)
/**
* @param method HTTP request method
* @param t tap instance
* @param isSetErrorHandler true: using setErrorHandler
*/
module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
const test = t.test
const fastify = require('..')()
if (isSetErrorHandler) {
fastify.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
reply
.code(err.statusCode)
.type('application/json; charset=utf-8')
.send(err)
})
}
const upMethod = method.toUpperCase()
const loMethod = method.toLowerCase()
const schema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
test(`${upMethod} can be created`, t => {
t.plan(1)
try {
fastify[loMethod]('/', schema, function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
} catch (e) {
t.fail()
}
})
test(`${upMethod} without schema can be created`, t => {
t.plan(1)
try {
fastify[loMethod]('/missing', function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
} catch (e) {
t.fail()
}
})
test(`${upMethod} with body and querystring`, t => {
t.plan(1)
try {
fastify[loMethod]('/with-query', function (req, reply) {
req.body.hello = req.body.hello + req.query.foo
reply.code(200).send(req.body)
})
t.pass()
} catch (e) {
t.fail()
}
})
test(`${upMethod} with bodyLimit option`, t => {
t.plan(1)
try {
fastify[loMethod]('/with-limit', { bodyLimit: 1 }, function (req, reply) {
reply.send(req.body)
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, function (err) {
if (err) {
t.error(err)
return
}
t.teardown(() => { fastify.close() })
test(`${upMethod} - correctly replies`, t => {
t.plan(3)
sget({
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' })
})
})
test(`${upMethod} - correctly replies with very large body`, t => {
t.plan(3)
const largeString = 'world'.repeat(13200)
sget({
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 })
})
})
test(`${upMethod} - correctly replies if the content type has the charset`, t => {
t.plan(3)
sget({
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' }))
})
})
test(`${upMethod} without schema - correctly replies`, t => {
t.plan(3)
sget({
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' })
})
})
test(`${upMethod} with body and querystring - correctly replies`, t => {
t.plan(3)
sget({
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' })
})
})
test(`${upMethod} with no body - correctly replies`, t => {
t.plan(6)
sget({
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(), '')
})
// Must use inject to make a request without a Content-Length header
fastify.inject({
method: upMethod,
url: '/missing'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), '')
})
})
test(`${upMethod} returns 415 - incorrect media type if body is not json`, 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)
}
})
})
if (loMethod === 'options') {
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', t => {
t.plan(2)
sget({
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)
})
})
}
test(`${upMethod} returns 400 - Bad Request`, t => {
t.plan(4)
sget({
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)
})
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)
})
})
test(`${upMethod} returns 413 - Payload Too Large`, t => {
t.plan(upMethod === 'OPTIONS' ? 4 : 6)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Content-Type': 'application/json',
'Content-Length': 1024 * 1024 + 1
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
})
// Node errors for OPTIONS requests with a stream body and no Content-Length header
if (upMethod !== 'OPTIONS') {
let chunk = Buffer.alloc(1024 * 1024 + 1, 0)
const largeStream = new stream.Readable({
read () {
this.push(chunk)
chunk = null
}
})
sget({
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)
})
}
sget({
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)
})
})
test(`${upMethod} should fail with empty body and application/json content-type`, t => {
if (upMethod === 'OPTIONS') return t.end()
t.plan(12)
fastify.inject({
method: `${upMethod}`,
url: '/',
headers: {
'Content-Type': 'application/json'
}
}, (err, res) => {
t.error(err)
t.same(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
})
})
sget({
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()), {
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
})
})
fastify.inject({
method: `${upMethod}`,
url: '/',
headers: {
'Content-Type': 'application/json'
},
payload: null
}, (err, res) => {
t.error(err)
t.same(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
})
})
sget({
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()), {
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
})
})
fastify.inject({
method: `${upMethod}`,
url: '/',
headers: {
'Content-Type': 'application/json'
},
payload: undefined
}, (err, res) => {
t.error(err)
t.same(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
})
})
sget({
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()), {
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
})
})
})
})
}
function lookupToIp (lookup) {
return lookup.family === 6 ? `[${lookup.address}]` : lookup.address
}
module.exports.getLoopbackHost = async () => {
const lookup = await dns.lookup('localhost')
return [lookup.address, lookupToIp(lookup)]
}
module.exports.plainTextParser = function (request, callback) {
let body = ''
request.setEncoding('utf8')
request.on('error', onError)
request.on('data', onData)
request.on('end', onEnd)
function onError (err) {
callback(err, null)
}
function onData (chunk) {
body += chunk
}
function onEnd () {
callback(null, body)
}
}
module.exports.getServerUrl = function (app) {
const { address, port } = app.server.address()
return address === '::1'
? `http://[${address}]:${port}`
: `http://${address}:${port}`
}

1075
node_modules/fastify/test/hooks-async.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1143
node_modules/fastify/test/hooks.on-listen.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

403
node_modules/fastify/test/hooks.on-ready.test.js generated vendored Normal file
View File

@@ -0,0 +1,403 @@
'use strict'
const t = require('tap')
const Fastify = require('../fastify')
const immediate = require('node:util').promisify(setImmediate)
t.test('onReady should be called in order', t => {
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')
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')
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')
})
})
})
fastify.ready(err => t.error(err))
})
t.test('onReady should be called once', async (t) => {
const app = Fastify()
let counter = 0
app.addHook('onReady', async function () {
counter++
})
const promises = [1, 2, 3, 4, 5].map((id) => app.ready().then(() => id))
const result = await Promise.race(promises)
t.strictSame(result, 1, 'Should resolve in order')
t.equal(counter, 1, 'Should call onReady only once')
})
t.test('async onReady should be called in order', async t => {
t.plan(7)
const fastify = Fastify()
let order = 0
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')
})
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')
})
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')
})
})
})
await fastify.ready()
t.pass('ready')
})
t.test('mix ready and onReady', async t => {
t.plan(2)
const fastify = Fastify()
let order = 0
fastify.addHook('onReady', async function () {
await immediate()
order++
})
await fastify.ready()
t.equal(order, 1)
await fastify.ready()
t.equal(order, 1, 'ready hooks execute once')
})
t.test('listen and onReady order', async t => {
t.plan(9)
const fastify = Fastify()
let order = 0
fastify.register((instance, opts, done) => {
instance.ready(checkOrder.bind(null, 0))
instance.addHook('onReady', checkOrder.bind(null, 4))
instance.register((subinstance, opts, done) => {
subinstance.ready(checkOrder.bind(null, 1))
subinstance.addHook('onReady', checkOrder.bind(null, 5))
subinstance.register((realSubInstance, opts, done) => {
realSubInstance.ready(checkOrder.bind(null, 2))
realSubInstance.addHook('onReady', checkOrder.bind(null, 6))
done()
})
done()
})
done()
})
fastify.addHook('onReady', checkOrder.bind(null, 3))
await fastify.ready()
t.pass('trigger the onReady')
await fastify.listen({ port: 0 })
t.pass('do not trigger the onReady')
await fastify.close()
function checkOrder (shouldbe) {
t.equal(order, shouldbe)
order++
}
})
t.test('multiple ready calls', async t => {
t.plan(11)
const fastify = Fastify()
let order = 0
fastify.register(async (instance, opts) => {
instance.ready(checkOrder.bind(null, 1))
instance.addHook('onReady', checkOrder.bind(null, 6))
await instance.register(async (subinstance, opts) => {
subinstance.ready(checkOrder.bind(null, 2))
subinstance.addHook('onReady', checkOrder.bind(null, 7))
})
t.equal(order, 0, 'ready and hooks not triggered yet')
order++
})
fastify.addHook('onReady', checkOrder.bind(null, 3))
fastify.addHook('onReady', checkOrder.bind(null, 4))
fastify.addHook('onReady', checkOrder.bind(null, 5))
await fastify.ready()
t.pass('trigger the onReady')
await fastify.ready()
t.pass('do not trigger the onReady')
await fastify.ready()
t.pass('do not trigger the onReady')
function checkOrder (shouldbe) {
t.equal(order, shouldbe)
order++
}
})
t.test('onReady should manage error in sync', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', function (done) {
t.pass('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')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL ON READY')
})
})
t.test('onReady should manage error in async', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', async function () {
t.pass('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')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL ON READY')
})
})
t.test('onReady should manage sync error', t => {
t.plan(4)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
done()
})
fastify.register(async (childOne, o) => {
childOne.addHook('onReady', function (done) {
t.pass('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')
})
})
})
fastify.ready(err => {
t.ok(err)
t.equal(err.message, 'FAIL UNWANTED SYNC EXCEPTION')
})
})
t.test('onReady can not add decorators or application hooks', t => {
t.plan(3)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
fastify.decorate('test', () => {})
fastify.addHook('onReady', async function () {
t.fail('it will be not called')
})
done()
})
fastify.addHook('onReady', function (done) {
t.ok(this.hasDecorator('test'))
done()
})
fastify.ready(err => { t.error(err) })
})
t.test('onReady cannot add lifecycle hooks', t => {
t.plan(5)
const fastify = Fastify()
fastify.addHook('onReady', function (done) {
t.pass('called in root')
try {
fastify.addHook('onRequest', (request, reply, done) => {})
} catch (error) {
t.ok(error)
t.equal(error.message, 'Root plugin has already booted')
// TODO: look where the error pops up
t.equal(error.code, 'AVV_ERR_ROOT_PLG_BOOTED')
done(error)
}
})
fastify.addHook('onRequest', (request, reply, done) => {})
fastify.get('/', async () => 'hello')
fastify.ready((err) => { t.ok(err) })
})
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.test('onReady does not call done', t => {
t.plan(6)
const fastify = Fastify({ pluginTimeout: 500 })
fastify.addHook('onReady', function (done) {
t.pass('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.test('onReady execution order', t => {
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) })
})
t.test('ready return the server with callback', t => {
t.plan(2)
const fastify = Fastify()
fastify.ready((err, instance) => {
t.error(err)
t.same(instance, fastify)
})
})
t.test('ready return the server with Promise', t => {
t.plan(1)
const fastify = Fastify()
fastify.ready()
.then(instance => { t.same(instance, fastify) })
.catch(err => { t.fail(err) })
})
t.test('ready return registered', t => {
t.plan(4)
const fastify = Fastify()
fastify.register((one, opts, done) => {
one.ready().then(itself => { t.same(itself, one) })
done()
})
fastify.register((two, opts, done) => {
two.ready().then(itself => { t.same(itself, two) })
two.register((twoDotOne, opts, done) => {
twoDotOne.ready().then(itself => { t.same(itself, twoDotOne) })
done()
})
done()
})
fastify.ready()
.then(instance => { t.same(instance, fastify) })
.catch(err => { t.fail(err) })
})
t.test('do not crash with error in follow up onReady hook', async t => {
const fastify = Fastify()
fastify.addHook('onReady', async function () {
})
fastify.addHook('onReady', function () {
throw new Error('kaboom')
})
await t.rejects(fastify.ready())
})

3626
node_modules/fastify/test/hooks.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

172
node_modules/fastify/test/http2/closing.test.js generated vendored Normal file
View File

@@ -0,0 +1,172 @@
'use strict'
const t = require('tap')
const Fastify = require('../..')
const http2 = require('node:http2')
const { promisify } = require('node:util')
const connect = promisify(http2.connect)
const { once } = require('node:events')
const { buildCertificate } = require('../build-certificate')
const { getServerUrl } = require('../helper')
t.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)
}
fastify.get('/', () => Promise.resolve({}))
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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()
})
session.on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
t.end()
})
})
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)
}
fastify.get('/', () => Promise.resolve({}))
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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()
})
session.on('error', () => {
// Nothing to do here,
// we are not interested in this error that might
// happen or not
t.end()
})
})
t.end()
})
})
t.test('http/2 closes successfully with async await', async t => {
const fastify = Fastify({
http2SessionTimeout: 100,
http2: true
})
await fastify.listen({ port: 0 })
const url = getServerUrl(fastify)
const session = await connect(url)
// An error might or might not happen, as it's OS dependent.
session.on('error', () => {})
await fastify.close()
})
t.test('https/2 closes successfully with async await', async t => {
const fastify = Fastify({
http2SessionTimeout: 100,
http2: true,
https: {
key: global.context.key,
cert: global.context.cert
}
})
await fastify.listen({ port: 0 })
const url = getServerUrl(fastify)
const session = await connect(url)
// An error might or might not happen, as it's OS dependent.
session.on('error', () => {})
await fastify.close()
})
t.test('http/2 server side session emits a timeout event', async t => {
let _resolve
const p = new Promise((resolve) => { _resolve = resolve })
const fastify = Fastify({
http2SessionTimeout: 100,
http2: true
})
fastify.get('/', async (req) => {
req.raw.stream.session.on('timeout', () => _resolve())
return {}
})
await fastify.listen({ port: 0 })
const url = getServerUrl(fastify)
const session = await connect(url)
const req = session.request({
':method': 'GET',
':path': '/'
}).end()
const [headers] = await once(req, 'response')
t.equal(headers[':status'], 200)
req.resume()
// An error might or might not happen, as it's OS dependent.
session.on('error', () => {})
await p
await fastify.close()
})

91
node_modules/fastify/test/http2/constraint.test.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const alpha = { res: 'alpha' }
const beta = { res: 'beta' }
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
test('A route supports host constraints under http2 protocol and secure connection', (t) => {
t.plan(5)
let fastify
try {
fastify = Fastify({
http2: true,
https: {
key: global.context.key,
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
}
const constrain = 'fastify.dev'
fastify.route({
method: 'GET',
url: '/',
handler: function (_, reply) {
reply.code(200).send(alpha)
}
})
fastify.route({
method: 'GET',
url: '/beta',
constraints: { host: constrain },
handler: function (_, reply) {
reply.code(200).send(beta)
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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 })
t.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(alpha).length)
t.same(JSON.parse(res.body), alpha)
})
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.equal(res.headers[':status'], 200)
t.equal(res.headers['content-length'], '' + JSON.stringify(beta).length)
t.same(JSON.parse(res.body), beta)
})
t.test('https get request - constrain - not found', 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)
})
})
})

35
node_modules/fastify/test/http2/head.test.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
let fastify
try {
fastify = Fastify({
http2: true
})
t.pass('http2 successfully loaded')
} catch (e) {
t.fail('http2 loading failed', e)
}
fastify.all('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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)
})
})

View File

@@ -0,0 +1,18 @@
'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')
}
})

53
node_modules/fastify/test/http2/plain.test.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
let fastify
try {
fastify = Fastify({
http2: true
})
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('/hostname', function (req, reply) {
reply.code(200).send(req.hostname)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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.same(JSON.parse(res.body), msg)
})
test('http hostname', async (t) => {
t.plan(1)
const hostname = `localhost:${fastify.server.address().port}`
const url = `http://${hostname}/hostname`
const res = await h2url.concat({ url })
t.equal(res.body, hostname)
})
})

View File

@@ -0,0 +1,110 @@
'use strict'
const t = require('tap')
const test = t.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)
test('secure with fallback', (t) => {
t.plan(7)
let fastify
try {
fastify = Fastify({
http2: true,
https: {
allowHTTP1: true,
key: global.context.key,
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
}
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.post('/', function (req, reply) {
reply.code(200).send(req.body)
})
fastify.get('/error', async function (req, reply) {
throw new Error('kaboom')
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.test('https get error', async (t) => {
t.plan(1)
const url = `https://localhost:${fastify.server.address().port}/error`
const res = await h2url.concat({ url })
t.equal(res.headers[':status'], 500)
})
t.test('https post', async (t) => {
t.plan(2)
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'
}
})
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 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.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' })
})
})
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)
})
})
})
})

59
node_modules/fastify/test/http2/secure.test.js generated vendored Normal file
View File

@@ -0,0 +1,59 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
test('secure', (t) => {
t.plan(4)
let fastify
try {
fastify = Fastify({
http2: true,
https: {
key: global.context.key,
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
}
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.get('/proto', function (req, reply) {
reply.code(200).send({ proto: req.protocol })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.test('https get request', async (t) => {
t.plan(3)
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.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' })
})
})
})

View File

@@ -0,0 +1,35 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }
const fastify = Fastify({
http2: true
})
fastify.get('/', function (req, reply) {
reply.code(200).send(msg)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
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), {
statusCode: 404,
code: 'FST_ERR_NOT_FOUND',
error: 'Not Found',
message: 'Not Found'
})
})
})

View File

@@ -0,0 +1,63 @@
'use strict'
const t = require('tap')
const test = t.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')
async function setup () {
await buildCertificate()
const localAddresses = await dns.lookup('localhost', { all: true })
test('Should support a custom https server', { skip: localAddresses.length < 1 }, async t => {
t.plan(4)
const fastify = Fastify({
serverFactory: (handler, opts) => {
t.ok(opts.serverFactory, 'it is called once for localhost')
const options = {
key: global.context.key,
cert: global.context.cert
}
const server = https.createServer(options, (req, res) => {
req.custom = true
handler(req, res)
})
return server
}
})
t.teardown(fastify.close.bind(fastify))
fastify.get('/', (req, reply) => {
t.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)
}
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), { hello: 'world' })
resolve()
})
})
})
}
setup()

76
node_modules/fastify/test/https/https.test.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
'use strict'
const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('../..')
const { buildCertificate } = require('../build-certificate')
t.before(buildCertificate)
test('https', (t) => {
t.plan(4)
let fastify
try {
fastify = Fastify({
https: {
key: global.context.key,
cert: global.context.cert
}
})
t.pass('Key/cert successfully loaded')
} catch (e) {
t.fail('Key/cert loading failed', e)
}
fastify.get('/', function (req, reply) {
reply.code(200).send({ hello: 'world' })
})
fastify.get('/proto', function (req, reply) {
reply.code(200).send({ proto: req.protocol })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.test('https 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' })
})
})
t.test('https get request without trust proxy - protocol', t => {
t.plan(4)
sget({
method: 'GET',
url: 'https://localhost:' + fastify.server.address().port + '/proto',
rejectUnauthorized: false
}, (err, response, body) => {
t.error(err)
t.same(JSON.parse(body), { proto: 'https' })
})
sget({
method: 'GET',
url: 'https://localhost:' + fastify.server.address().port + '/proto',
rejectUnauthorized: false,
headers: {
'x-forwarded-proto': 'lorem'
}
}, (err, response, body) => {
t.error(err)
t.same(JSON.parse(body), { proto: 'https' })
})
})
})
})

18
node_modules/fastify/test/imports.test.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
'use strict'
const t = require('tap')
const test = t.test
test('should import as default', t => {
t.plan(2)
const fastify = require('..')
t.ok(fastify)
t.equal(typeof fastify, 'function')
})
test('should import as esm', t => {
t.plan(2)
const { fastify } = require('..')
t.ok(fastify)
t.equal(typeof fastify, 'function')
})

486
node_modules/fastify/test/inject.test.js generated vendored Normal file
View File

@@ -0,0 +1,486 @@
'use strict'
const t = require('tap')
const test = t.test
const Stream = require('node:stream')
const util = require('node:util')
const Fastify = require('..')
const FormData = require('form-data')
const { Readable } = require('node:stream')
test('inject should exist', t => {
t.plan(2)
const fastify = Fastify()
t.ok(fastify.inject)
t.equal(typeof fastify.inject, 'function')
})
test('should wait for the ready event', t => {
t.plan(4)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.register((instance, opts, done) => {
instance.get('/', (req, reply) => {
reply.send(payload)
})
setTimeout(done, 500)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request', t => {
t.plan(4)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request - code check', t => {
t.plan(4)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.code(201).send(payload)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 201)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request - headers check', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.header('content-type', 'text/plain').send('')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal('', res.payload)
t.equal(res.headers['content-type'], 'text/plain')
t.equal(res.headers['content-length'], '0')
})
})
test('inject get request - querystring', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.send(req.query)
})
fastify.inject({
method: 'GET',
url: '/?hello=world'
}, (err, res) => {
t.error(err)
t.same({ hello: 'world' }, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request - params', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/:hello', (req, reply) => {
reply.send(req.params)
})
fastify.inject({
method: 'GET',
url: '/world'
}, (err, res) => {
t.error(err)
t.same({ hello: 'world' }, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request - wildcard', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/test/*', (req, reply) => {
reply.send(req.params)
})
fastify.inject({
method: 'GET',
url: '/test/wildcard'
}, (err, res) => {
t.error(err)
t.same({ '*': 'wildcard' }, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '16')
})
})
test('inject get request - headers', t => {
t.plan(4)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.send(req.headers)
})
fastify.inject({
method: 'GET',
url: '/',
headers: { hello: 'world' }
}, (err, res) => {
t.error(err)
t.equal('world', JSON.parse(res.payload).hello)
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '69')
})
})
test('inject post request', t => {
t.plan(4)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.inject({
method: 'POST',
url: '/',
payload
}, (err, res) => {
t.error(err)
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject post request - send stream', t => {
t.plan(4)
const fastify = Fastify()
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.inject({
method: 'POST',
url: '/',
headers: { 'content-type': 'application/json' },
payload: getStream()
}, (err, res) => {
t.error(err)
t.same('{"hello":"world"}', res.payload)
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
})
test('inject get request - reply stream', t => {
t.plan(3)
const fastify = Fastify()
fastify.get('/', (req, reply) => {
reply.send(getStream())
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.same('{"hello":"world"}', res.payload)
t.equal(res.statusCode, 200)
})
})
test('inject promisify - waiting for ready event', t => {
t.plan(1)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
const injectParams = {
method: 'GET',
url: '/'
}
fastify.inject(injectParams)
.then(res => {
t.equal(res.statusCode, 200)
})
.catch(t.fail)
})
test('inject promisify - after the ready event', t => {
t.plan(2)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
fastify.ready(err => {
t.error(err)
const injectParams = {
method: 'GET',
url: '/'
}
fastify.inject(injectParams)
.then(res => {
t.equal(res.statusCode, 200)
})
.catch(t.fail)
})
})
test('inject promisify - when the server is up', t => {
t.plan(2)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
fastify.ready(err => {
t.error(err)
// setTimeout because the ready event don't set "started" flag
// in this iteration of the 'event loop'
setTimeout(() => {
const injectParams = {
method: 'GET',
url: '/'
}
fastify.inject(injectParams)
.then(res => {
t.equal(res.statusCode, 200)
})
.catch(t.fail)
}, 10)
})
})
test('should reject in error case', t => {
t.plan(1)
const fastify = Fastify()
const error = new Error('DOOM!')
fastify.register((instance, opts, done) => {
setTimeout(done, 500, error)
})
fastify.inject({
method: 'GET',
url: '/'
})
.catch(e => {
t.equal(e, error)
})
})
test('inject a multipart request using form-body', t => {
t.plan(2)
const fastify = Fastify()
fastify.addContentTypeParser('*', function (req, payload, done) {
let body = ''
payload.on('data', d => {
body += d
})
payload.on('end', () => {
done(null, body)
})
})
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
const form = new FormData()
form.append('my_field', 'my value')
fastify.inject({
method: 'POST',
url: '/',
payload: form
})
.then(response => {
t.equal(response.statusCode, 200)
t.ok(/Content-Disposition: form-data; name="my_field"/.test(response.payload))
})
})
// https://github.com/hapijs/shot/blob/master/test/index.js#L836
function getStream () {
const Read = function () {
Stream.Readable.call(this)
}
util.inherits(Read, Stream.Readable)
const word = '{"hello":"world"}'
let i = 0
Read.prototype._read = function (size) {
this.push(word[i] ? word[i++] : null)
}
return new Read()
}
test('should error the promise if ready errors', t => {
t.plan(3)
const fastify = Fastify()
fastify.register((instance, opts) => {
return Promise.reject(new Error('kaboom'))
}).after(function () {
t.pass('after is called')
})
fastify.inject({
method: 'GET',
url: '/'
}).then(() => {
t.fail('this should not be called')
}).catch(err => {
t.ok(err)
t.equal(err.message, 'kaboom')
})
})
test('should throw error if callback specified and if ready errors', t => {
t.plan(2)
const fastify = Fastify()
const error = new Error('kaboom')
fastify.register((instance, opts) => {
return Promise.reject(error)
})
fastify.inject({
method: 'GET',
url: '/'
}, err => {
t.ok(err)
t.equal(err, error)
})
})
test('should support builder-style injection with ready app', async (t) => {
t.plan(3)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
await fastify.ready()
const res = await fastify.inject().get('/').end()
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
test('should support builder-style injection with non-ready app', async (t) => {
t.plan(3)
const fastify = Fastify()
const payload = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(payload)
})
const res = await fastify.inject().get('/').end()
t.same(payload, JSON.parse(res.payload))
t.equal(res.statusCode, 200)
t.equal(res.headers['content-length'], '17')
})
test('should handle errors in builder-style injection correctly', async (t) => {
t.plan(2)
const fastify = Fastify()
fastify.register((instance, opts, done) => {
done(new Error('Kaboom'))
})
try {
await fastify.inject().get('/')
} catch (err) {
t.ok(err)
t.equal(err.message, 'Kaboom')
}
})
test('Should not throw on access to routeConfig frameworkErrors handler - FST_ERR_BAD_URL', t => {
t.plan(5)
const fastify = Fastify({
frameworkErrors: function (err, req, res) {
t.ok(typeof req.id === 'string')
t.ok(req.raw instanceof Readable)
t.same(req.routerPath, undefined)
res.send(`${err.message} - ${err.code}`)
}
})
fastify.get('/test/:id', (req, res) => {
res.send('{ hello: \'world\' }')
})
fastify.inject(
{
method: 'GET',
url: '/test/%world'
},
(err, res) => {
t.error(err)
t.equal(res.body, '\'/test/%world\' is not a valid url component - FST_ERR_BAD_URL')
}
)
})

336
node_modules/fastify/test/input-validation.js generated vendored Normal file
View File

@@ -0,0 +1,336 @@
'use strict'
const sget = require('simple-get').concat
const Ajv = require('ajv')
const Joi = require('joi')
const yup = require('yup')
module.exports.payloadMethod = function (method, t) {
const test = t.test
const fastify = require('..')()
const upMethod = method.toUpperCase()
const loMethod = method.toLowerCase()
const opts = {
schema: {
body: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
}
}
}
}
const ajv = new Ajv({ coerceTypes: true, removeAdditional: true })
const optsWithCustomValidator = {
schema: {
body: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
},
additionalProperties: false
}
},
validatorCompiler: function ({ schema, method, url, httpPart }) {
return ajv.compile(schema)
}
}
const optsWithJoiValidator = {
schema: {
body: Joi.object().keys({
hello: Joi.string().required()
}).required()
},
validatorCompiler: function ({ schema, method, url, httpPart }) {
return schema.validate.bind(schema)
}
}
const yupOptions = {
strict: true, // don't coerce
abortEarly: false, // return all errors
stripUnknown: true, // remove additional properties
recursive: true
}
const optsWithYupValidator = {
schema: {
body: yup.object().shape({
hello: yup.string().required()
}).required()
},
validatorCompiler: function ({ schema, method, url, httpPart }) {
return data => {
try {
const result = schema.validateSync(data, yupOptions)
return { value: result }
} catch (e) {
return { error: [e] }
}
}
}
}
test(`${upMethod} can be created`, t => {
t.plan(1)
try {
fastify[loMethod]('/', opts, function (req, reply) {
reply.send(req.body)
})
fastify[loMethod]('/custom', optsWithCustomValidator, function (req, reply) {
reply.send(req.body)
})
fastify[loMethod]('/joi', optsWithJoiValidator, function (req, reply) {
reply.send(req.body)
})
fastify[loMethod]('/yup', optsWithYupValidator, function (req, reply) {
reply.send(req.body)
})
fastify.register(function (fastify2, opts, done) {
fastify2.setValidatorCompiler(function schema ({ schema, method, url, httpPart }) {
return body => ({ error: new Error('From custom schema compiler!') })
})
const withInstanceCustomCompiler = {
schema: {
body: {
type: 'object',
properties: { },
additionalProperties: false
}
}
}
fastify2[loMethod]('/plugin', withInstanceCustomCompiler, (req, reply) => reply.send({ hello: 'never here!' }))
const optsWithCustomValidator2 = {
schema: {
body: {
type: 'object',
properties: { },
additionalProperties: false
}
},
validatorCompiler: function ({ schema, method, url, httpPart }) {
return function (body) {
return { error: new Error('Always fail!') }
}
}
}
fastify2[loMethod]('/plugin/custom', optsWithCustomValidator2, (req, reply) => reply.send({ hello: 'never here!' }))
done()
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, function (err) {
if (err) {
t.error(err)
}
t.teardown(() => { fastify.close() })
test(`${upMethod} - correctly replies`, t => {
if (upMethod === 'HEAD') {
t.plan(2)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
})
} else {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: {
hello: 42
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 42 })
})
}
})
test(`${upMethod} - 400 on bad parameters`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(body, {
error: 'Bad Request',
message: 'body/hello must be integer',
statusCode: 400,
code: 'FST_ERR_VALIDATION'
})
})
})
test(`${upMethod} - input-validation coerce`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: {
hello: '42'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 42 })
})
})
test(`${upMethod} - input-validation custom schema compiler`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/custom',
body: {
hello: '42',
world: 55
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 42 })
})
})
test(`${upMethod} - input-validation joi schema compiler ok`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/joi',
body: {
hello: '42'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 42 })
})
})
test(`${upMethod} - input-validation joi schema compiler ko`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/joi',
body: {
hello: 44
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(body, {
error: 'Bad Request',
message: '"hello" must be a string',
statusCode: 400,
code: 'FST_ERR_VALIDATION'
})
})
})
test(`${upMethod} - input-validation yup schema compiler ok`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/yup',
body: {
hello: '42'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 42 })
})
})
test(`${upMethod} - input-validation yup schema compiler ko`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/yup',
body: {
hello: 44
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.match(body, {
error: 'Bad Request',
message: /body hello must be a `string` type, but the final value was: `44`./,
statusCode: 400,
code: 'FST_ERR_VALIDATION'
})
})
})
test(`${upMethod} - input-validation instance custom schema compiler encapsulated`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/plugin',
body: { },
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(body, {
error: 'Bad Request',
message: 'From custom schema compiler!',
statusCode: '400',
code: 'FST_ERR_VALIDATION'
})
})
})
test(`${upMethod} - input-validation custom schema compiler encapsulated`, t => {
t.plan(3)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/plugin/custom',
body: { },
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(body, {
error: 'Bad Request',
message: 'Always fail!',
statusCode: '400',
code: 'FST_ERR_VALIDATION'
})
})
})
})
}

41
node_modules/fastify/test/internals/all.test.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const { supportedMethods } = require('../../lib/httpMethods')
test('fastify.all should add all the methods to the same url', t => {
const requirePayload = [
'POST',
'PUT',
'PATCH'
]
t.plan(supportedMethods.length * 2)
const fastify = Fastify()
fastify.all('/', (req, reply) => {
reply.send({ method: req.raw.method })
})
supportedMethods.forEach(injectRequest)
function injectRequest (method) {
const options = {
url: '/',
method
}
if (requirePayload.includes(method)) {
options.payload = { hello: 'world' }
}
fastify.inject(options, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.same(payload, { method })
})
}
})

View File

@@ -0,0 +1,112 @@
'use strict'
const t = require('tap')
const proxyquire = require('proxyquire')
const test = t.test
const { Readable } = require('node:stream')
const { kTestInternals, kRouteContext } = require('../../lib/symbols')
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
test('rawBody function', t => {
t.plan(2)
const internals = require('../../lib/contentTypeParser')[kTestInternals]
const body = Buffer.from('你好 世界')
const parser = {
asString: true,
asBuffer: false,
fn (req, bodyInString, done) {
t.equal(bodyInString, body.toString())
t.equal(typeof done, 'function')
return {
then (cb) {
cb()
}
}
}
}
const res = {}
res.end = () => { }
res.writeHead = () => { }
res.log = { error: () => { }, info: () => { } }
const context = {
Reply,
Request,
preHandler: [],
onSend: [],
_parserOptions: {
limit: 1024
}
}
const rs = new Readable()
rs._read = function () { }
rs.headers = { 'content-length': body.length }
const request = new Request('id', 'params', rs, 'query', 'log', context)
const reply = new Reply(res, request)
const done = () => { }
internals.rawBody(
request,
reply,
reply[kRouteContext]._parserOptions,
parser,
done
)
rs.emit('data', body.toString())
rs.emit('end')
})
test('Should support Webpack and faux modules', t => {
t.plan(2)
const internals = proxyquire('../../lib/contentTypeParser', {
'tiny-lru': { default: () => { } }
})[kTestInternals]
const body = Buffer.from('你好 世界')
const parser = {
asString: true,
asBuffer: false,
fn (req, bodyInString, done) {
t.equal(bodyInString, body.toString())
t.equal(typeof done, 'function')
return {
then (cb) {
cb()
}
}
}
}
const res = {}
res.end = () => { }
res.writeHead = () => { }
res.log = { error: () => { }, info: () => { } }
const context = {
Reply,
Request,
preHandler: [],
onSend: [],
_parserOptions: {
limit: 1024
}
}
const rs = new Readable()
rs._read = function () { }
rs.headers = { 'content-length': body.length }
const request = new Request('id', 'params', rs, 'query', 'log', context)
const reply = new Reply(res, request)
const done = () => { }
internals.rawBody(
request,
reply,
reply[kRouteContext]._parserOptions,
parser,
done
)
rs.emit('data', body.toString())
rs.emit('end')
})

33
node_modules/fastify/test/internals/context.test.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
'use strict'
const { test } = require('tap')
const { kRouteContext } = require('../../lib/symbols')
const Context = require('../../lib/context')
const Fastify = require('../..')
test('context', context => {
context.plan(1)
context.test('Should not contain undefined as key prop', async t => {
const app = Fastify()
app.get('/', (req, reply) => {
t.type(req[kRouteContext], Context)
t.type(reply[kRouteContext], Context)
t.notOk('undefined' in reply[kRouteContext])
t.notOk('undefined' in req[kRouteContext])
reply.send('hello world!')
})
try {
await app.inject('/')
} catch (e) {
t.fail(e)
}
t.plan(4)
})
})

159
node_modules/fastify/test/internals/decorator.test.js generated vendored Normal file
View File

@@ -0,0 +1,159 @@
'use strict'
/* eslint no-prototype-builtins: 0 */
const t = require('tap')
const test = t.test
const decorator = require('../../lib/decorate')
const {
kState
} = require('../../lib/symbols')
test('decorate should add the given method to its instance', t => {
t.plan(1)
function build () {
server.add = decorator.add
server[kState] = {
listening: false,
closing: false,
started: false
}
return server
function server () {}
}
const server = build()
server.add('test', () => {})
t.ok(server.test)
})
test('decorate is chainable', t => {
t.plan(3)
function build () {
server.add = decorator.add
server[kState] = {
listening: false,
closing: false,
started: false
}
return server
function server () {}
}
const server = build()
server
.add('test1', () => {})
.add('test2', () => {})
.add('test3', () => {})
t.ok(server.test1)
t.ok(server.test2)
t.ok(server.test3)
})
test('checkExistence should check if a property is part of the given instance', t => {
t.plan(1)
const instance = { test: () => {} }
t.ok(decorator.exist(instance, 'test'))
})
test('checkExistence should find the instance if not given', t => {
t.plan(1)
function build () {
server.add = decorator.add
server.check = decorator.exist
server[kState] = {
listening: false,
closing: false,
started: false
}
return server
function server () {}
}
const server = build()
server.add('test', () => {})
t.ok(server.check('test'))
})
test('checkExistence should check the prototype as well', t => {
t.plan(1)
function Instance () {}
Instance.prototype.test = () => {}
const instance = new Instance()
t.ok(decorator.exist(instance, 'test'))
})
test('checkDependencies should throw if a dependency is not present', t => {
t.plan(2)
const instance = {}
try {
decorator.dependencies(instance, 'foo', ['test'])
t.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.equal(e.message, 'The decorator is missing dependency \'test\'.')
}
})
test('decorate should internally call checkDependencies', t => {
t.plan(2)
function build () {
server.add = decorator.add
server[kState] = {
listening: false,
closing: false,
started: false
}
return server
function server () {}
}
const server = build()
try {
server.add('method', () => {}, ['test'])
t.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.equal(e.message, 'The decorator is missing dependency \'test\'.')
}
})
test('decorate should recognize getter/setter objects', t => {
t.plan(6)
const one = {
[kState]: {
listening: false,
closing: false,
started: false
}
}
decorator.add.call(one, 'foo', {
getter: () => this._a,
setter: (val) => {
t.pass()
this._a = val
}
})
t.equal(Object.prototype.hasOwnProperty.call(one, 'foo'), true)
t.equal(one.foo, undefined)
one.foo = 'a'
t.equal(one.foo, 'a')
// getter only
const two = {
[kState]: {
listening: false,
closing: false,
started: false
}
}
decorator.add.call(two, 'foo', {
getter: () => 'a getter'
})
t.equal(Object.prototype.hasOwnProperty.call(two, 'foo'), true)
t.equal(two.foo, 'a getter')
})

888
node_modules/fastify/test/internals/errors.test.js generated vendored Normal file
View File

@@ -0,0 +1,888 @@
'use strict'
const { test } = require('tap')
const errors = require('../../lib/errors')
const { readFileSync } = require('node:fs')
const { resolve } = require('node:path')
test('should expose 80 errors', t => {
t.plan(1)
const exportedKeys = Object.keys(errors)
let counter = 0
for (const key of exportedKeys) {
if (errors[key].name === 'FastifyError') {
counter++
}
}
t.equal(counter, 80)
})
test('ensure name and codes of Errors are identical', t => {
t.plan(80)
const exportedKeys = Object.keys(errors)
for (const key of exportedKeys) {
if (errors[key].name === 'FastifyError') {
t.equal(key, new errors[key]().code, key)
}
}
})
test('FST_ERR_NOT_FOUND', t => {
t.plan(5)
const error = new errors.FST_ERR_NOT_FOUND()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_NOT_FOUND')
t.equal(error.message, 'Not Found')
t.equal(error.statusCode, 404)
t.ok(error instanceof Error)
})
test('FST_ERR_OPTIONS_NOT_OBJ', t => {
t.plan(5)
const error = new errors.FST_ERR_OPTIONS_NOT_OBJ()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_OPTIONS_NOT_OBJ')
t.equal(error.message, 'Options must be an object')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_QSP_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_QSP_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_QSP_NOT_FN')
t.equal(error.message, "querystringParser option should be a function, instead got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN')
t.equal(error.message, "schemaController.bucket option should be a function, instead got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
t.equal(error.message, "schemaErrorFormatter option should be a non async function. Instead got '%s'.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ', t => {
t.plan(5)
const error = new errors.FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ')
t.equal(error.message, "ajv.customOptions option should be an object, instead got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR', t => {
t.plan(5)
const error = new errors.FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR')
t.equal(error.message, "ajv.plugins option should be an array, instead got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_VERSION_CONSTRAINT_NOT_STR', t => {
t.plan(5)
const error = new errors.FST_ERR_VERSION_CONSTRAINT_NOT_STR()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_VERSION_CONSTRAINT_NOT_STR')
t.equal(error.message, 'Version constraint should be a string.')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_CTP_ALREADY_PRESENT', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_ALREADY_PRESENT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_ALREADY_PRESENT')
t.equal(error.message, "Content type parser '%s' already present.")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_CTP_INVALID_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INVALID_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INVALID_TYPE')
t.equal(error.message, 'The content type should be a string or a RegExp')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_CTP_EMPTY_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_EMPTY_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_EMPTY_TYPE')
t.equal(error.message, 'The content type cannot be an empty string')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_CTP_INVALID_HANDLER', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INVALID_HANDLER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INVALID_HANDLER')
t.equal(error.message, 'The content type handler should be a function')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_CTP_INVALID_PARSE_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INVALID_PARSE_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INVALID_PARSE_TYPE')
t.equal(error.message, "The body parser can only parse your data as 'string' or 'buffer', you asked '%s' which is not supported.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_CTP_BODY_TOO_LARGE', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_BODY_TOO_LARGE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_BODY_TOO_LARGE')
t.equal(error.message, 'Request body is too large')
t.equal(error.statusCode, 413)
t.ok(error instanceof RangeError)
})
test('FST_ERR_CTP_INVALID_MEDIA_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INVALID_MEDIA_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INVALID_MEDIA_TYPE')
t.equal(error.message, 'Unsupported Media Type: %s')
t.equal(error.statusCode, 415)
t.ok(error instanceof Error)
})
test('FST_ERR_CTP_INVALID_CONTENT_LENGTH', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INVALID_CONTENT_LENGTH()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INVALID_CONTENT_LENGTH')
t.equal(error.message, 'Request body size did not match Content-Length')
t.equal(error.statusCode, 400)
t.ok(error instanceof RangeError)
})
test('FST_ERR_CTP_EMPTY_JSON_BODY', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_EMPTY_JSON_BODY()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_EMPTY_JSON_BODY')
t.equal(error.message, "Body cannot be empty when content-type is set to 'application/json'")
t.equal(error.statusCode, 400)
t.ok(error instanceof Error)
})
test('FST_ERR_CTP_INSTANCE_ALREADY_STARTED', t => {
t.plan(5)
const error = new errors.FST_ERR_CTP_INSTANCE_ALREADY_STARTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_CTP_INSTANCE_ALREADY_STARTED')
t.equal(error.message, 'Cannot call "%s" when fastify instance is already started!')
t.equal(error.statusCode, 400)
t.ok(error instanceof Error)
})
test('FST_ERR_DEC_ALREADY_PRESENT', t => {
t.plan(5)
const error = new errors.FST_ERR_DEC_ALREADY_PRESENT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DEC_ALREADY_PRESENT')
t.equal(error.message, "The decorator '%s' has already been added!")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_DEC_DEPENDENCY_INVALID_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_DEC_DEPENDENCY_INVALID_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DEC_DEPENDENCY_INVALID_TYPE')
t.equal(error.message, "The dependencies of decorator '%s' must be of type Array.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_DEC_MISSING_DEPENDENCY', t => {
t.plan(5)
const error = new errors.FST_ERR_DEC_MISSING_DEPENDENCY()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.equal(error.message, "The decorator is missing dependency '%s'.")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_DEC_AFTER_START', t => {
t.plan(5)
const error = new errors.FST_ERR_DEC_AFTER_START()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DEC_AFTER_START')
t.equal(error.message, "The decorator '%s' has been added after start!")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_HOOK_INVALID_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_HOOK_INVALID_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HOOK_INVALID_TYPE')
t.equal(error.message, 'The hook name must be a string')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_HOOK_INVALID_HANDLER', t => {
t.plan(5)
const error = new errors.FST_ERR_HOOK_INVALID_HANDLER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HOOK_INVALID_HANDLER')
t.equal(error.message, '%s hook should be a function, instead got %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_HOOK_INVALID_ASYNC_HANDLER', t => {
t.plan(5)
const error = new errors.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
t.equal(error.message, "Async function has too many arguments. Async hooks should not use the 'done' argument.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_HOOK_NOT_SUPPORTED', t => {
t.plan(5)
const error = new errors.FST_ERR_HOOK_NOT_SUPPORTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HOOK_NOT_SUPPORTED')
t.equal(error.message, '%s hook not supported!')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_MISSING_MIDDLEWARE', t => {
t.plan(5)
const error = new errors.FST_ERR_MISSING_MIDDLEWARE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_MISSING_MIDDLEWARE')
t.equal(error.message, 'You must register a plugin for handling middlewares, visit fastify.dev/docs/latest/Reference/Middleware/ for more info.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_HOOK_TIMEOUT', t => {
t.plan(5)
const error = new errors.FST_ERR_HOOK_TIMEOUT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HOOK_TIMEOUT')
t.equal(error.message, "A callback for '%s' hook timed out. You may have forgotten to call 'done' function or to resolve a Promise")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_LOG_INVALID_DESTINATION', t => {
t.plan(5)
const error = new errors.FST_ERR_LOG_INVALID_DESTINATION()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_LOG_INVALID_DESTINATION')
t.equal(error.message, 'Cannot specify both logger.stream and logger.file options')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_LOG_INVALID_LOGGER', t => {
t.plan(5)
const error = new errors.FST_ERR_LOG_INVALID_LOGGER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_LOG_INVALID_LOGGER')
t.equal(error.message, "Invalid logger object provided. The logger instance should have these functions(s): '%s'.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_REP_INVALID_PAYLOAD_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_REP_INVALID_PAYLOAD_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REP_INVALID_PAYLOAD_TYPE')
t.equal(error.message, "Attempted to send payload of invalid type '%s'. Expected a string or Buffer.")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_REP_RESPONSE_BODY_CONSUMED', t => {
t.plan(5)
const error = new errors.FST_ERR_REP_RESPONSE_BODY_CONSUMED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REP_RESPONSE_BODY_CONSUMED')
t.equal(error.message, 'Response.body is already consumed.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_REP_ALREADY_SENT', t => {
t.plan(5)
const error = new errors.FST_ERR_REP_ALREADY_SENT('/hello', 'GET')
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REP_ALREADY_SENT')
t.equal(error.message, 'Reply was already sent, did you forget to "return reply" in "/hello" (GET)?')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_REP_SENT_VALUE', t => {
t.plan(5)
const error = new errors.FST_ERR_REP_SENT_VALUE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REP_SENT_VALUE')
t.equal(error.message, 'The only possible value for reply.sent is true.')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_SEND_INSIDE_ONERR', t => {
t.plan(5)
const error = new errors.FST_ERR_SEND_INSIDE_ONERR()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SEND_INSIDE_ONERR')
t.equal(error.message, 'You cannot use `send` inside the `onError` hook')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SEND_UNDEFINED_ERR', t => {
t.plan(5)
const error = new errors.FST_ERR_SEND_UNDEFINED_ERR()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SEND_UNDEFINED_ERR')
t.equal(error.message, 'Undefined error has occurred')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_BAD_STATUS_CODE', t => {
t.plan(5)
const error = new errors.FST_ERR_BAD_STATUS_CODE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_BAD_STATUS_CODE')
t.equal(error.message, 'Called reply with an invalid status code: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_BAD_TRAILER_NAME', t => {
t.plan(5)
const error = new errors.FST_ERR_BAD_TRAILER_NAME()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_BAD_TRAILER_NAME')
t.equal(error.message, 'Called reply.trailer with an invalid header name: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_BAD_TRAILER_VALUE', t => {
t.plan(5)
const error = new errors.FST_ERR_BAD_TRAILER_VALUE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_BAD_TRAILER_VALUE')
t.equal(error.message, "Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function.")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_FAILED_ERROR_SERIALIZATION', t => {
t.plan(5)
const error = new errors.FST_ERR_FAILED_ERROR_SERIALIZATION()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_FAILED_ERROR_SERIALIZATION')
t.equal(error.message, 'Failed to serialize an error. Error: %s. Original error: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_MISSING_SERIALIZATION_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_MISSING_SERIALIZATION_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_MISSING_SERIALIZATION_FN')
t.equal(error.message, 'Missing serialization function. Key "%s"')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN')
t.equal(error.message, 'Missing serialization function. Key "%s:%s"')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_REQ_INVALID_VALIDATION_INVOCATION', t => {
t.plan(5)
const error = new errors.FST_ERR_REQ_INVALID_VALIDATION_INVOCATION()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION')
t.equal(error.message, 'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_MISSING_ID', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_MISSING_ID()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_MISSING_ID')
t.equal(error.message, 'Missing schema $id property')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_ALREADY_PRESENT', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_ALREADY_PRESENT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_ALREADY_PRESENT')
t.equal(error.message, "Schema with id '%s' already declared!")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_CONTENT_MISSING_SCHEMA', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_CONTENT_MISSING_SCHEMA()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA')
t.equal(error.message, "Schema is missing for the content type '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_DUPLICATE', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_DUPLICATE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_DUPLICATE')
t.equal(error.message, "Schema with '%s' already present!")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_VALIDATION_BUILD', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_VALIDATION_BUILD()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(error.message, 'Failed building the validation schema for %s: %s, due to error %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_SERIALIZATION_BUILD', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_SERIALIZATION_BUILD()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
t.equal(error.message, 'Failed building the serialization schema for %s: %s, due to error %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX', t => {
t.plan(5)
const error = new errors.FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX')
t.equal(error.message, 'response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_HTTP2_INVALID_VERSION', t => {
t.plan(5)
const error = new errors.FST_ERR_HTTP2_INVALID_VERSION()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_HTTP2_INVALID_VERSION')
t.equal(error.message, 'HTTP2 is available only from node >= 8.8.1')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_INIT_OPTS_INVALID', t => {
t.plan(5)
const error = new errors.FST_ERR_INIT_OPTS_INVALID()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_INIT_OPTS_INVALID')
t.equal(error.message, "Invalid initialization options: '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE', t => {
t.plan(5)
const error = new errors.FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE')
t.equal(error.message, "Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_DUPLICATED_ROUTE', t => {
t.plan(5)
const error = new errors.FST_ERR_DUPLICATED_ROUTE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DUPLICATED_ROUTE')
t.equal(error.message, "Method '%s' already declared for route '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_BAD_URL', t => {
t.plan(5)
const error = new errors.FST_ERR_BAD_URL()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_BAD_URL')
t.equal(error.message, "'%s' is not a valid url component")
t.equal(error.statusCode, 400)
t.ok(error instanceof Error)
})
test('FST_ERR_ASYNC_CONSTRAINT', t => {
t.plan(5)
const error = new errors.FST_ERR_ASYNC_CONSTRAINT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ASYNC_CONSTRAINT')
t.equal(error.message, 'Unexpected error from async constraint')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_DEFAULT_ROUTE_INVALID_TYPE', t => {
t.plan(5)
const error = new errors.FST_ERR_DEFAULT_ROUTE_INVALID_TYPE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_DEFAULT_ROUTE_INVALID_TYPE')
t.equal(error.message, 'The defaultRoute type should be a function')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_INVALID_URL', t => {
t.plan(5)
const error = new errors.FST_ERR_INVALID_URL()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_INVALID_URL')
t.equal(error.message, "URL must be a string. Received '%s'")
t.equal(error.statusCode, 400)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_OPTIONS_NOT_OBJ', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_OPTIONS_NOT_OBJ()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_OPTIONS_NOT_OBJ')
t.equal(error.message, 'Options for "%s:%s" route must be an object')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_DUPLICATED_HANDLER', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_DUPLICATED_HANDLER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_DUPLICATED_HANDLER')
t.equal(error.message, 'Duplicate handler for "%s:%s" route is not allowed!')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_ROUTE_HANDLER_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_HANDLER_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_HANDLER_NOT_FN')
t.equal(error.message, 'Error Handler for %s:%s route, if defined, must be a function')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_MISSING_HANDLER', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_MISSING_HANDLER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_MISSING_HANDLER')
t.equal(error.message, 'Missing handler function for "%s:%s" route.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_ROUTE_METHOD_INVALID', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_METHOD_INVALID()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_METHOD_INVALID')
t.equal(error.message, 'Provided method is invalid!')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_METHOD_NOT_SUPPORTED', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_METHOD_NOT_SUPPORTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED')
t.equal(error.message, '%s method is not supported.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED')
t.equal(error.message, 'Body validation schema for %s:%s route is not supported!')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT')
t.equal(error.message, "'bodyLimit' option must be an integer > 0. Got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT')
t.equal(error.message, "'bodyLimit' option must be an integer > 0. Got '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ROUTE_REWRITE_NOT_STR', t => {
t.plan(5)
const error = new errors.FST_ERR_ROUTE_REWRITE_NOT_STR()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROUTE_REWRITE_NOT_STR')
t.equal(error.message, 'Rewrite url for "%s" needs to be of type "string" but received "%s"')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_REOPENED_CLOSE_SERVER', t => {
t.plan(5)
const error = new errors.FST_ERR_REOPENED_CLOSE_SERVER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
t.equal(error.message, 'Fastify has already been closed and cannot be reopened')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_REOPENED_SERVER', t => {
t.plan(5)
const error = new errors.FST_ERR_REOPENED_SERVER()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REOPENED_SERVER')
t.equal(error.message, 'Fastify is already listening')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_INSTANCE_ALREADY_LISTENING', t => {
t.plan(5)
const error = new errors.FST_ERR_INSTANCE_ALREADY_LISTENING()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_INSTANCE_ALREADY_LISTENING')
t.equal(error.message, 'Fastify instance is already listening. %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_PLUGIN_VERSION_MISMATCH', t => {
t.plan(5)
const error = new errors.FST_ERR_PLUGIN_VERSION_MISMATCH()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PLUGIN_VERSION_MISMATCH')
t.equal(error.message, "fastify-plugin: %s - expected '%s' fastify version, '%s' is installed")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE', t => {
t.plan(5)
const error = new errors.FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE')
t.equal(error.message, "The decorator '%s'%s is not present in %s")
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_PLUGIN_CALLBACK_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_PLUGIN_CALLBACK_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PLUGIN_CALLBACK_NOT_FN')
t.equal(error.message, 'fastify-plugin: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_PLUGIN_NOT_VALID', t => {
t.plan(5)
const error = new errors.FST_ERR_PLUGIN_NOT_VALID()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PLUGIN_NOT_VALID')
t.equal(error.message, 'fastify-plugin: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_ROOT_PLG_BOOTED', t => {
t.plan(5)
const error = new errors.FST_ERR_ROOT_PLG_BOOTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ROOT_PLG_BOOTED')
t.equal(error.message, 'fastify-plugin: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_PARENT_PLUGIN_BOOTED', t => {
t.plan(5)
const error = new errors.FST_ERR_PARENT_PLUGIN_BOOTED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PARENT_PLUGIN_BOOTED')
t.equal(error.message, 'fastify-plugin: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_PLUGIN_TIMEOUT', t => {
t.plan(5)
const error = new errors.FST_ERR_PLUGIN_TIMEOUT()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_PLUGIN_TIMEOUT')
t.equal(error.message, 'fastify-plugin: %s')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_VALIDATION', t => {
t.plan(5)
const error = new errors.FST_ERR_VALIDATION()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_VALIDATION')
t.equal(error.message, '%s')
t.equal(error.statusCode, 400)
t.ok(error instanceof Error)
})
test('FST_ERR_LISTEN_OPTIONS_INVALID', t => {
t.plan(5)
const error = new errors.FST_ERR_LISTEN_OPTIONS_INVALID()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_LISTEN_OPTIONS_INVALID')
t.equal(error.message, "Invalid listen options: '%s'")
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('FST_ERR_ERROR_HANDLER_NOT_FN', t => {
t.plan(5)
const error = new errors.FST_ERR_ERROR_HANDLER_NOT_FN()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_ERROR_HANDLER_NOT_FN')
t.equal(error.message, 'Error Handler must be a function')
t.equal(error.statusCode, 500)
t.ok(error instanceof TypeError)
})
test('Ensure that all errors are in Errors.md TOC', t => {
t.plan(80)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
const exportedKeys = Object.keys(errors)
for (const key of exportedKeys) {
if (errors[key].name === 'FastifyError') {
t.ok(errorsMd.includes(` - [${key.toUpperCase()}](#${key.toLowerCase()})`), key)
}
}
})
test('Ensure that non-existing errors are not in Errors.md TOC', t => {
t.plan(80)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
const matchRE = / {4}- \[([A-Z0-9_]+)\]\(#[a-z0-9_]+\)/g
const matches = errorsMd.matchAll(matchRE)
const exportedKeys = Object.keys(errors)
for (const match of matches) {
t.ok(exportedKeys.indexOf(match[1]) !== -1, match[1])
}
})
test('Ensure that all errors are in Errors.md documented', t => {
t.plan(80)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
const exportedKeys = Object.keys(errors)
for (const key of exportedKeys) {
if (errors[key].name === 'FastifyError') {
t.ok(errorsMd.includes(`<a id="${key.toLowerCase()}">${key.toUpperCase()}</a>`), key)
}
}
})
test('Ensure that non-existing errors are not in Errors.md documented', t => {
t.plan(80)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')
const matchRE = /<a id="[0-9a-zA-Z_]+">([0-9a-zA-Z_]+)<\/a>/g
const matches = errorsMd.matchAll(matchRE)
const exportedKeys = Object.keys(errors)
for (const match of matches) {
t.ok(exportedKeys.indexOf(match[1]) !== -1, match[1])
}
})

View File

@@ -0,0 +1,248 @@
'use strict'
const { test } = require('tap')
const handleRequest = require('../../lib/handleRequest')
const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
const { kRouteContext } = require('../../lib/symbols')
const buildSchema = require('../../lib/validation').compileSchemasForValidation
const sget = require('simple-get').concat
const Ajv = require('ajv')
const ajv = new Ajv({ coerceTypes: true })
function schemaValidator ({ schema, method, url, httpPart }) {
const validateFunction = ajv.compile(schema)
const fn = function (body) {
const isOk = validateFunction(body)
if (isOk) return
return false
}
fn.errors = []
return fn
}
test('handleRequest function - sent reply', t => {
t.plan(1)
const request = {}
const reply = { sent: true }
const res = handleRequest(null, request, reply)
t.equal(res, undefined)
})
test('handleRequest function - invoke with error', t => {
t.plan(1)
const request = {}
const reply = {}
reply.send = (err) => t.equal(err.message, 'Kaboom')
handleRequest(new Error('Kaboom'), request, reply)
})
test('handler function - invalid schema', t => {
t.plan(1)
const res = {}
res.log = { error: () => {}, info: () => {} }
const context = {
config: {
method: 'GET',
url: '/an-url'
},
schema: {
body: {
type: 'object',
properties: {
hello: { type: 'number' }
}
}
},
errorHandler: { func: () => { t.pass('errorHandler called') } },
handler: () => {},
Reply,
Request,
preValidation: [],
preHandler: [],
onSend: [],
onError: [],
attachValidation: false,
schemaErrorFormatter: () => new Error()
}
buildSchema(context, schemaValidator)
const request = {
body: { hello: 'world' },
[kRouteContext]: context
}
internals.handler(request, new Reply(res, request))
})
test('handler function - reply', t => {
t.plan(3)
const res = {}
res.end = () => {
t.equal(res.statusCode, 204)
t.pass()
}
res.writeHead = () => {}
const context = {
handler: (req, reply) => {
t.equal(typeof reply, 'object')
reply.code(204)
reply.send(undefined)
},
Reply,
Request,
preValidation: [],
preHandler: [],
onSend: [],
onError: []
}
buildSchema(context, schemaValidator)
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
})
test('handler function - preValidationCallback with finished response', t => {
t.plan(0)
const res = {}
// Be sure to check only `writableEnded` where is available
res.writableEnded = true
res.end = () => {
t.fail()
}
res.writeHead = () => {}
const context = {
handler: (req, reply) => {
t.fail()
reply.send(undefined)
},
Reply,
Request,
preValidation: null,
preHandler: [],
onSend: [],
onError: []
}
buildSchema(context, schemaValidator)
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
})
test('request should be defined in onSend Hook on post request with content type application/json', t => {
t.plan(8)
const fastify = require('../..')()
fastify.addHook('onSend', (request, reply, payload, done) => {
t.ok(request)
t.ok(request.raw)
t.ok(request.id)
t.ok(request.params)
t.ok(request.query)
done()
})
fastify.post('/', (request, reply) => {
reply.send(200)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'content-type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
// a 400 error is expected because of no body
t.equal(response.statusCode, 400)
})
})
})
test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', t => {
t.plan(7)
const fastify = require('../..')()
fastify.addHook('onSend', (request, reply, payload, done) => {
t.ok(request)
t.ok(request.raw)
t.ok(request.params)
t.ok(request.query)
done()
})
fastify.post('/', (request, reply) => {
reply.send(200)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'POST',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
}, (err, response, body) => {
t.error(err)
// a 415 error is expected because of missing content type parser
t.equal(response.statusCode, 415)
})
})
})
test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', t => {
t.plan(7)
const fastify = require('../..')()
fastify.addHook('onSend', (request, reply, payload, done) => {
t.ok(request)
t.ok(request.raw)
t.ok(request.params)
t.ok(request.query)
done()
})
fastify.options('/', (request, reply) => {
reply.send(200)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
sget({
method: 'OPTIONS',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
}, (err, response, body) => {
t.error(err)
// Body parsing skipped, so no body sent
t.equal(response.statusCode, 200)
})
})
})
test('request should respond with an error if an unserialized payload is sent inside an async handler', t => {
t.plan(3)
const fastify = require('../..')()
fastify.get('/', (request, reply) => {
reply.type('text/html')
return Promise.resolve(request.headers)
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
t.strictSame(JSON.parse(res.payload), {
error: 'Internal Server Error',
code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',
statusCode: 500
})
})
})

450
node_modules/fastify/test/internals/hookRunner.test.js generated vendored Normal file
View File

@@ -0,0 +1,450 @@
'use strict'
const t = require('tap')
const test = t.test
const { hookRunnerGenerator, onSendHookRunner } = require('../../lib/hooks')
test('hookRunner - Basic', t => {
t.plan(9)
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
return fn(a, b, done)
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function fn3 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function done (err, a, b) {
t.error(err)
t.equal(a, 'a')
t.equal(b, 'b')
}
})
test('hookRunner - In case of error should skip to done', t => {
t.plan(7)
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
return fn(a, b, done)
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
}
})
test('hookRunner - Should handle throw', t => {
t.plan(7)
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
return fn(a, b, done)
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
throw new Error('kaboom')
}
function fn3 () {
t.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
}
})
test('hookRunner - Should handle promises', t => {
t.plan(9)
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
return fn(a, b, done)
}
function fn1 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
return Promise.resolve()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
return Promise.resolve()
}
function fn3 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
return Promise.resolve()
}
function done (err, a, b) {
t.error(err)
t.equal(a, 'a')
t.equal(b, 'b')
}
})
test('hookRunner - In case of error should skip to done (with promises)', t => {
t.plan(7)
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
return fn(a, b, done)
}
function fn1 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
return Promise.resolve()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
return Promise.reject(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
}
})
test('hookRunner - Be able to exit before its natural end', t => {
t.plan(4)
const hookRunner = hookRunnerGenerator(iterator)
let shouldStop = false
hookRunner([fn1, fn2, fn3], 'a', 'b', done)
function iterator (fn, a, b, done) {
if (shouldStop) {
return undefined
}
return fn(a, b, done)
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
done()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
shouldStop = true
return Promise.resolve()
}
function fn3 () {
t.fail('this should not be called')
}
function done () {
t.fail('this should not be called')
}
})
test('hookRunner - Promises that resolve to a value do not change the state', t => {
t.plan(5)
const originalState = { a: 'a', b: 'b' }
const hookRunner = hookRunnerGenerator(iterator)
hookRunner([fn1, fn2, fn3], originalState, 'b', done)
function iterator (fn, state, b, done) {
return fn(state, b, done)
}
function fn1 (state, b, done) {
t.equal(state, originalState)
return Promise.resolve(null)
}
function fn2 (state, b, done) {
t.equal(state, originalState)
return Promise.resolve('string')
}
function fn3 (state, b, done) {
t.equal(state, originalState)
return Promise.resolve({ object: true })
}
function done (err, state, b) {
t.error(err)
t.equal(state, originalState)
}
})
test('onSendHookRunner - Basic', t => {
t.plan(13)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const originalPayload = 'payload'
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, originalPayload, done)
function fn1 (request, reply, payload, done) {
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
done()
}
function fn2 (request, reply, payload, done) {
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
done()
}
function fn3 (request, reply, payload, done) {
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
done()
}
function done (err, request, reply, payload) {
t.error(err)
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
}
})
test('onSendHookRunner - Can change the payload', t => {
t.plan(7)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const v1 = { hello: 'world' }
const v2 = { ciao: 'mondo' }
const v3 = { winter: 'is coming' }
const v4 = { winter: 'has come' }
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload, done) {
t.same(payload, v2)
done(null, v3)
}
function fn3 (request, reply, payload, done) {
t.same(payload, v3)
done(null, v4)
}
function done (err, request, reply, payload) {
t.error(err)
t.same(request, originalRequest)
t.same(reply, originalReply)
t.same(payload, v4)
}
})
test('onSendHookRunner - In case of error should skip to done', t => {
t.plan(6)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const v1 = { hello: 'world' }
const v2 = { ciao: 'mondo' }
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload, done) {
t.same(payload, v2)
done(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
}
function done (err, request, reply, payload) {
t.equal(err.message, 'kaboom')
t.same(request, originalRequest)
t.same(reply, originalReply)
t.same(payload, v2)
}
})
test('onSendHookRunner - Should handle promises', t => {
t.plan(7)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const v1 = { hello: 'world' }
const v2 = { ciao: 'mondo' }
const v3 = { winter: 'is coming' }
const v4 = { winter: 'has come' }
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload) {
t.same(payload, v1)
return Promise.resolve(v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
return Promise.resolve(v3)
}
function fn3 (request, reply, payload) {
t.same(payload, v3)
return Promise.resolve(v4)
}
function done (err, request, reply, payload) {
t.error(err)
t.same(request, originalRequest)
t.same(reply, originalReply)
t.same(payload, v4)
}
})
test('onSendHookRunner - In case of error should skip to done (with promises)', t => {
t.plan(6)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const v1 = { hello: 'world' }
const v2 = { ciao: 'mondo' }
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload) {
t.same(payload, v1)
return Promise.resolve(v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
return Promise.reject(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
}
function done (err, request, reply, payload) {
t.equal(err.message, 'kaboom')
t.same(request, originalRequest)
t.same(reply, originalReply)
t.same(payload, v2)
}
})
test('onSendHookRunner - Be able to exit before its natural end', t => {
t.plan(2)
const originalRequest = { body: null }
const originalReply = { request: originalRequest }
const v1 = { hello: 'world' }
const v2 = { ciao: 'mondo' }
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
}
function fn3 () {
t.fail('this should not be called')
}
function done () {
t.fail('this should not be called')
}
})

83
node_modules/fastify/test/internals/hooks.test.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
'use strict'
const t = require('tap')
const test = t.test
const { Hooks } = require('../../lib/hooks')
const noop = () => {}
test('hooks should have 4 array with the registered hooks', t => {
const hooks = new Hooks()
t.equal(typeof hooks, 'object')
t.ok(Array.isArray(hooks.onRequest))
t.ok(Array.isArray(hooks.onSend))
t.ok(Array.isArray(hooks.preParsing))
t.ok(Array.isArray(hooks.preValidation))
t.ok(Array.isArray(hooks.preHandler))
t.ok(Array.isArray(hooks.onResponse))
t.ok(Array.isArray(hooks.onError))
t.end()
})
test('hooks.add should add a hook to the given hook', t => {
const hooks = new Hooks()
hooks.add('onRequest', noop)
t.equal(hooks.onRequest.length, 1)
t.equal(typeof hooks.onRequest[0], 'function')
hooks.add('preParsing', noop)
t.equal(hooks.preParsing.length, 1)
t.equal(typeof hooks.preParsing[0], 'function')
hooks.add('preValidation', noop)
t.equal(hooks.preValidation.length, 1)
t.equal(typeof hooks.preValidation[0], 'function')
hooks.add('preHandler', noop)
t.equal(hooks.preHandler.length, 1)
t.equal(typeof hooks.preHandler[0], 'function')
hooks.add('onResponse', noop)
t.equal(hooks.onResponse.length, 1)
t.equal(typeof hooks.onResponse[0], 'function')
hooks.add('onSend', noop)
t.equal(hooks.onSend.length, 1)
t.equal(typeof hooks.onSend[0], 'function')
hooks.add('onError', noop)
t.equal(hooks.onError.length, 1)
t.equal(typeof hooks.onError[0], 'function')
t.end()
})
test('hooks should throw on unexisting handler', t => {
t.plan(1)
const hooks = new Hooks()
try {
hooks.add('onUnexistingHook', noop)
t.fail()
} catch (e) {
t.pass()
}
})
test('should throw on wrong parameters', t => {
const hooks = new Hooks()
t.plan(4)
try {
hooks.add(null, () => {})
t.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_HOOK_INVALID_TYPE')
t.equal(e.message, 'The hook name must be a string')
}
try {
hooks.add('onSend', null)
t.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_HOOK_INVALID_HANDLER')
t.equal(e.message, 'onSend hook should be a function, instead got [object Null]')
}
})

View File

@@ -0,0 +1,400 @@
'use strict'
const { test, before } = require('tap')
const Fastify = require('../..')
const helper = require('../helper')
const http = require('node:http')
const pino = require('pino')
const split = require('split2')
const deepClone = require('rfdc')({ circles: true, proto: false })
const { deepFreezeObject } = require('../../lib/initialConfigValidation').utils
const { buildCertificate } = require('../build-certificate')
process.removeAllListeners('warning')
let localhost
let localhostForURL
before(async function () {
await buildCertificate();
[localhost, localhostForURL] = await helper.getLoopbackHost()
})
test('Fastify.initialConfig is an object', t => {
t.plan(1)
t.type(Fastify().initialConfig, 'object')
})
test('without options passed to Fastify, initialConfig should expose default values', t => {
t.plan(1)
const fastifyDefaultOptions = {
connectionTimeout: 0,
keepAliveTimeout: 72000,
maxRequestsPerSocket: 0,
requestTimeout: 0,
bodyLimit: 1024 * 1024,
caseSensitive: true,
allowUnsafeRegex: false,
disableRequestLogging: false,
jsonShorthand: true,
ignoreTrailingSlash: false,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
onProtoPoisoning: 'error',
onConstructorPoisoning: 'error',
pluginTimeout: 10000,
requestIdHeader: 'request-id',
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000,
exposeHeadRoutes: true,
useSemicolonDelimiter: true
}
t.same(Fastify().initialConfig, fastifyDefaultOptions)
})
test('Fastify.initialConfig should expose all options', t => {
t.plan(22)
const serverFactory = (handler, opts) => {
const server = http.createServer((req, res) => {
handler(req, res)
})
return server
}
const versionStrategy = {
name: 'version',
storage: function () {
const versions = {}
return {
get: (version) => { return versions[version] || null },
set: (version, store) => { versions[version] = store }
}
},
deriveConstraint: (req, ctx) => {
return req.headers.accept
},
validate () { return true }
}
let reqId = 0
const options = {
http2: true,
https: {
key: global.context.key,
cert: global.context.cert
},
ignoreTrailingSlash: true,
ignoreDuplicateSlashes: true,
maxParamLength: 200,
connectionTimeout: 0,
keepAliveTimeout: 72000,
bodyLimit: 1049600,
onProtoPoisoning: 'remove',
serverFactory,
caseSensitive: true,
allowUnsafeRegex: false,
requestIdHeader: 'request-id-alt',
pluginTimeout: 20000,
useSemicolonDelimiter: false,
querystringParser: str => str,
genReqId: function (req) {
return reqId++
},
logger: pino({ level: 'info' }),
constraints: {
version: versionStrategy
},
trustProxy: function myTrustFn (address, hop) {
return address === '1.2.3.4' || hop === 1
}
}
const fastify = Fastify(options)
t.equal(fastify.initialConfig.http2, true)
t.equal(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
t.equal(fastify.initialConfig.ignoreTrailingSlash, true)
t.equal(fastify.initialConfig.ignoreDuplicateSlashes, true)
t.equal(fastify.initialConfig.maxParamLength, 200)
t.equal(fastify.initialConfig.connectionTimeout, 0)
t.equal(fastify.initialConfig.keepAliveTimeout, 72000)
t.equal(fastify.initialConfig.bodyLimit, 1049600)
t.equal(fastify.initialConfig.onProtoPoisoning, 'remove')
t.equal(fastify.initialConfig.caseSensitive, true)
t.equal(fastify.initialConfig.useSemicolonDelimiter, false)
t.equal(fastify.initialConfig.allowUnsafeRegex, false)
t.equal(fastify.initialConfig.requestIdHeader, 'request-id-alt')
t.equal(fastify.initialConfig.pluginTimeout, 20000)
t.ok(fastify.initialConfig.constraints.version)
// obfuscated options:
t.equal(fastify.initialConfig.serverFactory, undefined)
t.equal(fastify.initialConfig.trustProxy, undefined)
t.equal(fastify.initialConfig.genReqId, undefined)
t.equal(fastify.initialConfig.childLoggerFactory, undefined)
t.equal(fastify.initialConfig.querystringParser, undefined)
t.equal(fastify.initialConfig.logger, undefined)
t.equal(fastify.initialConfig.trustProxy, undefined)
})
test('Should throw if you try to modify Fastify.initialConfig', t => {
t.plan(4)
const fastify = Fastify({ ignoreTrailingSlash: true })
try {
fastify.initialConfig.ignoreTrailingSlash = false
t.fail()
} catch (error) {
t.type(error, TypeError)
t.equal(error.message, "Cannot assign to read only property 'ignoreTrailingSlash' of object '#<Object>'")
t.ok(error.stack)
t.pass()
}
})
test('We must avoid shallow freezing and ensure that the whole object is freezed', t => {
t.plan(4)
const fastify = Fastify({
https: {
allowHTTP1: true,
key: global.context.key,
cert: global.context.cert
}
})
try {
fastify.initialConfig.https.allowHTTP1 = false
t.fail()
} catch (error) {
t.type(error, TypeError)
t.equal(error.message, "Cannot assign to read only property 'allowHTTP1' of object '#<Object>'")
t.ok(error.stack)
t.same(fastify.initialConfig.https, {
allowHTTP1: true
}, 'key cert removed')
}
})
test('https value check', t => {
t.plan(1)
const fastify = Fastify({})
t.notOk(fastify.initialConfig.https)
})
test('Return an error if options do not match the validation schema', t => {
t.plan(6)
try {
Fastify({ ignoreTrailingSlash: 'string instead of boolean' })
t.fail()
} catch (error) {
t.type(error, Error)
t.equal(error.name, 'FastifyError')
t.equal(error.message, 'Invalid initialization options: \'["must be boolean"]\'')
t.equal(error.code, 'FST_ERR_INIT_OPTS_INVALID')
t.ok(error.stack)
t.pass()
}
})
test('Original options must not be frozen', t => {
t.plan(4)
const originalOptions = {
https: {
allowHTTP1: true,
key: global.context.key,
cert: global.context.cert
}
}
const fastify = Fastify(originalOptions)
t.equal(Object.isFrozen(originalOptions), false)
t.equal(Object.isFrozen(originalOptions.https), false)
t.equal(Object.isFrozen(fastify.initialConfig), true)
t.equal(Object.isFrozen(fastify.initialConfig.https), true)
})
test('Original options must not be altered (test deep cloning)', t => {
t.plan(3)
const originalOptions = {
https: {
allowHTTP1: true,
key: global.context.key,
cert: global.context.cert
}
}
const originalOptionsClone = deepClone(originalOptions)
const fastify = Fastify(originalOptions)
// initialConfig has been triggered
t.equal(Object.isFrozen(fastify.initialConfig), true)
// originalOptions must not have been altered
t.same(originalOptions.https.key, originalOptionsClone.https.key)
t.same(originalOptions.https.cert, originalOptionsClone.https.cert)
})
test('Should not have issues when passing stream options to Pino.js', t => {
t.plan(17)
const stream = split(JSON.parse)
const originalOptions = {
ignoreTrailingSlash: true,
logger: {
level: 'trace',
stream
}
}
let fastify
try {
fastify = Fastify(originalOptions)
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
bindings.someBinding = 'value'
return logger.child(bindings, opts)
})
t.type(fastify, 'object')
t.same(fastify.initialConfig, {
connectionTimeout: 0,
keepAliveTimeout: 72000,
maxRequestsPerSocket: 0,
requestTimeout: 0,
bodyLimit: 1024 * 1024,
caseSensitive: true,
allowUnsafeRegex: false,
disableRequestLogging: false,
jsonShorthand: true,
ignoreTrailingSlash: true,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
onProtoPoisoning: 'error',
onConstructorPoisoning: 'error',
pluginTimeout: 10000,
requestIdHeader: 'request-id',
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000,
exposeHeadRoutes: true,
useSemicolonDelimiter: true
})
} catch (error) {
t.fail()
}
fastify.get('/', function (req, reply) {
t.ok(req.log)
reply.send({ hello: 'world' })
})
stream.once('data', listenAtLogLine => {
t.ok(listenAtLogLine, 'listen at log message is ok')
stream.once('data', line => {
const id = line.reqId
t.ok(line.reqId, 'reqId is defined')
t.equal(line.someBinding, 'value', 'child logger binding is set')
t.ok(line.req, 'req is defined')
t.equal(line.msg, 'incoming request', 'message is set')
t.equal(line.req.method, 'GET', 'method is get')
stream.once('data', line => {
t.equal(line.reqId, id)
t.ok(line.reqId, 'reqId is defined')
t.equal(line.someBinding, 'value', 'child logger binding is set')
t.ok(line.res, 'res is defined')
t.equal(line.msg, 'request completed', 'message is set')
t.equal(line.res.statusCode, 200, 'statusCode is 200')
t.ok(line.responseTime, 'responseTime is defined')
})
})
})
fastify.listen({ port: 0, host: localhost }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
http.get(`http://${localhostForURL}:${fastify.server.address().port}`)
})
})
test('deepFreezeObject() should not throw on TypedArray', t => {
t.plan(5)
const object = {
buffer: Buffer.from(global.context.key),
dataView: new DataView(new ArrayBuffer(16)),
float: 1.1,
integer: 1,
object: {
nested: { string: 'string' }
},
stream: split(JSON.parse),
string: 'string'
}
try {
const frozenObject = deepFreezeObject(object)
// Buffers should not be frozen, as they are Uint8Array inherited instances
t.equal(Object.isFrozen(frozenObject.buffer), false)
t.equal(Object.isFrozen(frozenObject), true)
t.equal(Object.isFrozen(frozenObject.object), true)
t.equal(Object.isFrozen(frozenObject.object.nested), true)
t.pass()
} catch (error) {
t.fail()
}
})
test('Fastify.initialConfig should accept the deprecated versioning option', t => {
t.plan(1)
function onWarning (warning) {
t.equal(warning.code, 'FSTDEP009')
}
process.on('warning', onWarning)
const versioning = {
storage: function () {
const versions = {}
return {
get: (version) => { return versions[version] || null },
set: (version, store) => { versions[version] = store }
}
},
deriveVersion: (req, ctx) => {
return req.headers.accept
}
}
Fastify({ versioning })
setImmediate(function () {
process.removeListener('warning', onWarning)
t.end()
})
})
test('pluginTimeout should be parsed correctly', t => {
const withDisabledTimeout = Fastify({ pluginTimeout: '0' })
t.equal(withDisabledTimeout.initialConfig.pluginTimeout, 0)
const withInvalidTimeout = Fastify({ pluginTimeout: undefined })
t.equal(withInvalidTimeout.initialConfig.pluginTimeout, 10000)
t.end()
})

134
node_modules/fastify/test/internals/logger.test.js generated vendored Normal file
View File

@@ -0,0 +1,134 @@
'use strict'
const t = require('tap')
const test = t.test
const Fastify = require('../..')
const loggerUtils = require('../../lib/logger')
test('time resolution', t => {
t.plan(2)
t.equal(typeof loggerUtils.now, 'function')
t.equal(typeof loggerUtils.now(), 'number')
})
test('The logger should add a unique id for every request', t => {
const ids = []
const fastify = Fastify()
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
const queue = new Queue()
for (let i = 0; i < 10; i++) {
queue.add(checkId)
}
queue.add(() => {
fastify.close()
t.end()
})
})
function checkId (done) {
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.ok(ids.indexOf(payload.id) === -1, 'the id should not be duplicated')
ids.push(payload.id)
done()
})
}
})
test('The logger should reuse request id header for req.id', t => {
const fastify = Fastify()
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Request-Id': 'request-id-1'
}
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.ok(payload.id === 'request-id-1', 'the request id from the header should be returned')
fastify.close()
t.end()
})
})
})
function Queue () {
this.q = []
this.running = false
}
Queue.prototype.add = function add (job) {
this.q.push(job)
if (!this.running) this.run()
}
Queue.prototype.run = function run () {
this.running = true
const job = this.q.shift()
job(() => {
if (this.q.length) {
this.run()
} else {
this.running = false
}
})
}
test('The logger should error if both stream and file destination are given', t => {
t.plan(2)
const stream = require('node:stream').Writable
try {
Fastify({
logger: {
level: 'info',
stream,
file: '/test'
}
})
} catch (err) {
t.equal(err.code, 'FST_ERR_LOG_INVALID_DESTINATION')
t.equal(err.message, 'Cannot specify both logger.stream and logger.file options')
}
})
test('The serializer prevent fails if the request socket is undefined', t => {
t.plan(1)
const serialized = loggerUtils.serializers.req({
method: 'GET',
url: '/',
socket: undefined,
headers: {}
})
t.same(serialized, {
method: 'GET',
url: '/',
version: undefined,
hostname: undefined,
remoteAddress: undefined,
remotePort: undefined
})
})

171
node_modules/fastify/test/internals/plugin.test.js generated vendored Normal file
View File

@@ -0,0 +1,171 @@
'use strict'
const t = require('tap')
const test = t.test
const pluginUtilsPublic = require('../../lib/pluginUtils.js')
const symbols = require('../../lib/symbols.js')
const pluginUtils = require('../../lib/pluginUtils')[symbols.kTestInternals]
test("shouldSkipOverride should check the 'skip-override' symbol", t => {
t.plan(2)
yes[Symbol.for('skip-override')] = true
t.ok(pluginUtils.shouldSkipOverride(yes))
t.notOk(pluginUtils.shouldSkipOverride(no))
function yes () {}
function no () {}
})
test('getPluginName should return plugin name if the file is cached', t => {
t.plan(1)
const expectedPluginName = 'example'
const fn = () => console.log('is just an example')
require.cache[expectedPluginName] = { exports: fn }
const pluginName = pluginUtilsPublic.getPluginName(fn)
t.equal(pluginName, expectedPluginName)
})
test('getPluginName should not throw when require.cache is undefined', t => {
t.plan(1)
function example () {
console.log('is just an example')
}
const cache = require.cache
require.cache = undefined
t.teardown(() => {
require.cache = cache
})
const pluginName = pluginUtilsPublic.getPluginName(example)
t.equal(pluginName, 'example')
})
test("getMeta should return the object stored with the 'plugin-meta' symbol", t => {
t.plan(1)
const meta = { hello: 'world' }
fn[Symbol.for('plugin-meta')] = meta
t.same(meta, pluginUtils.getMeta(fn))
function fn () {}
})
test('checkDecorators should check if the given decorator is present in the instance', t => {
t.plan(1)
fn[Symbol.for('plugin-meta')] = {
decorators: {
fastify: ['plugin'],
reply: ['plugin'],
request: ['plugin']
}
}
function context () {}
context.plugin = true
context[symbols.kReply] = { prototype: { plugin: true }, props: [] }
context[symbols.kRequest] = { prototype: { plugin: true }, props: [] }
try {
pluginUtils.checkDecorators.call(context, fn)
t.pass('Everything ok')
} catch (err) {
t.fail(err)
}
function fn () {}
})
test('checkDecorators should check if the given decorator is present in the instance (errored)', t => {
t.plan(1)
fn[Symbol.for('plugin-meta')] = {
decorators: {
fastify: ['plugin'],
reply: ['plugin'],
request: ['plugin']
}
}
function context () {}
context.plugin = true
context[symbols.kReply] = { prototype: { plugin: true }, props: [] }
context[symbols.kRequest] = { prototype: {}, props: [] }
try {
pluginUtils.checkDecorators.call(context, fn)
t.fail('should throw')
} catch (err) {
t.equal(err.message, "The decorator 'plugin' is not present in Request")
}
function fn () {}
})
test('checkDecorators should accept optional decorators', t => {
t.plan(1)
fn[Symbol.for('plugin-meta')] = {
decorators: { }
}
function context () {}
context.plugin = true
context[symbols.kReply] = { prototype: { plugin: true } }
context[symbols.kRequest] = { prototype: { plugin: true } }
try {
pluginUtils.checkDecorators.call(context, fn)
t.pass('Everything ok')
} catch (err) {
t.fail(err)
}
function fn () {}
})
test('checkDependencies should check if the given dependency is present in the instance', t => {
t.plan(1)
fn[Symbol.for('plugin-meta')] = {
dependencies: ['plugin']
}
function context () {}
context[pluginUtilsPublic.kRegisteredPlugins] = ['plugin']
try {
pluginUtils.checkDependencies.call(context, fn)
t.pass('Everything ok')
} catch (err) {
t.fail(err)
}
function fn () {}
})
test('checkDependencies should check if the given dependency is present in the instance (errored)', t => {
t.plan(1)
fn[Symbol.for('plugin-meta')] = {
name: 'test-plugin',
dependencies: ['plugin']
}
function context () {}
context[pluginUtilsPublic.kRegisteredPlugins] = []
try {
pluginUtils.checkDependencies.call(context, fn)
t.fail('should throw')
} catch (err) {
t.equal(err.message, "The dependency 'plugin' of plugin 'test-plugin' is not registered")
}
function fn () {}
})

View File

@@ -0,0 +1,699 @@
'use strict'
const { test } = require('tap')
const { kReplyCacheSerializeFns, kRouteContext } = require('../../lib/symbols')
const Fastify = require('../../fastify')
function getDefaultSchema () {
return {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' },
world: { type: 'string' }
}
}
}
function getResponseSchema () {
return {
201: {
type: 'object',
required: ['status'],
properties: {
status: {
type: 'string',
enum: ['ok']
},
message: {
type: 'string'
}
}
},
'4xx': {
type: 'object',
properties: {
status: {
type: 'string',
enum: ['error']
},
code: {
type: 'integer',
minimum: 1
},
message: {
type: 'string'
}
}
},
'3xx': {
content: {
'application/json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
}
}
test('Reply#compileSerializationSchema', t => {
t.plan(4)
t.test('Should return a serialization function', async t => {
const fastify = Fastify()
t.plan(4)
fastify.get('/', (req, reply) => {
const serialize = reply.compileSerializationSchema(getDefaultSchema())
const input = { hello: 'world' }
t.type(serialize, Function)
t.type(serialize(input), 'string')
t.equal(serialize(input), JSON.stringify(input))
try {
serialize({ world: 'foo' })
} catch (err) {
t.equal(err.message, '"hello" is required!')
}
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
t.test('Should reuse the serialize fn across multiple invocations - Route without schema',
async t => {
const fastify = Fastify()
let serialize = null
let counter = 0
t.plan(17)
const schemaObj = getDefaultSchema()
fastify.get('/', (req, reply) => {
const input = { hello: 'world' }
counter++
if (counter > 1) {
const newSerialize = reply.compileSerializationSchema(schemaObj)
t.equal(serialize, newSerialize, 'Are the same validate function')
serialize = newSerialize
} else {
t.pass('build the schema compilation function')
serialize = reply.compileSerializationSchema(schemaObj)
}
t.type(serialize, Function)
t.equal(serialize(input), JSON.stringify(input))
try {
serialize({ world: 'foo' })
} catch (err) {
t.equal(err.message, '"hello" is required!')
}
reply.send({ hello: 'world' })
})
await Promise.all([
fastify.inject('/'),
fastify.inject('/'),
fastify.inject('/'),
fastify.inject('/')
])
t.equal(counter, 4)
}
)
t.test('Should use the custom serializer compiler for the route',
async t => {
const fastify = Fastify()
let called = 0
const custom = ({ schema, httpStatus, url, method }) => {
t.equal(schema, schemaObj)
t.equal(url, '/')
t.equal(method, 'GET')
t.equal(httpStatus, '201')
return input => {
called++
t.same(input, { hello: 'world' })
return JSON.stringify(input)
}
}
const custom2 = ({ schema, httpStatus, url, method, contentType }) => {
t.equal(schema, schemaObj)
t.equal(url, '/user')
t.equal(method, 'GET')
t.equal(httpStatus, '3xx')
t.equal(contentType, 'application/json')
return input => {
t.same(input, { fullName: 'Jone', phone: 1090243795 })
return JSON.stringify(input)
}
}
t.plan(17)
const schemaObj = getDefaultSchema()
fastify.get('/', { serializerCompiler: custom }, (req, reply) => {
const input = { hello: 'world' }
const first = reply.compileSerializationSchema(schemaObj, '201')
const second = reply.compileSerializationSchema(schemaObj, '201')
t.equal(first, second)
t.ok(first(input), JSON.stringify(input))
t.ok(second(input), JSON.stringify(input))
t.equal(called, 2)
reply.send({ hello: 'world' })
})
fastify.get('/user', { serializerCompiler: custom2 }, (req, reply) => {
const input = { fullName: 'Jone', phone: 1090243795 }
const first = reply.compileSerializationSchema(schemaObj, '3xx', 'application/json')
t.ok(first(input), JSON.stringify(input))
reply.send(input)
})
await fastify.inject({
path: '/',
method: 'GET'
})
await fastify.inject({
path: '/user',
method: 'GET'
})
}
)
t.test('Should build a WeakMap for cache when called', async t => {
const fastify = Fastify()
t.plan(4)
fastify.get('/', (req, reply) => {
const input = { hello: 'world' }
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
t.type(reply[kRouteContext][kReplyCacheSerializeFns], WeakMap)
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
})
test('Reply#getSerializationFunction', t => {
t.plan(3)
t.test('Should retrieve the serialization function from the Schema definition',
async t => {
const fastify = Fastify()
const okInput201 = {
status: 'ok',
message: 'done!'
}
const notOkInput201 = {
message: 'created'
}
const okInput4xx = {
status: 'error',
code: 2,
message: 'oops!'
}
const notOkInput4xx = {
status: 'error',
code: 'something'
}
const okInput3xx = {
fullName: 'Jone',
phone: 0
}
const noOkInput3xx = {
fullName: 'Jone',
phone: 'phone'
}
let cached4xx
let cached201
let cachedJson3xx
t.plan(13)
const responseSchema = getResponseSchema()
fastify.get(
'/:id',
{
params: {
id: {
type: 'integer'
}
},
schema: {
response: responseSchema
}
},
(req, reply) => {
const { id } = req.params
if (Number(id) === 1) {
const serialize4xx = reply.getSerializationFunction('4xx')
const serialize201 = reply.getSerializationFunction(201)
const serializeJson3xx = reply.getSerializationFunction('3xx', 'application/json')
const serializeUndefined = reply.getSerializationFunction(undefined)
cached4xx = serialize4xx
cached201 = serialize201
cachedJson3xx = serializeJson3xx
t.type(serialize4xx, Function)
t.type(serialize201, Function)
t.type(serializeJson3xx, Function)
t.equal(serialize4xx(okInput4xx), JSON.stringify(okInput4xx))
t.equal(serialize201(okInput201), JSON.stringify(okInput201))
t.equal(serializeJson3xx(okInput3xx), JSON.stringify(okInput3xx))
t.notOk(serializeUndefined)
try {
serialize4xx(notOkInput4xx)
} catch (err) {
t.equal(
err.message,
'The value "something" cannot be converted to an integer.'
)
}
try {
serialize201(notOkInput201)
} catch (err) {
t.equal(err.message, '"status" is required!')
}
try {
serializeJson3xx(noOkInput3xx)
} catch (err) {
t.equal(err.message, 'The value "phone" cannot be converted to a number.')
}
reply.status(201).send(okInput201)
} else {
const serialize201 = reply.getSerializationFunction(201)
const serialize4xx = reply.getSerializationFunction('4xx')
const serializeJson3xx = reply.getSerializationFunction('3xx', 'application/json')
t.equal(serialize4xx, cached4xx)
t.equal(serialize201, cached201)
t.equal(serializeJson3xx, cachedJson3xx)
reply.status(401).send(okInput4xx)
}
}
)
await Promise.all([
fastify.inject('/1'),
fastify.inject('/2')
])
}
)
t.test('Should retrieve the serialization function from the cached one',
async t => {
const fastify = Fastify()
const schemaObj = getDefaultSchema()
const okInput = {
hello: 'world',
world: 'done!'
}
const notOkInput = {
world: 'done!'
}
let cached
t.plan(6)
fastify.get(
'/:id',
{
params: {
id: {
type: 'integer'
}
}
},
(req, reply) => {
const { id } = req.params
if (Number(id) === 1) {
const serialize = reply.compileSerializationSchema(schemaObj)
t.type(serialize, Function)
t.equal(serialize(okInput), JSON.stringify(okInput))
try {
serialize(notOkInput)
} catch (err) {
t.equal(err.message, '"hello" is required!')
}
cached = serialize
} else {
const serialize = reply.getSerializationFunction(schemaObj)
t.equal(serialize, cached)
t.equal(serialize(okInput), JSON.stringify(okInput))
try {
serialize(notOkInput)
} catch (err) {
t.equal(err.message, '"hello" is required!')
}
}
reply.status(201).send(okInput)
}
)
await Promise.all([
fastify.inject('/1'),
fastify.inject('/2')
])
}
)
t.test('Should not instantiate a WeakMap if it is not needed', async t => {
const fastify = Fastify()
t.plan(4)
fastify.get('/', (req, reply) => {
t.notOk(reply.getSerializationFunction(getDefaultSchema()))
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.notOk(reply.getSerializationFunction('200'))
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
})
test('Reply#serializeInput', t => {
t.plan(6)
t.test(
'Should throw if missed serialization function from HTTP status',
async t => {
const fastify = Fastify()
t.plan(2)
fastify.get('/', (req, reply) => {
reply.serializeInput({}, 201)
})
const result = await fastify.inject({
path: '/',
method: 'GET'
})
t.equal(result.statusCode, 500)
t.same(result.json(), {
statusCode: 500,
code: 'FST_ERR_MISSING_SERIALIZATION_FN',
error: 'Internal Server Error',
message: 'Missing serialization function. Key "201"'
})
}
)
t.test(
'Should throw if missed serialization function from HTTP status with specific content type',
async t => {
const fastify = Fastify()
t.plan(2)
fastify.get('/', {
schema: {
response: {
'3xx': {
content: {
'application/json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
}
}
}, (req, reply) => {
reply.serializeInput({}, '3xx', 'application/vnd.v1+json')
})
const result = await fastify.inject({
path: '/',
method: 'GET'
})
t.equal(result.statusCode, 500)
t.same(result.json(), {
statusCode: 500,
code: 'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
error: 'Internal Server Error',
message: 'Missing serialization function. Key "3xx:application/vnd.v1+json"'
})
}
)
t.test('Should use a serializer fn from HTTP status', async t => {
const fastify = Fastify()
const okInput201 = {
status: 'ok',
message: 'done!'
}
const notOkInput201 = {
message: 'created'
}
const okInput4xx = {
status: 'error',
code: 2,
message: 'oops!'
}
const notOkInput4xx = {
status: 'error',
code: 'something'
}
const okInput3xx = {
fullName: 'Jone',
phone: 0
}
const noOkInput3xx = {
fullName: 'Jone',
phone: 'phone'
}
t.plan(6)
fastify.get(
'/',
{
params: {
id: {
type: 'integer'
}
},
schema: {
response: getResponseSchema()
}
},
(req, reply) => {
t.equal(
reply.serializeInput(okInput4xx, '4xx'),
JSON.stringify(okInput4xx)
)
t.equal(
reply.serializeInput(okInput201, 201),
JSON.stringify(okInput201)
)
t.equal(
reply.serializeInput(okInput3xx, {}, '3xx', 'application/json'),
JSON.stringify(okInput3xx)
)
try {
reply.serializeInput(noOkInput3xx, '3xx', 'application/json')
} catch (err) {
t.equal(err.message, 'The value "phone" cannot be converted to a number.')
}
try {
reply.serializeInput(notOkInput4xx, '4xx')
} catch (err) {
t.equal(
err.message,
'The value "something" cannot be converted to an integer.'
)
}
try {
reply.serializeInput(notOkInput201, 201)
} catch (err) {
t.equal(err.message, '"status" is required!')
}
reply.status(204).send('')
}
)
await fastify.inject({
path: '/',
method: 'GET'
})
})
t.test(
'Should compile a serializer out of a schema if serializer fn missed',
async t => {
let compilerCalled = 0
let serializerCalled = 0
const testInput = { hello: 'world' }
const schemaObj = getDefaultSchema()
const fastify = Fastify()
const serializerCompiler = ({ schema, httpStatus, method, url }) => {
t.equal(schema, schemaObj)
t.notOk(httpStatus)
t.equal(method, 'GET')
t.equal(url, '/')
compilerCalled++
return input => {
t.equal(input, testInput)
serializerCalled++
return JSON.stringify(input)
}
}
t.plan(10)
fastify.get('/', { serializerCompiler }, (req, reply) => {
t.equal(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
t.equal(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
reply.status(201).send(testInput)
})
await fastify.inject({
path: '/',
method: 'GET'
})
t.equal(compilerCalled, 1)
t.equal(serializerCalled, 2)
}
)
t.test('Should use a cached serializer fn', async t => {
let compilerCalled = 0
let serializerCalled = 0
let cached
const testInput = { hello: 'world' }
const schemaObj = getDefaultSchema()
const fastify = Fastify()
const serializer = input => {
t.equal(input, testInput)
serializerCalled++
return JSON.stringify(input)
}
const serializerCompiler = ({ schema, httpStatus, method, url }) => {
t.equal(schema, schemaObj)
t.notOk(httpStatus)
t.equal(method, 'GET')
t.equal(url, '/')
compilerCalled++
return serializer
}
t.plan(12)
fastify.get('/', { serializerCompiler }, (req, reply) => {
t.equal(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
cached = reply.getSerializationFunction(schemaObj)
t.equal(
reply.serializeInput(testInput, schemaObj),
cached(testInput)
)
reply.status(201).send(testInput)
})
await fastify.inject({
path: '/',
method: 'GET'
})
t.equal(cached, serializer)
t.equal(compilerCalled, 1)
t.equal(serializerCalled, 3)
})
t.test('Should instantiate a WeakMap after first call', async t => {
const fastify = Fastify()
t.plan(3)
fastify.get('/', (req, reply) => {
const input = { hello: 'world' }
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.equal(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
t.type(reply[kRouteContext][kReplyCacheSerializeFns], WeakMap)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
})

2279
node_modules/fastify/test/internals/reply.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
'use strict'
const { test } = require('tap')
const { reqIdGenFactory } = require('../../lib/reqIdGenFactory')
test('should create incremental ids deterministically', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory()
for (let i = 1; i < 1e4; ++i) {
if (reqIdGen() !== 'req-' + i.toString(36)) {
t.fail()
break
}
}
t.pass()
})
test('should have prefix "req-"', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory()
t.ok(reqIdGen().startsWith('req-'))
})
test('different id generator functions should have separate internal counters', t => {
t.plan(5)
const reqIdGenA = reqIdGenFactory()
const reqIdGenB = reqIdGenFactory()
t.equal(reqIdGenA(), 'req-1')
t.equal(reqIdGenA(), 'req-2')
t.equal(reqIdGenB(), 'req-1')
t.equal(reqIdGenA(), 'req-3')
t.equal(reqIdGenB(), 'req-2')
})
test('should start counting with 1', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory()
t.equal(reqIdGen(), 'req-1')
})
test('should handle requestIdHeader and return provided id in header', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory('id')
t.equal(reqIdGen({ headers: { id: '1337' } }), '1337')
})
test('should handle requestIdHeader and fallback if id is not provided in header', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory('id')
t.equal(reqIdGen({ headers: { notId: '1337' } }), 'req-1')
})
test('should handle requestIdHeader and increment internal counter if no header was provided', t => {
t.plan(4)
const reqIdGen = reqIdGenFactory('id')
t.equal(reqIdGen({ headers: {} }), 'req-1')
t.equal(reqIdGen({ headers: {} }), 'req-2')
t.equal(reqIdGen({ headers: { id: '1337' } }), '1337')
t.equal(reqIdGen({ headers: {} }), 'req-3')
})
test('should use optGenReqId to generate ids', t => {
t.plan(4)
let i = 1
let gotCalled = false
function optGenReqId () {
gotCalled = true
return (i++).toString(16)
}
const reqIdGen = reqIdGenFactory(undefined, optGenReqId)
t.equal(gotCalled, false)
t.equal(reqIdGen(), '1')
t.equal(gotCalled, true)
t.equal(reqIdGen(), '2')
})
test('should use optGenReqId to generate ids if requestIdHeader is used but not provided', t => {
t.plan(4)
let i = 1
let gotCalled = false
function optGenReqId () {
gotCalled = true
return (i++).toString(16)
}
const reqIdGen = reqIdGenFactory('reqId', optGenReqId)
t.equal(gotCalled, false)
t.equal(reqIdGen({ headers: {} }), '1')
t.equal(gotCalled, true)
t.equal(reqIdGen({ headers: {} }), '2')
})
test('should not use optGenReqId to generate ids if requestIdHeader is used and provided', t => {
t.plan(2)
function optGenReqId () {
t.fail()
}
const reqIdGen = reqIdGenFactory('reqId', optGenReqId)
t.equal(reqIdGen({ headers: { reqId: 'r1' } }), 'r1')
t.equal(reqIdGen({ headers: { reqId: 'r2' } }), 'r2')
})
test('should fallback to use optGenReqId to generate ids if requestIdHeader is sometimes provided', t => {
t.plan(4)
let i = 1
let gotCalled = false
function optGenReqId () {
gotCalled = true
return (i++).toString(16)
}
const reqIdGen = reqIdGenFactory('reqId', optGenReqId)
t.equal(reqIdGen({ headers: { reqId: 'r1' } }), 'r1')
t.equal(gotCalled, false)
t.equal(reqIdGen({ headers: {} }), '1')
t.equal(gotCalled, true)
})

File diff suppressed because it is too large Load Diff

403
node_modules/fastify/test/internals/request.test.js generated vendored Normal file
View File

@@ -0,0 +1,403 @@
'use strict'
const { test } = require('tap')
const Request = require('../../lib/request')
const Context = require('../../lib/context')
const {
kPublicRouteContext,
kReply,
kRequest,
kOptions
} = require('../../lib/symbols')
process.removeAllListeners('warning')
test('Regular request', t => {
const headers = {
host: 'hostname'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const context = new Context({
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' }
}
}
},
config: {
some: 'config',
url: req.url,
method: req.method
},
server: {
[kReply]: {},
[kRequest]: Request,
[kOptions]: {
requestIdLogLabel: 'reqId'
}
}
})
req.connection = req.socket
const request = new Request('id', 'params', req, 'query', 'log', context)
t.type(request, Request)
t.type(request.validateInput, Function)
t.type(request.getValidationFunction, Function)
t.type(request.compileValidationSchema, Function)
t.equal(request.id, 'id')
t.equal(request.params, 'params')
t.equal(request.raw, req)
t.equal(request.query, 'query')
t.equal(request.headers, headers)
t.equal(request.log, 'log')
t.equal(request.ip, 'ip')
t.equal(request.ips, undefined)
t.equal(request.hostname, 'hostname')
t.equal(request.body, undefined)
t.equal(request.method, 'GET')
t.equal(request.url, '/')
t.equal(request.originalUrl, '/')
t.equal(request.socket, req.socket)
t.equal(request.protocol, 'http')
t.equal(request.routerPath, context.config.url)
t.equal(request.routerMethod, context.config.method)
t.equal(request.routeConfig, context[kPublicRouteContext].config)
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
// Aim to not bad property keys (including Symbols)
t.notOk('undefined' in request)
// This will be removed, it's deprecated
t.equal(request.connection, req.connection)
t.end()
})
test('Request with undefined config', t => {
const headers = {
host: 'hostname'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const context = new Context({
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' }
}
}
},
server: {
[kReply]: {},
[kRequest]: Request,
[kOptions]: {
requestIdLogLabel: 'reqId'
}
}
})
req.connection = req.socket
const request = new Request('id', 'params', req, 'query', 'log', context)
t.type(request, Request)
t.type(request.validateInput, Function)
t.type(request.getValidationFunction, Function)
t.type(request.compileValidationSchema, Function)
t.equal(request.id, 'id')
t.equal(request.params, 'params')
t.equal(request.raw, req)
t.equal(request.query, 'query')
t.equal(request.headers, headers)
t.equal(request.log, 'log')
t.equal(request.ip, 'ip')
t.equal(request.ips, undefined)
t.equal(request.hostname, 'hostname')
t.equal(request.body, undefined)
t.equal(request.method, 'GET')
t.equal(request.url, '/')
t.equal(request.originalUrl, '/')
t.equal(request.socket, req.socket)
t.equal(request.protocol, 'http')
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
t.equal(request.routerPath, undefined)
t.equal(request.routerMethod, undefined)
t.equal(request.routeConfig, undefined)
// Aim to not bad property keys (including Symbols)
t.notOk('undefined' in request)
// This will be removed, it's deprecated
t.equal(request.connection, req.connection)
t.end()
})
test('Regular request - hostname from authority', t => {
t.plan(2)
const headers = {
':authority': 'authority'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const request = new Request('id', 'params', req, 'query', 'log')
t.type(request, Request)
t.equal(request.hostname, 'authority')
})
test('Regular request - host header has precedence over authority', t => {
t.plan(2)
const headers = {
host: 'hostname',
':authority': 'authority'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const request = new Request('id', 'params', req, 'query', 'log')
t.type(request, Request)
t.equal(request.hostname, 'hostname')
})
test('Request with trust proxy', t => {
t.plan(22)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const context = new Context({
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' }
}
}
},
config: {
some: 'config',
url: req.url,
method: req.method
},
server: {
[kReply]: {},
[kRequest]: Request,
[kOptions]: {
requestIdLogLabel: 'reqId'
}
}
})
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log', context)
t.type(request, TpRequest)
t.equal(request.id, 'id')
t.equal(request.params, 'params')
t.same(request.raw, req)
t.equal(request.query, 'query')
t.equal(request.headers, headers)
t.equal(request.log, 'log')
t.equal(request.ip, '2.2.2.2')
t.same(request.ips, ['ip', '1.1.1.1', '2.2.2.2'])
t.equal(request.hostname, 'example.com')
t.equal(request.body, undefined)
t.equal(request.method, 'GET')
t.equal(request.url, '/')
t.equal(request.socket, req.socket)
t.equal(request.protocol, 'http')
t.type(request.validateInput, Function)
t.type(request.getValidationFunction, Function)
t.type(request.compileValidationSchema, Function)
t.equal(request.routerPath, context.config.url)
t.equal(request.routerMethod, context.config.method)
t.equal(request.routeConfig, context[kPublicRouteContext].config)
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
})
test('Request with trust proxy, encrypted', t => {
t.plan(2)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip', encrypted: true },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.protocol, 'https')
})
test('Request with trust proxy - no x-forwarded-host header', t => {
t.plan(2)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
host: 'hostname'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'hostname')
})
test('Request with trust proxy - no x-forwarded-host header and fallback to authority', t => {
t.plan(2)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
':authority': 'authority'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'authority')
})
test('Request with trust proxy - x-forwarded-host header has precedence over host', t => {
t.plan(2)
const headers = {
'x-forwarded-for': ' 2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com',
host: 'hostname'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'example.com')
})
test('Request with trust proxy - handles multiple entries in x-forwarded-host/proto', t => {
t.plan(3)
const headers = {
'x-forwarded-host': 'example2.com, example.com',
'x-forwarded-proto': 'http, https'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'example.com')
t.equal(request.protocol, 'https')
})
test('Request with trust proxy - plain', t => {
t.plan(1)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com'
}
const req = {
method: 'GET',
url: '/',
socket: { remoteAddress: 'ip' },
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.same(request.protocol, 'http')
})
test('Request with undefined socket', t => {
t.plan(18)
const headers = {
host: 'hostname'
}
const req = {
method: 'GET',
url: '/',
socket: undefined,
headers
}
const request = new Request('id', 'params', req, 'query', 'log')
t.type(request, Request)
t.equal(request.id, 'id')
t.equal(request.params, 'params')
t.same(request.raw, req)
t.equal(request.query, 'query')
t.equal(request.headers, headers)
t.equal(request.log, 'log')
t.equal(request.ip, undefined)
t.equal(request.ips, undefined)
t.equal(request.hostname, 'hostname')
t.same(request.body, null)
t.equal(request.method, 'GET')
t.equal(request.url, '/')
t.equal(request.protocol, undefined)
t.same(request.socket, req.socket)
t.type(request.validateInput, Function)
t.type(request.getValidationFunction, Function)
t.type(request.compileValidationSchema, Function)
})
test('Request with trust proxy and undefined socket', t => {
t.plan(1)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com'
}
const req = {
method: 'GET',
url: '/',
socket: undefined,
headers
}
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.same(request.protocol, undefined)
})

88
node_modules/fastify/test/internals/server.test.js generated vendored Normal file
View File

@@ -0,0 +1,88 @@
'use strict'
const { test } = require('tap')
const proxyquire = require('proxyquire')
const Fastify = require('../../fastify')
const { createServer } = require('../../lib/server')
const handler = (req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ data: 'Hello World!' }))
}
test('start listening', async t => {
const { server, listen } = createServer({}, handler)
await listen.call(Fastify(), { port: 0, host: 'localhost' })
server.close()
t.pass('server started')
})
test('DNS errors does not stop the main server on localhost - promise interface', async t => {
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
lookup: (hostname, options, cb) => {
cb(new Error('DNS error'))
}
}
})
const { server, listen } = createServer({}, handler)
await listen.call(Fastify(), { port: 0, host: 'localhost' })
server.close()
t.pass('server started')
})
test('DNS errors does not stop the main server on localhost - callback interface', t => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
lookup: (hostname, options, cb) => {
cb(new Error('DNS error'))
}
}
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
server.close()
t.pass('server started')
})
})
test('DNS returns empty binding', t => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
lookup: (hostname, options, cb) => {
cb(null, [])
}
}
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
server.close()
t.pass('server started')
})
})
test('DNS returns more than two binding', t => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
lookup: (hostname, options, cb) => {
cb(null, [
{ address: '::1', family: 6 },
{ address: '127.0.0.1', family: 4 },
{ address: '0.0.0.0', family: 4 }
])
}
}
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
server.close()
t.pass('server started')
})
})

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