Aktueller Stand

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

View File

@@ -1,28 +1,27 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const { supportedMethods } = require('../../lib/httpMethods')
test('fastify.all should add all the methods to the same url', t => {
test('fastify.all should add all the methods to the same url', async t => {
const fastify = Fastify()
const requirePayload = [
'POST',
'PUT',
'PATCH'
]
t.plan(supportedMethods.length * 2)
const fastify = Fastify()
const supportedMethods = fastify.supportedMethods
t.plan(supportedMethods.length)
fastify.all('/', (req, reply) => {
reply.send({ method: req.raw.method })
})
supportedMethods.forEach(injectRequest)
await Promise.all(supportedMethods.map(async method => injectRequest(method)))
function injectRequest (method) {
async function injectRequest (method) {
const options = {
url: '/',
method
@@ -32,10 +31,8 @@ test('fastify.all should add all the methods to the same url', t => {
options.payload = { hello: 'world' }
}
fastify.inject(options, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.same(payload, { method })
})
const res = await fastify.inject(options)
const payload = JSON.parse(res.payload)
t.assert.deepStrictEqual(payload, { method })
}
})

View File

@@ -1,8 +1,7 @@
'use strict'
const t = require('tap')
const { test } = require('node:test')
const proxyquire = require('proxyquire')
const test = t.test
const { Readable } = require('node:stream')
const { kTestInternals, kRouteContext } = require('../../lib/symbols')
const Request = require('../../lib/request')
@@ -11,14 +10,14 @@ const Reply = require('../../lib/reply')
test('rawBody function', t => {
t.plan(2)
const internals = require('../../lib/contentTypeParser')[kTestInternals]
const internals = require('../../lib/content-type-parser')[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')
t.assert.strictEqual(bodyInString, body.toString())
t.assert.strictEqual(typeof done, 'function')
return {
then (cb) {
cb()
@@ -61,8 +60,8 @@ test('rawBody function', t => {
test('Should support Webpack and faux modules', t => {
t.plan(2)
const internals = proxyquire('../../lib/contentTypeParser', {
'tiny-lru': { default: () => { } }
const internals = proxyquire('../../lib/content-type-parser', {
'toad-cache': { default: () => { } }
})[kTestInternals]
const body = Buffer.from('你好 世界')
@@ -70,8 +69,8 @@ test('Should support Webpack and faux modules', t => {
asString: true,
asBuffer: false,
fn (req, bodyInString, done) {
t.equal(bodyInString, body.toString())
t.equal(typeof done, 'function')
t.assert.strictEqual(bodyInString, body.toString())
t.assert.strictEqual(typeof done, 'function')
return {
then (cb) {
cb()

View File

@@ -1,23 +1,23 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const { kRouteContext } = require('../../lib/symbols')
const Context = require('../../lib/context')
const Fastify = require('../..')
test('context', context => {
test('context', async context => {
context.plan(1)
context.test('Should not contain undefined as key prop', async t => {
await context.test('Should not contain undefined as key prop', async t => {
t.plan(4)
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])
t.assert.ok(req[kRouteContext] instanceof Context)
t.assert.ok(reply[kRouteContext] instanceof Context)
t.assert.ok(!('undefined' in reply[kRouteContext]))
t.assert.ok(!('undefined' in req[kRouteContext]))
reply.send('hello world!')
})
@@ -25,9 +25,7 @@ test('context', context => {
try {
await app.inject('/')
} catch (e) {
t.fail(e)
t.assert.fail(e)
}
t.plan(4)
})
})

View File

@@ -1,9 +1,6 @@
'use strict'
/* eslint no-prototype-builtins: 0 */
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const decorator = require('../../lib/decorate')
const {
kState
@@ -24,7 +21,7 @@ test('decorate should add the given method to its instance', t => {
const server = build()
server.add('test', () => {})
t.ok(server.test)
t.assert.ok(server.test)
})
test('decorate is chainable', t => {
@@ -46,15 +43,15 @@ test('decorate is chainable', t => {
.add('test2', () => {})
.add('test3', () => {})
t.ok(server.test1)
t.ok(server.test2)
t.ok(server.test3)
t.assert.ok(server.test1)
t.assert.ok(server.test2)
t.assert.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'))
t.assert.ok(decorator.exist(instance, 'test'))
})
test('checkExistence should find the instance if not given', t => {
@@ -73,7 +70,7 @@ test('checkExistence should find the instance if not given', t => {
const server = build()
server.add('test', () => {})
t.ok(server.check('test'))
t.assert.ok(server.check('test'))
})
test('checkExistence should check the prototype as well', t => {
@@ -82,7 +79,7 @@ test('checkExistence should check the prototype as well', t => {
Instance.prototype.test = () => {}
const instance = new Instance()
t.ok(decorator.exist(instance, 'test'))
t.assert.ok(decorator.exist(instance, 'test'))
})
test('checkDependencies should throw if a dependency is not present', t => {
@@ -90,10 +87,10 @@ test('checkDependencies should throw if a dependency is not present', t => {
const instance = {}
try {
decorator.dependencies(instance, 'foo', ['test'])
t.fail()
t.assert.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.equal(e.message, 'The decorator is missing dependency \'test\'.')
t.assert.strictEqual(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.assert.strictEqual(e.message, 'The decorator is missing dependency \'test\'.')
}
})
@@ -114,10 +111,10 @@ test('decorate should internally call checkDependencies', t => {
try {
server.add('method', () => {}, ['test'])
t.fail()
t.assert.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.equal(e.message, 'The decorator is missing dependency \'test\'.')
t.assert.strictEqual(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
t.assert.strictEqual(e.message, 'The decorator is missing dependency \'test\'.')
}
})
@@ -134,14 +131,14 @@ test('decorate should recognize getter/setter objects', t => {
decorator.add.call(one, 'foo', {
getter: () => this._a,
setter: (val) => {
t.pass()
t.assert.ok(true)
this._a = val
}
})
t.equal(Object.prototype.hasOwnProperty.call(one, 'foo'), true)
t.equal(one.foo, undefined)
t.assert.strictEqual(Object.hasOwn(one, 'foo'), true)
t.assert.strictEqual(one.foo, undefined)
one.foo = 'a'
t.equal(one.foo, 'a')
t.assert.strictEqual(one.foo, 'a')
// getter only
const two = {
@@ -154,6 +151,6 @@ test('decorate should recognize getter/setter objects', t => {
decorator.add.call(two, 'foo', {
getter: () => 'a getter'
})
t.equal(Object.prototype.hasOwnProperty.call(two, 'foo'), true)
t.equal(two.foo, 'a getter')
t.assert.strictEqual(Object.hasOwn(two, 'foo'), true)
t.assert.strictEqual(two.foo, 'a getter')
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,12 @@
'use strict'
const { test } = require('tap')
const handleRequest = require('../../lib/handleRequest')
const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
const { test } = require('node:test')
const handleRequest = require('../../lib/handle-request')
const internals = require('../../lib/handle-request')[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 })
@@ -28,14 +27,14 @@ test('handleRequest function - sent reply', t => {
const request = {}
const reply = { sent: true }
const res = handleRequest(null, request, reply)
t.equal(res, undefined)
t.assert.strictEqual(res, undefined)
})
test('handleRequest function - invoke with error', t => {
t.plan(1)
const request = {}
const reply = {}
reply.send = (err) => t.equal(err.message, 'Kaboom')
reply.send = (err) => t.assert.strictEqual(err.message, 'Kaboom')
handleRequest(new Error('Kaboom'), request, reply)
})
@@ -56,7 +55,7 @@ test('handler function - invalid schema', t => {
}
}
},
errorHandler: { func: () => { t.pass('errorHandler called') } },
errorHandler: { func: () => { t.assert.ok('errorHandler called') } },
handler: () => {},
Reply,
Request,
@@ -79,13 +78,13 @@ test('handler function - reply', t => {
t.plan(3)
const res = {}
res.end = () => {
t.equal(res.statusCode, 204)
t.pass()
t.assert.strictEqual(res.statusCode, 204)
t.assert.ok(true)
}
res.writeHead = () => {}
const context = {
handler: (req, reply) => {
t.equal(typeof reply, 'object')
t.assert.strictEqual(typeof reply, 'object')
reply.code(204)
reply.send(undefined)
},
@@ -94,7 +93,11 @@ test('handler function - reply', t => {
preValidation: [],
preHandler: [],
onSend: [],
onError: []
onError: [],
config: {
url: '',
method: ''
}
}
buildSchema(context, schemaValidator)
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
@@ -106,12 +109,12 @@ test('handler function - preValidationCallback with finished response', t => {
// Be sure to check only `writableEnded` where is available
res.writableEnded = true
res.end = () => {
t.fail()
t.assert.fail()
}
res.writeHead = () => {}
const context = {
handler: (req, reply) => {
t.fail()
t.assert.fail()
reply.send(undefined)
},
Reply,
@@ -125,105 +128,125 @@ test('handler function - preValidationCallback with finished response', t => {
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)
test('request should be defined in onSend Hook on post request with content type application/json', async t => {
t.plan(6)
const fastify = require('../..')()
t.after(() => {
fastify.close()
})
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)
t.assert.ok(request)
t.assert.ok(request.raw)
t.assert.ok(request.id)
t.assert.ok(request.params)
t.assert.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)
})
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(fastifyServer, {
method: 'POST',
headers: {
'content-type': 'application/json'
}
})
t.assert.strictEqual(result.status, 400)
})
test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', t => {
t.plan(7)
test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', async t => {
t.plan(5)
const fastify = require('../..')()
t.after(() => {
fastify.close()
})
fastify.addHook('onSend', (request, reply, payload, done) => {
t.ok(request)
t.ok(request.raw)
t.ok(request.params)
t.ok(request.query)
t.assert.ok(request)
t.assert.ok(request.raw)
t.assert.ok(request.params)
t.assert.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)
})
const fastifyServer = await fastify.listen({ port: 0 })
const result = await fetch(fastifyServer, {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
// a 415 error is expected because of missing content type parser
t.assert.strictEqual(result.status, 415)
})
test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', t => {
t.plan(7)
test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', async t => {
t.plan(15)
const fastify = require('../..')()
t.after(() => {
fastify.close()
})
fastify.addHook('onSend', (request, reply, payload, done) => {
t.ok(request)
t.ok(request.raw)
t.ok(request.params)
t.ok(request.query)
t.assert.ok(request)
t.assert.ok(request.raw)
t.assert.ok(request.params)
t.assert.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 1: OPTIONS with body and content-type header
const result1 = await fastify.inject({
method: 'OPTIONS',
url: '/',
body: 'first-name=OPTIONS&last-name=METHOD',
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
// Content-Type is not supported
t.assert.strictEqual(result1.statusCode, 415)
// Test 2: OPTIONS with content-type header only (no body)
const result2 = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
// Content-Type is not supported
t.assert.strictEqual(result2.statusCode, 415)
// Test 3: OPTIONS with body but no content-type header
const result3 = await fastify.inject({
method: 'OPTIONS',
url: '/',
body: 'first-name=OPTIONS&last-name=METHOD'
})
// No content-type with payload
t.assert.strictEqual(result3.statusCode, 415)
})
test('request should respond with an error if an unserialized payload is sent inside an async handler', t => {
t.plan(3)
test('request should respond with an error if an unserialized payload is sent inside an async handler', async t => {
t.plan(2)
const fastify = require('../..')()
@@ -232,17 +255,16 @@ test('request should respond with an error if an unserialized payload is sent in
return Promise.resolve(request.headers)
})
fastify.inject({
const res = await 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
})
})
t.assert.strictEqual(res.statusCode, 500)
t.assert.deepStrictEqual(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
})
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const { hookRunnerGenerator, onSendHookRunner } = require('../../lib/hooks')
test('hookRunner - Basic', t => {
@@ -16,27 +15,27 @@ test('hookRunner - Basic', t => {
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function fn3 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function done (err, a, b) {
t.error(err)
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.ifError(err)
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
}
})
@@ -52,25 +51,25 @@ test('hookRunner - In case of error should skip to done', t => {
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
t.assert.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(err.message, 'kaboom')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
}
})
@@ -86,25 +85,25 @@ test('hookRunner - Should handle throw', t => {
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function fn2 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
throw new Error('kaboom')
}
function fn3 () {
t.fail('We should not be here')
t.assert.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(err.message, 'kaboom')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
}
})
@@ -120,27 +119,27 @@ test('hookRunner - Should handle promises', t => {
}
function fn1 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
return Promise.resolve()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
return Promise.resolve()
}
function fn3 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
return Promise.resolve()
}
function done (err, a, b) {
t.error(err)
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.ifError(err)
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
}
})
@@ -156,25 +155,25 @@ test('hookRunner - In case of error should skip to done (with promises)', t => {
}
function fn1 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
return Promise.resolve()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
return Promise.reject(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
t.assert.fail('We should not be here')
}
function done (err, a, b) {
t.equal(err.message, 'kaboom')
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(err.message, 'kaboom')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
}
})
@@ -194,24 +193,24 @@ test('hookRunner - Be able to exit before its natural end', t => {
}
function fn1 (a, b, done) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
done()
}
function fn2 (a, b) {
t.equal(a, 'a')
t.equal(b, 'b')
t.assert.strictEqual(a, 'a')
t.assert.strictEqual(b, 'b')
shouldStop = true
return Promise.resolve()
}
function fn3 () {
t.fail('this should not be called')
t.assert.fail('this should not be called')
}
function done () {
t.fail('this should not be called')
t.assert.fail('this should not be called')
}
})
@@ -229,23 +228,23 @@ test('hookRunner - Promises that resolve to a value do not change the state', t
}
function fn1 (state, b, done) {
t.equal(state, originalState)
t.assert.strictEqual(state, originalState)
return Promise.resolve(null)
}
function fn2 (state, b, done) {
t.equal(state, originalState)
t.assert.strictEqual(state, originalState)
return Promise.resolve('string')
}
function fn3 (state, b, done) {
t.equal(state, originalState)
t.assert.strictEqual(state, originalState)
return Promise.resolve({ object: true })
}
function done (err, state, b) {
t.error(err)
t.equal(state, originalState)
t.assert.ifError(err)
t.assert.strictEqual(state, originalState)
}
})
@@ -259,31 +258,31 @@ test('onSendHookRunner - Basic', t => {
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)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.strictEqual(payload, originalPayload)
done()
}
function fn2 (request, reply, payload, done) {
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.strictEqual(payload, originalPayload)
done()
}
function fn3 (request, reply, payload, done) {
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.strictEqual(payload, originalPayload)
done()
}
function done (err, request, reply, payload) {
t.error(err)
t.same(request, originalRequest)
t.same(reply, originalReply)
t.equal(payload, originalPayload)
t.assert.ifError(err)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.strictEqual(payload, originalPayload)
}
})
@@ -300,25 +299,25 @@ test('onSendHookRunner - Can change the payload', t => {
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
t.assert.deepStrictEqual(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload, done) {
t.same(payload, v2)
t.assert.deepStrictEqual(payload, v2)
done(null, v3)
}
function fn3 (request, reply, payload, done) {
t.same(payload, v3)
t.assert.deepStrictEqual(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)
t.assert.ifError(err)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.deepStrictEqual(payload, v4)
}
})
@@ -333,24 +332,24 @@ test('onSendHookRunner - In case of error should skip to done', t => {
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
t.assert.deepStrictEqual(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload, done) {
t.same(payload, v2)
t.assert.deepStrictEqual(payload, v2)
done(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
t.assert.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)
t.assert.strictEqual(err.message, 'kaboom')
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.deepStrictEqual(payload, v2)
}
})
@@ -367,25 +366,25 @@ test('onSendHookRunner - Should handle promises', t => {
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload) {
t.same(payload, v1)
t.assert.deepStrictEqual(payload, v1)
return Promise.resolve(v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
t.assert.deepStrictEqual(payload, v2)
return Promise.resolve(v3)
}
function fn3 (request, reply, payload) {
t.same(payload, v3)
t.assert.deepStrictEqual(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)
t.assert.ifError(err)
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.deepStrictEqual(payload, v4)
}
})
@@ -400,24 +399,24 @@ test('onSendHookRunner - In case of error should skip to done (with promises)',
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload) {
t.same(payload, v1)
t.assert.deepStrictEqual(payload, v1)
return Promise.resolve(v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
t.assert.deepStrictEqual(payload, v2)
return Promise.reject(new Error('kaboom'))
}
function fn3 () {
t.fail('We should not be here')
t.assert.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)
t.assert.strictEqual(err.message, 'kaboom')
t.assert.deepStrictEqual(request, originalRequest)
t.assert.deepStrictEqual(reply, originalReply)
t.assert.deepStrictEqual(payload, v2)
}
})
@@ -432,19 +431,19 @@ test('onSendHookRunner - Be able to exit before its natural end', t => {
onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
function fn1 (request, reply, payload, done) {
t.same(payload, v1)
t.assert.deepStrictEqual(payload, v1)
done(null, v2)
}
function fn2 (request, reply, payload) {
t.same(payload, v2)
t.assert.deepStrictEqual(payload, v2)
}
function fn3 () {
t.fail('this should not be called')
t.assert.fail('this should not be called')
}
function done () {
t.fail('this should not be called')
t.assert.fail('this should not be called')
}
})

View File

@@ -1,54 +1,51 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const { Hooks } = require('../../lib/hooks')
const { default: fastify } = require('../../fastify')
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()
t.assert.strictEqual(typeof hooks, 'object')
t.assert.ok(Array.isArray(hooks.onRequest))
t.assert.ok(Array.isArray(hooks.onSend))
t.assert.ok(Array.isArray(hooks.preParsing))
t.assert.ok(Array.isArray(hooks.preValidation))
t.assert.ok(Array.isArray(hooks.preHandler))
t.assert.ok(Array.isArray(hooks.onResponse))
t.assert.ok(Array.isArray(hooks.onError))
})
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')
t.assert.strictEqual(hooks.onRequest.length, 1)
t.assert.strictEqual(typeof hooks.onRequest[0], 'function')
hooks.add('preParsing', noop)
t.equal(hooks.preParsing.length, 1)
t.equal(typeof hooks.preParsing[0], 'function')
t.assert.strictEqual(hooks.preParsing.length, 1)
t.assert.strictEqual(typeof hooks.preParsing[0], 'function')
hooks.add('preValidation', noop)
t.equal(hooks.preValidation.length, 1)
t.equal(typeof hooks.preValidation[0], 'function')
t.assert.strictEqual(hooks.preValidation.length, 1)
t.assert.strictEqual(typeof hooks.preValidation[0], 'function')
hooks.add('preHandler', noop)
t.equal(hooks.preHandler.length, 1)
t.equal(typeof hooks.preHandler[0], 'function')
t.assert.strictEqual(hooks.preHandler.length, 1)
t.assert.strictEqual(typeof hooks.preHandler[0], 'function')
hooks.add('onResponse', noop)
t.equal(hooks.onResponse.length, 1)
t.equal(typeof hooks.onResponse[0], 'function')
t.assert.strictEqual(hooks.onResponse.length, 1)
t.assert.strictEqual(typeof hooks.onResponse[0], 'function')
hooks.add('onSend', noop)
t.equal(hooks.onSend.length, 1)
t.equal(typeof hooks.onSend[0], 'function')
t.assert.strictEqual(hooks.onSend.length, 1)
t.assert.strictEqual(typeof hooks.onSend[0], 'function')
hooks.add('onError', noop)
t.equal(hooks.onError.length, 1)
t.equal(typeof hooks.onError[0], 'function')
t.end()
t.assert.strictEqual(hooks.onError.length, 1)
t.assert.strictEqual(typeof hooks.onError[0], 'function')
})
test('hooks should throw on unexisting handler', t => {
@@ -56,9 +53,9 @@ test('hooks should throw on unexisting handler', t => {
const hooks = new Hooks()
try {
hooks.add('onUnexistingHook', noop)
t.fail()
t.assert.fail()
} catch (e) {
t.pass()
t.assert.ok(true)
}
})
@@ -67,17 +64,33 @@ test('should throw on wrong parameters', t => {
t.plan(4)
try {
hooks.add(null, () => {})
t.fail()
t.assert.fail()
} catch (e) {
t.equal(e.code, 'FST_ERR_HOOK_INVALID_TYPE')
t.equal(e.message, 'The hook name must be a string')
t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_TYPE')
t.assert.strictEqual(e.message, 'The hook name must be a string')
}
try {
hooks.add('onSend', null)
t.fail()
t.assert.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]')
t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_HANDLER')
t.assert.strictEqual(e.message, 'onSend hook should be a function, instead got [object Null]')
}
})
test('Integration test: internal function _addHook should be turned into app.ready() rejection', async (t) => {
const app = fastify()
app.register(async function () {
app.addHook('notRealHook', async () => {})
})
try {
await app.ready()
t.assert.fail('Expected ready() to throw')
} catch (err) {
t.assert.strictEqual(err.code, 'FST_ERR_HOOK_NOT_SUPPORTED')
t.assert.match(err.message, /hook not supported/i)
}
})

View File

@@ -1,13 +1,13 @@
'use strict'
const { test, before } = require('tap')
const { test, before } = require('node:test')
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 { deepFreezeObject } = require('../../lib/initial-config-validation').utils
const { buildCertificate } = require('../build-certificate')
@@ -23,7 +23,7 @@ before(async function () {
test('Fastify.initialConfig is an object', t => {
t.plan(1)
t.type(Fastify().initialConfig, 'object')
t.assert.ok(typeof Fastify().initialConfig === 'object')
})
test('without options passed to Fastify, initialConfig should expose default values', t => {
@@ -38,21 +38,20 @@ test('without options passed to Fastify, initialConfig should expose default val
caseSensitive: true,
allowUnsafeRegex: false,
disableRequestLogging: false,
jsonShorthand: true,
ignoreTrailingSlash: false,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
onProtoPoisoning: 'error',
onConstructorPoisoning: 'error',
pluginTimeout: 10000,
requestIdHeader: 'request-id',
requestIdHeader: false,
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000,
exposeHeadRoutes: true,
useSemicolonDelimiter: true
useSemicolonDelimiter: false
}
t.same(Fastify().initialConfig, fastifyDefaultOptions)
t.assert.deepStrictEqual(Fastify().initialConfig, fastifyDefaultOptions)
})
test('Fastify.initialConfig should expose all options', t => {
@@ -105,7 +104,7 @@ test('Fastify.initialConfig should expose all options', t => {
genReqId: function (req) {
return reqId++
},
logger: pino({ level: 'info' }),
loggerInstance: pino({ level: 'info' }),
constraints: {
version: versionStrategy
},
@@ -115,30 +114,30 @@ test('Fastify.initialConfig should expose all options', t => {
}
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)
t.assert.strictEqual(fastify.initialConfig.http2, true)
t.assert.strictEqual(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
t.assert.strictEqual(fastify.initialConfig.ignoreTrailingSlash, true)
t.assert.strictEqual(fastify.initialConfig.ignoreDuplicateSlashes, true)
t.assert.strictEqual(fastify.initialConfig.maxParamLength, 200)
t.assert.strictEqual(fastify.initialConfig.connectionTimeout, 0)
t.assert.strictEqual(fastify.initialConfig.keepAliveTimeout, 72000)
t.assert.strictEqual(fastify.initialConfig.bodyLimit, 1049600)
t.assert.strictEqual(fastify.initialConfig.onProtoPoisoning, 'remove')
t.assert.strictEqual(fastify.initialConfig.caseSensitive, true)
t.assert.strictEqual(fastify.initialConfig.useSemicolonDelimiter, false)
t.assert.strictEqual(fastify.initialConfig.allowUnsafeRegex, false)
t.assert.strictEqual(fastify.initialConfig.requestIdHeader, 'request-id-alt')
t.assert.strictEqual(fastify.initialConfig.pluginTimeout, 20000)
t.assert.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)
t.assert.strictEqual(fastify.initialConfig.serverFactory, undefined)
t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
t.assert.strictEqual(fastify.initialConfig.genReqId, undefined)
t.assert.strictEqual(fastify.initialConfig.childLoggerFactory, undefined)
t.assert.strictEqual(fastify.initialConfig.querystringParser, undefined)
t.assert.strictEqual(fastify.initialConfig.logger, undefined)
t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
})
test('Should throw if you try to modify Fastify.initialConfig', t => {
@@ -147,12 +146,12 @@ test('Should throw if you try to modify Fastify.initialConfig', t => {
const fastify = Fastify({ ignoreTrailingSlash: true })
try {
fastify.initialConfig.ignoreTrailingSlash = false
t.fail()
t.assert.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()
t.assert.ok(error instanceof TypeError)
t.assert.strictEqual(error.message, "Cannot assign to read only property 'ignoreTrailingSlash' of object '#<Object>'")
t.assert.ok(error.stack)
t.assert.ok(true)
}
})
@@ -169,12 +168,12 @@ test('We must avoid shallow freezing and ensure that the whole object is freezed
try {
fastify.initialConfig.https.allowHTTP1 = false
t.fail()
t.assert.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, {
t.assert.ok(error instanceof TypeError)
t.assert.strictEqual(error.message, "Cannot assign to read only property 'allowHTTP1' of object '#<Object>'")
t.assert.ok(error.stack)
t.assert.deepStrictEqual(fastify.initialConfig.https, {
allowHTTP1: true
}, 'key cert removed')
}
@@ -184,7 +183,7 @@ test('https value check', t => {
t.plan(1)
const fastify = Fastify({})
t.notOk(fastify.initialConfig.https)
t.assert.ok(!fastify.initialConfig.https)
})
test('Return an error if options do not match the validation schema', t => {
@@ -193,14 +192,14 @@ test('Return an error if options do not match the validation schema', t => {
try {
Fastify({ ignoreTrailingSlash: 'string instead of boolean' })
t.fail()
t.assert.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()
t.assert.ok(error instanceof Error)
t.assert.strictEqual(error.name, 'FastifyError')
t.assert.strictEqual(error.message, 'Invalid initialization options: \'["must be boolean"]\'')
t.assert.strictEqual(error.code, 'FST_ERR_INIT_OPTS_INVALID')
t.assert.ok(error.stack)
t.assert.ok(true)
}
})
@@ -217,10 +216,10 @@ test('Original options must not be frozen', t => {
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)
t.assert.strictEqual(Object.isFrozen(originalOptions), false)
t.assert.strictEqual(Object.isFrozen(originalOptions.https), false)
t.assert.strictEqual(Object.isFrozen(fastify.initialConfig), true)
t.assert.strictEqual(Object.isFrozen(fastify.initialConfig.https), true)
})
test('Original options must not be altered (test deep cloning)', t => {
@@ -239,14 +238,14 @@ test('Original options must not be altered (test deep cloning)', t => {
const fastify = Fastify(originalOptions)
// initialConfig has been triggered
t.equal(Object.isFrozen(fastify.initialConfig), true)
t.assert.strictEqual(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)
t.assert.deepStrictEqual(originalOptions.https.key, originalOptionsClone.https.key)
t.assert.deepStrictEqual(originalOptions.https.cert, originalOptionsClone.https.cert)
})
test('Should not have issues when passing stream options to Pino.js', t => {
test('Should not have issues when passing stream options to Pino.js', (t, done) => {
t.plan(17)
const stream = split(JSON.parse)
@@ -268,8 +267,8 @@ test('Should not have issues when passing stream options to Pino.js', t => {
return logger.child(bindings, opts)
})
t.type(fastify, 'object')
t.same(fastify.initialConfig, {
t.assert.ok(typeof fastify === 'object')
t.assert.deepStrictEqual(fastify.initialConfig, {
connectionTimeout: 0,
keepAliveTimeout: 72000,
maxRequestsPerSocket: 0,
@@ -278,56 +277,57 @@ test('Should not have issues when passing stream options to Pino.js', t => {
caseSensitive: true,
allowUnsafeRegex: false,
disableRequestLogging: false,
jsonShorthand: true,
ignoreTrailingSlash: true,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
onProtoPoisoning: 'error',
onConstructorPoisoning: 'error',
pluginTimeout: 10000,
requestIdHeader: 'request-id',
requestIdHeader: false,
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000,
exposeHeadRoutes: true,
useSemicolonDelimiter: true
useSemicolonDelimiter: false
})
} catch (error) {
t.fail()
t.assert.fail()
}
fastify.get('/', function (req, reply) {
t.ok(req.log)
t.assert.ok(req.log)
reply.send({ hello: 'world' })
})
stream.once('data', listenAtLogLine => {
t.ok(listenAtLogLine, 'listen at log message is ok')
t.assert.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')
t.assert.ok(line.reqId, 'reqId is defined')
t.assert.strictEqual(line.someBinding, 'value', 'child logger binding is set')
t.assert.ok(line.req, 'req is defined')
t.assert.strictEqual(line.msg, 'incoming request', 'message is set')
t.assert.strictEqual(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')
t.assert.strictEqual(line.reqId, id)
t.assert.ok(line.reqId, 'reqId is defined')
t.assert.strictEqual(line.someBinding, 'value', 'child logger binding is set')
t.assert.ok(line.res, 'res is defined')
t.assert.strictEqual(line.msg, 'request completed', 'message is set')
t.assert.strictEqual(line.res.statusCode, 200, 'statusCode is 200')
t.assert.ok(line.responseTime, 'responseTime is defined')
})
})
})
fastify.listen({ port: 0, host: localhost }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
t.assert.ifError(err)
t.after(() => { fastify.close() })
http.get(`http://${localhostForURL}:${fastify.server.address().port}`)
http.get(`http://${localhostForURL}:${fastify.server.address().port}`, () => {
done()
})
})
})
@@ -350,51 +350,32 @@ test('deepFreezeObject() should not throw on TypedArray', t => {
const frozenObject = deepFreezeObject(object)
// Buffers should not be frozen, as they are Uint8Array inherited instances
t.equal(Object.isFrozen(frozenObject.buffer), false)
t.assert.strictEqual(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.assert.strictEqual(Object.isFrozen(frozenObject), true)
t.assert.strictEqual(Object.isFrozen(frozenObject.object), true)
t.assert.strictEqual(Object.isFrozen(frozenObject.object.nested), true)
t.pass()
t.assert.ok(true)
} catch (error) {
t.fail()
t.assert.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)
t.assert.strictEqual(withDisabledTimeout.initialConfig.pluginTimeout, 0)
const withInvalidTimeout = Fastify({ pluginTimeout: undefined })
t.equal(withInvalidTimeout.initialConfig.pluginTimeout, 10000)
t.end()
t.assert.strictEqual(withInvalidTimeout.initialConfig.pluginTimeout, 10000)
})
test('Should not mutate the options object outside Fastify', async t => {
const options = Object.freeze({})
try {
Fastify(options)
t.assert.ok(true)
} catch (error) {
t.assert.fail(error.message)
}
})

View File

@@ -1,34 +1,34 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Fastify = require('../..')
const loggerUtils = require('../../lib/logger')
const loggerUtils = require('../../lib/logger-factory')
const { serializers } = require('../../lib/logger-pino')
test('time resolution', t => {
t.plan(2)
t.equal(typeof loggerUtils.now, 'function')
t.equal(typeof loggerUtils.now(), 'number')
t.assert.strictEqual(typeof loggerUtils.now, 'function')
t.assert.strictEqual(typeof loggerUtils.now(), 'number')
})
test('The logger should add a unique id for every request', t => {
test('The logger should add a unique id for every request', (t, done) => {
const ids = []
const fastify = Fastify()
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
const queue = new Queue()
for (let i = 0; i < 10; i++) {
queue.add(checkId)
}
queue.add(() => {
fastify.close()
t.end()
done()
})
})
@@ -37,24 +37,24 @@ test('The logger should add a unique id for every request', t => {
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.ok(ids.indexOf(payload.id) === -1, 'the id should not be duplicated')
t.assert.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 => {
test('The logger should not reuse request id header for req.id', (t, done) => {
const fastify = Fastify()
fastify.get('/', (req, reply) => {
t.ok(req.id)
t.assert.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.assert.ifError(err)
fastify.inject({
method: 'GET',
@@ -63,11 +63,40 @@ test('The logger should reuse request id header for req.id', t => {
'Request-Id': 'request-id-1'
}
}, (err, res) => {
t.error(err)
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.ok(payload.id === 'request-id-1', 'the request id from the header should be returned')
t.assert.ok(payload.id !== 'request-id-1', 'the request id from the header should not be returned with default configuration')
t.assert.ok(payload.id === 'req-1') // first request id when using the default configuration
fastify.close()
t.end()
done()
})
})
})
test('The logger should reuse request id header for req.id if requestIdHeader is set', (t, done) => {
const fastify = Fastify({
requestIdHeader: 'request-id'
})
fastify.get('/', (req, reply) => {
t.assert.ok(req.id)
reply.send({ id: req.id })
})
fastify.listen({ port: 0 }, err => {
t.assert.ifError(err)
fastify.inject({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Request-Id': 'request-id-1'
}
}, (err, res) => {
t.assert.ifError(err)
const payload = JSON.parse(res.payload)
t.assert.ok(payload.id === 'request-id-1', 'the request id from the header should be returned')
fastify.close()
done()
})
})
})
@@ -108,26 +137,26 @@ test('The logger should error if both stream and file destination are given', t
}
})
} catch (err) {
t.equal(err.code, 'FST_ERR_LOG_INVALID_DESTINATION')
t.equal(err.message, 'Cannot specify both logger.stream and logger.file options')
t.assert.strictEqual(err.code, 'FST_ERR_LOG_INVALID_DESTINATION')
t.assert.strictEqual(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({
const serialized = serializers.req({
method: 'GET',
url: '/',
socket: undefined,
headers: {}
})
t.same(serialized, {
t.assert.deepStrictEqual(serialized, {
method: 'GET',
url: '/',
version: undefined,
hostname: undefined,
host: undefined,
remoteAddress: undefined,
remotePort: undefined
})

View File

@@ -1,19 +1,18 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const pluginUtilsPublic = require('../../lib/pluginUtils.js')
const pluginUtilsPublic = require('../../lib/plugin-utils.js')
const symbols = require('../../lib/symbols.js')
const pluginUtils = require('../../lib/pluginUtils')[symbols.kTestInternals]
const pluginUtils = require('../../lib/plugin-utils')[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))
t.assert.ok(pluginUtils.shouldSkipOverride(yes))
t.assert.ok(!pluginUtils.shouldSkipOverride(no))
function yes () {}
function no () {}
@@ -26,7 +25,7 @@ test('getPluginName should return plugin name if the file is cached', t => {
require.cache[expectedPluginName] = { exports: fn }
const pluginName = pluginUtilsPublic.getPluginName(fn)
t.equal(pluginName, expectedPluginName)
t.assert.strictEqual(pluginName, expectedPluginName)
})
test('getPluginName should not throw when require.cache is undefined', t => {
@@ -36,12 +35,12 @@ test('getPluginName should not throw when require.cache is undefined', t => {
}
const cache = require.cache
require.cache = undefined
t.teardown(() => {
t.after(() => {
require.cache = cache
})
const pluginName = pluginUtilsPublic.getPluginName(example)
t.equal(pluginName, 'example')
t.assert.strictEqual(pluginName, 'example')
})
test("getMeta should return the object stored with the 'plugin-meta' symbol", t => {
@@ -50,7 +49,7 @@ test("getMeta should return the object stored with the 'plugin-meta' symbol", t
const meta = { hello: 'world' }
fn[Symbol.for('plugin-meta')] = meta
t.same(meta, pluginUtils.getMeta(fn))
t.assert.deepStrictEqual(meta, pluginUtils.getMeta(fn))
function fn () {}
})
@@ -73,9 +72,9 @@ test('checkDecorators should check if the given decorator is present in the inst
try {
pluginUtils.checkDecorators.call(context, fn)
t.pass('Everything ok')
t.assert.ok('Everything ok')
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
function fn () {}
@@ -99,9 +98,9 @@ test('checkDecorators should check if the given decorator is present in the inst
try {
pluginUtils.checkDecorators.call(context, fn)
t.fail('should throw')
t.assert.fail('should throw')
} catch (err) {
t.equal(err.message, "The decorator 'plugin' is not present in Request")
t.assert.strictEqual(err.message, "The decorator 'plugin' is not present in Request")
}
function fn () {}
@@ -121,9 +120,9 @@ test('checkDecorators should accept optional decorators', t => {
try {
pluginUtils.checkDecorators.call(context, fn)
t.pass('Everything ok')
t.assert.ok('Everything ok')
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
function fn () {}
@@ -141,9 +140,9 @@ test('checkDependencies should check if the given dependency is present in the i
try {
pluginUtils.checkDependencies.call(context, fn)
t.pass('Everything ok')
t.assert.ok('Everything ok')
} catch (err) {
t.fail(err)
t.assert.fail(err)
}
function fn () {}
@@ -162,9 +161,9 @@ test('checkDependencies should check if the given dependency is present in the i
try {
pluginUtils.checkDependencies.call(context, fn)
t.fail('should throw')
t.assert.fail('should throw')
} catch (err) {
t.equal(err.message, "The dependency 'plugin' of plugin 'test-plugin' is not registered")
t.assert.strictEqual(err.message, "The dependency 'plugin' of plugin 'test-plugin' is not registered")
}
function fn () {}

View File

@@ -0,0 +1,63 @@
'use strict'
const { test } = require('node:test')
const { kTestInternals } = require('../../lib/symbols')
const PonyPromise = require('../../lib/promise')
test('withResolvers', async (t) => {
t.plan(3)
await t.test('resolve', async (t) => {
t.plan(1)
const { promise, resolve } = PonyPromise.withResolvers()
resolve(true)
t.assert.ok(await promise)
})
await t.test('reject', async (t) => {
t.plan(1)
const { promise, reject } = PonyPromise.withResolvers()
await t.assert.rejects(async () => {
reject(Error('reject'))
return promise
}, {
name: 'Error',
message: 'reject'
})
})
await t.test('thenable', async (t) => {
t.plan(1)
const { promise, resolve } = PonyPromise.withResolvers()
resolve(true)
promise.then((value) => {
t.assert.ok(value)
})
})
})
test('withResolvers - ponyfill', async (t) => {
await t.test('resolve', async (t) => {
t.plan(1)
const { promise, resolve } = PonyPromise[kTestInternals].withResolvers()
resolve(true)
t.assert.ok(await promise)
})
await t.test('reject', async (t) => {
t.plan(1)
const { promise, reject } = PonyPromise[kTestInternals].withResolvers()
await t.assert.rejects(async () => {
reject(Error('reject'))
return promise
}, {
name: 'Error',
message: 'reject'
})
})
await t.test('thenable', async (t) => {
t.plan(1)
const { promise, resolve } = PonyPromise.withResolvers()
resolve(true)
promise.then((value) => {
t.assert.ok(value)
})
})
})

View File

@@ -1,6 +1,6 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const { kReplyCacheSerializeFns, kRouteContext } = require('../../lib/symbols')
const Fastify = require('../../fastify')
@@ -50,8 +50,11 @@ function getResponseSchema () {
content: {
'application/json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'number' }
type: 'object',
properties: {
fullName: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
@@ -59,10 +62,10 @@ function getResponseSchema () {
}
}
test('Reply#compileSerializationSchema', t => {
test('Reply#compileSerializationSchema', async t => {
t.plan(4)
t.test('Should return a serialization function', async t => {
await t.test('Should return a serialization function', async t => {
const fastify = Fastify()
t.plan(4)
@@ -70,14 +73,14 @@ test('Reply#compileSerializationSchema', t => {
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))
t.assert.ok(serialize instanceof Function)
t.assert.ok(typeof serialize(input) === 'string')
t.assert.strictEqual(serialize(input), JSON.stringify(input))
try {
serialize({ world: 'foo' })
} catch (err) {
t.equal(err.message, '"hello" is required!')
t.assert.strictEqual(err.message, '"hello" is required!')
}
reply.send({ hello: 'world' })
@@ -89,7 +92,7 @@ test('Reply#compileSerializationSchema', t => {
})
})
t.test('Should reuse the serialize fn across multiple invocations - Route without schema',
await t.test('Should reuse the serialize fn across multiple invocations - Route without schema',
async t => {
const fastify = Fastify()
let serialize = null
@@ -104,20 +107,20 @@ test('Reply#compileSerializationSchema', t => {
counter++
if (counter > 1) {
const newSerialize = reply.compileSerializationSchema(schemaObj)
t.equal(serialize, newSerialize, 'Are the same validate function')
t.assert.strictEqual(serialize, newSerialize, 'Are the same validate function')
serialize = newSerialize
} else {
t.pass('build the schema compilation function')
t.assert.ok(true, 'build the schema compilation function')
serialize = reply.compileSerializationSchema(schemaObj)
}
t.type(serialize, Function)
t.equal(serialize(input), JSON.stringify(input))
t.assert.ok(serialize instanceof Function)
t.assert.strictEqual(serialize(input), JSON.stringify(input))
try {
serialize({ world: 'foo' })
} catch (err) {
t.equal(err.message, '"hello" is required!')
t.assert.strictEqual(err.message, '"hello" is required!')
}
reply.send({ hello: 'world' })
@@ -130,36 +133,36 @@ test('Reply#compileSerializationSchema', t => {
fastify.inject('/')
])
t.equal(counter, 4)
t.assert.strictEqual(counter, 4)
}
)
t.test('Should use the custom serializer compiler for the route',
await 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')
t.assert.strictEqual(schema, schemaObj)
t.assert.strictEqual(url, '/')
t.assert.strictEqual(method, 'GET')
t.assert.strictEqual(httpStatus, '201')
return input => {
called++
t.same(input, { hello: 'world' })
t.assert.deepStrictEqual(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')
t.assert.strictEqual(schema, schemaObj)
t.assert.strictEqual(url, '/user')
t.assert.strictEqual(method, 'GET')
t.assert.strictEqual(httpStatus, '3xx')
t.assert.strictEqual(contentType, 'application/json')
return input => {
t.same(input, { fullName: 'Jone', phone: 1090243795 })
t.assert.deepStrictEqual(input, { fullName: 'Jone', phone: 1090243795 })
return JSON.stringify(input)
}
}
@@ -172,10 +175,10 @@ test('Reply#compileSerializationSchema', t => {
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)
t.assert.strictEqual(first, second)
t.assert.ok(first(input), JSON.stringify(input))
t.assert.ok(second(input), JSON.stringify(input))
t.assert.strictEqual(called, 2)
reply.send({ hello: 'world' })
})
@@ -183,7 +186,7 @@ test('Reply#compileSerializationSchema', t => {
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))
t.assert.ok(first(input), JSON.stringify(input))
reply.send(input)
})
@@ -199,7 +202,7 @@ test('Reply#compileSerializationSchema', t => {
}
)
t.test('Should build a WeakMap for cache when called', async t => {
await t.test('Should build a WeakMap for cache when called', async t => {
const fastify = Fastify()
t.plan(4)
@@ -207,10 +210,10 @@ test('Reply#compileSerializationSchema', t => {
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))
t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.assert.strictEqual(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
t.assert.ok(reply[kRouteContext][kReplyCacheSerializeFns] instanceof WeakMap)
t.assert.strictEqual(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
reply.send({ hello: 'world' })
})
@@ -222,10 +225,10 @@ test('Reply#compileSerializationSchema', t => {
})
})
test('Reply#getSerializationFunction', t => {
test('Reply#getSerializationFunction', async t => {
t.plan(3)
t.test('Should retrieve the serialization function from the Schema definition',
await t.test('Should retrieve the serialization function from the Schema definition',
async t => {
const fastify = Fastify()
const okInput201 = {
@@ -264,8 +267,11 @@ test('Reply#getSerializationFunction', t => {
'/:id',
{
params: {
id: {
type: 'integer'
type: 'object',
properties: {
id: {
type: 'integer'
}
}
},
schema: {
@@ -285,18 +291,18 @@ test('Reply#getSerializationFunction', t => {
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)
t.assert.ok(serialize4xx instanceof Function)
t.assert.ok(serialize201 instanceof Function)
t.assert.ok(serializeJson3xx instanceof Function)
t.assert.strictEqual(serialize4xx(okInput4xx), JSON.stringify(okInput4xx))
t.assert.strictEqual(serialize201(okInput201), JSON.stringify(okInput201))
t.assert.strictEqual(serializeJson3xx(okInput3xx), JSON.stringify(okInput3xx))
t.assert.ok(!serializeUndefined)
try {
serialize4xx(notOkInput4xx)
} catch (err) {
t.equal(
t.assert.strictEqual(
err.message,
'The value "something" cannot be converted to an integer.'
)
@@ -305,13 +311,13 @@ test('Reply#getSerializationFunction', t => {
try {
serialize201(notOkInput201)
} catch (err) {
t.equal(err.message, '"status" is required!')
t.assert.strictEqual(err.message, '"status" is required!')
}
try {
serializeJson3xx(noOkInput3xx)
} catch (err) {
t.equal(err.message, 'The value "phone" cannot be converted to a number.')
t.assert.strictEqual(err.message, 'The value "phone" cannot be converted to a number.')
}
reply.status(201).send(okInput201)
@@ -320,9 +326,9 @@ test('Reply#getSerializationFunction', t => {
const serialize4xx = reply.getSerializationFunction('4xx')
const serializeJson3xx = reply.getSerializationFunction('3xx', 'application/json')
t.equal(serialize4xx, cached4xx)
t.equal(serialize201, cached201)
t.equal(serializeJson3xx, cachedJson3xx)
t.assert.strictEqual(serialize4xx, cached4xx)
t.assert.strictEqual(serialize201, cached201)
t.assert.strictEqual(serializeJson3xx, cachedJson3xx)
reply.status(401).send(okInput4xx)
}
}
@@ -335,7 +341,7 @@ test('Reply#getSerializationFunction', t => {
}
)
t.test('Should retrieve the serialization function from the cached one',
await t.test('Should retrieve the serialization function from the cached one',
async t => {
const fastify = Fastify()
@@ -356,8 +362,11 @@ test('Reply#getSerializationFunction', t => {
'/:id',
{
params: {
id: {
type: 'integer'
type: 'object',
properties: {
id: {
type: 'integer'
}
}
}
},
@@ -367,26 +376,26 @@ test('Reply#getSerializationFunction', t => {
if (Number(id) === 1) {
const serialize = reply.compileSerializationSchema(schemaObj)
t.type(serialize, Function)
t.equal(serialize(okInput), JSON.stringify(okInput))
t.assert.ok(serialize instanceof Function)
t.assert.strictEqual(serialize(okInput), JSON.stringify(okInput))
try {
serialize(notOkInput)
} catch (err) {
t.equal(err.message, '"hello" is required!')
t.assert.strictEqual(err.message, '"hello" is required!')
}
cached = serialize
} else {
const serialize = reply.getSerializationFunction(schemaObj)
t.equal(serialize, cached)
t.equal(serialize(okInput), JSON.stringify(okInput))
t.assert.strictEqual(serialize, cached)
t.assert.strictEqual(serialize(okInput), JSON.stringify(okInput))
try {
serialize(notOkInput)
} catch (err) {
t.equal(err.message, '"hello" is required!')
t.assert.strictEqual(err.message, '"hello" is required!')
}
}
@@ -401,16 +410,16 @@ test('Reply#getSerializationFunction', t => {
}
)
t.test('Should not instantiate a WeakMap if it is not needed', async t => {
await 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)
t.assert.ok(!reply.getSerializationFunction(getDefaultSchema()))
t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.assert.ok(!reply.getSerializationFunction('200'))
t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
reply.send({ hello: 'world' })
})
@@ -422,10 +431,10 @@ test('Reply#getSerializationFunction', t => {
})
})
test('Reply#serializeInput', t => {
test('Reply#serializeInput', async t => {
t.plan(6)
t.test(
await t.test(
'Should throw if missed serialization function from HTTP status',
async t => {
const fastify = Fastify()
@@ -441,8 +450,8 @@ test('Reply#serializeInput', t => {
method: 'GET'
})
t.equal(result.statusCode, 500)
t.same(result.json(), {
t.assert.strictEqual(result.statusCode, 500)
t.assert.deepStrictEqual(result.json(), {
statusCode: 500,
code: 'FST_ERR_MISSING_SERIALIZATION_FN',
error: 'Internal Server Error',
@@ -451,7 +460,7 @@ test('Reply#serializeInput', t => {
}
)
t.test(
await t.test(
'Should throw if missed serialization function from HTTP status with specific content type',
async t => {
const fastify = Fastify()
@@ -465,8 +474,11 @@ test('Reply#serializeInput', t => {
content: {
'application/json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'number' }
type: 'object',
properties: {
fullName: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
@@ -482,8 +494,8 @@ test('Reply#serializeInput', t => {
method: 'GET'
})
t.equal(result.statusCode, 500)
t.same(result.json(), {
t.assert.strictEqual(result.statusCode, 500)
t.assert.deepStrictEqual(result.json(), {
statusCode: 500,
code: 'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
error: 'Internal Server Error',
@@ -492,7 +504,7 @@ test('Reply#serializeInput', t => {
}
)
t.test('Should use a serializer fn from HTTP status', async t => {
await t.test('Should use a serializer fn from HTTP status', async t => {
const fastify = Fastify()
const okInput201 = {
status: 'ok',
@@ -525,8 +537,11 @@ test('Reply#serializeInput', t => {
'/',
{
params: {
id: {
type: 'integer'
type: 'object',
properties: {
id: {
type: 'integer'
}
}
},
schema: {
@@ -534,16 +549,16 @@ test('Reply#serializeInput', t => {
}
},
(req, reply) => {
t.equal(
t.assert.strictEqual(
reply.serializeInput(okInput4xx, '4xx'),
JSON.stringify(okInput4xx)
)
t.equal(
t.assert.strictEqual(
reply.serializeInput(okInput201, 201),
JSON.stringify(okInput201)
)
t.equal(
t.assert.strictEqual(
reply.serializeInput(okInput3xx, {}, '3xx', 'application/json'),
JSON.stringify(okInput3xx)
)
@@ -551,13 +566,13 @@ test('Reply#serializeInput', t => {
try {
reply.serializeInput(noOkInput3xx, '3xx', 'application/json')
} catch (err) {
t.equal(err.message, 'The value "phone" cannot be converted to a number.')
t.assert.strictEqual(err.message, 'The value "phone" cannot be converted to a number.')
}
try {
reply.serializeInput(notOkInput4xx, '4xx')
} catch (err) {
t.equal(
t.assert.strictEqual(
err.message,
'The value "something" cannot be converted to an integer.'
)
@@ -566,7 +581,7 @@ test('Reply#serializeInput', t => {
try {
reply.serializeInput(notOkInput201, 201)
} catch (err) {
t.equal(err.message, '"status" is required!')
t.assert.strictEqual(err.message, '"status" is required!')
}
reply.status(204).send('')
@@ -579,7 +594,7 @@ test('Reply#serializeInput', t => {
})
})
t.test(
await t.test(
'Should compile a serializer out of a schema if serializer fn missed',
async t => {
let compilerCalled = 0
@@ -588,14 +603,14 @@ test('Reply#serializeInput', t => {
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, '/')
t.assert.strictEqual(schema, schemaObj)
t.assert.ok(!httpStatus)
t.assert.strictEqual(method, 'GET')
t.assert.strictEqual(url, '/')
compilerCalled++
return input => {
t.equal(input, testInput)
t.assert.strictEqual(input, testInput)
serializerCalled++
return JSON.stringify(input)
}
@@ -604,12 +619,12 @@ test('Reply#serializeInput', t => {
t.plan(10)
fastify.get('/', { serializerCompiler }, (req, reply) => {
t.equal(
t.assert.strictEqual(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
t.equal(
t.assert.strictEqual(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
@@ -622,12 +637,12 @@ test('Reply#serializeInput', t => {
method: 'GET'
})
t.equal(compilerCalled, 1)
t.equal(serializerCalled, 2)
t.assert.strictEqual(compilerCalled, 1)
t.assert.strictEqual(serializerCalled, 2)
}
)
t.test('Should use a cached serializer fn', async t => {
await t.test('Should use a cached serializer fn', async t => {
let compilerCalled = 0
let serializerCalled = 0
let cached
@@ -635,15 +650,15 @@ test('Reply#serializeInput', t => {
const schemaObj = getDefaultSchema()
const fastify = Fastify()
const serializer = input => {
t.equal(input, testInput)
t.assert.strictEqual(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, '/')
t.assert.strictEqual(schema, schemaObj)
t.assert.ok(!httpStatus)
t.assert.strictEqual(method, 'GET')
t.assert.strictEqual(url, '/')
compilerCalled++
return serializer
@@ -652,14 +667,14 @@ test('Reply#serializeInput', t => {
t.plan(12)
fastify.get('/', { serializerCompiler }, (req, reply) => {
t.equal(
t.assert.strictEqual(
reply.serializeInput(testInput, schemaObj),
JSON.stringify(testInput)
)
cached = reply.getSerializationFunction(schemaObj)
t.equal(
t.assert.strictEqual(
reply.serializeInput(testInput, schemaObj),
cached(testInput)
)
@@ -672,21 +687,21 @@ test('Reply#serializeInput', t => {
method: 'GET'
})
t.equal(cached, serializer)
t.equal(compilerCalled, 1)
t.equal(serializerCalled, 3)
t.assert.strictEqual(cached, serializer)
t.assert.strictEqual(compilerCalled, 1)
t.assert.strictEqual(serializerCalled, 3)
})
t.test('Should instantiate a WeakMap after first call', async t => {
await 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)
t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
t.assert.strictEqual(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
t.assert.ok(reply[kRouteContext][kReplyCacheSerializeFns] instanceof WeakMap)
reply.send({ hello: 'world' })
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
'use strict'
const { test } = require('tap')
const { reqIdGenFactory } = require('../../lib/reqIdGenFactory')
const { test } = require('node:test')
const { reqIdGenFactory } = require('../../lib/req-id-gen-factory')
test('should create incremental ids deterministically', t => {
t.plan(1)
@@ -9,18 +9,18 @@ test('should create incremental ids deterministically', t => {
for (let i = 1; i < 1e4; ++i) {
if (reqIdGen() !== 'req-' + i.toString(36)) {
t.fail()
t.assert.fail()
break
}
}
t.pass()
t.assert.ok(true)
})
test('should have prefix "req-"', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory()
t.ok(reqIdGen().startsWith('req-'))
t.assert.ok(reqIdGen().startsWith('req-'))
})
test('different id generator functions should have separate internal counters', t => {
@@ -28,18 +28,18 @@ test('different id generator functions should have separate internal counters',
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')
t.assert.strictEqual(reqIdGenA(), 'req-1')
t.assert.strictEqual(reqIdGenA(), 'req-2')
t.assert.strictEqual(reqIdGenB(), 'req-1')
t.assert.strictEqual(reqIdGenA(), 'req-3')
t.assert.strictEqual(reqIdGenB(), 'req-2')
})
test('should start counting with 1', t => {
t.plan(1)
const reqIdGen = reqIdGenFactory()
t.equal(reqIdGen(), 'req-1')
t.assert.strictEqual(reqIdGen(), 'req-1')
})
test('should handle requestIdHeader and return provided id in header', t => {
@@ -47,7 +47,7 @@ test('should handle requestIdHeader and return provided id in header', t => {
const reqIdGen = reqIdGenFactory('id')
t.equal(reqIdGen({ headers: { id: '1337' } }), '1337')
t.assert.strictEqual(reqIdGen({ headers: { id: '1337' } }), '1337')
})
test('should handle requestIdHeader and fallback if id is not provided in header', t => {
@@ -55,7 +55,7 @@ test('should handle requestIdHeader and fallback if id is not provided in header
const reqIdGen = reqIdGenFactory('id')
t.equal(reqIdGen({ headers: { notId: '1337' } }), 'req-1')
t.assert.strictEqual(reqIdGen({ headers: { notId: '1337' } }), 'req-1')
})
test('should handle requestIdHeader and increment internal counter if no header was provided', t => {
@@ -63,10 +63,10 @@ test('should handle requestIdHeader and increment internal counter if no header
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')
t.assert.strictEqual(reqIdGen({ headers: {} }), 'req-1')
t.assert.strictEqual(reqIdGen({ headers: {} }), 'req-2')
t.assert.strictEqual(reqIdGen({ headers: { id: '1337' } }), '1337')
t.assert.strictEqual(reqIdGen({ headers: {} }), 'req-3')
})
test('should use optGenReqId to generate ids', t => {
@@ -80,10 +80,10 @@ test('should use optGenReqId to generate ids', t => {
}
const reqIdGen = reqIdGenFactory(undefined, optGenReqId)
t.equal(gotCalled, false)
t.equal(reqIdGen(), '1')
t.equal(gotCalled, true)
t.equal(reqIdGen(), '2')
t.assert.strictEqual(gotCalled, false)
t.assert.strictEqual(reqIdGen(), '1')
t.assert.strictEqual(gotCalled, true)
t.assert.strictEqual(reqIdGen(), '2')
})
test('should use optGenReqId to generate ids if requestIdHeader is used but not provided', t => {
@@ -97,22 +97,22 @@ test('should use optGenReqId to generate ids if requestIdHeader is used but not
}
const reqIdGen = reqIdGenFactory('reqId', optGenReqId)
t.equal(gotCalled, false)
t.equal(reqIdGen({ headers: {} }), '1')
t.equal(gotCalled, true)
t.equal(reqIdGen({ headers: {} }), '2')
t.assert.strictEqual(gotCalled, false)
t.assert.strictEqual(reqIdGen({ headers: {} }), '1')
t.assert.strictEqual(gotCalled, true)
t.assert.strictEqual(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()
t.assert.fail()
}
const reqIdGen = reqIdGenFactory('reqId', optGenReqId)
t.equal(reqIdGen({ headers: { reqId: 'r1' } }), 'r1')
t.equal(reqIdGen({ headers: { reqId: 'r2' } }), 'r2')
t.assert.strictEqual(reqIdGen({ headers: { reqId: 'r1' } }), 'r1')
t.assert.strictEqual(reqIdGen({ headers: { reqId: 'r2' } }), 'r2')
})
test('should fallback to use optGenReqId to generate ids if requestIdHeader is sometimes provided', t => {
@@ -126,8 +126,8 @@ test('should fallback to use optGenReqId to generate ids if requestIdHeader is s
}
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)
t.assert.strictEqual(reqIdGen({ headers: { reqId: 'r1' } }), 'r1')
t.assert.strictEqual(gotCalled, false)
t.assert.strictEqual(reqIdGen({ headers: {} }), '1')
t.assert.strictEqual(gotCalled, true)
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,10 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const Request = require('../../lib/request')
const Context = require('../../lib/context')
const {
kPublicRouteContext,
kReply,
kRequest,
kOptions
@@ -43,40 +42,33 @@ test('Regular request', t => {
[kRequest]: Request,
[kOptions]: {
requestIdLogLabel: 'reqId'
}
},
server: {}
}
})
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)
t.assert.ok(request instanceof Request)
t.assert.ok(request.validateInput instanceof Function)
t.assert.ok(request.getValidationFunction instanceof Function)
t.assert.ok(request.compileValidationSchema instanceof Function)
t.assert.strictEqual(request.id, 'id')
t.assert.strictEqual(request.params, 'params')
t.assert.strictEqual(request.raw, req)
t.assert.strictEqual(request.query, 'query')
t.assert.strictEqual(request.headers, headers)
t.assert.strictEqual(request.log, 'log')
t.assert.strictEqual(request.ip, 'ip')
t.assert.strictEqual(request.ips, undefined)
t.assert.strictEqual(request.host, 'hostname')
t.assert.strictEqual(request.body, undefined)
t.assert.strictEqual(request.method, 'GET')
t.assert.strictEqual(request.url, '/')
t.assert.strictEqual(request.originalUrl, '/')
t.assert.strictEqual(request.socket, req.socket)
t.assert.strictEqual(request.protocol, 'http')
// 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()
t.assert.ok(!('undefined' in request))
})
test('Request with undefined config', t => {
@@ -104,45 +96,38 @@ test('Request with undefined config', t => {
[kRequest]: Request,
[kOptions]: {
requestIdLogLabel: 'reqId'
}
},
server: {}
}
})
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)
t.assert.ok(request, Request)
t.assert.ok(request.validateInput, Function)
t.assert.ok(request.getValidationFunction, Function)
t.assert.ok(request.compileValidationSchema, Function)
t.assert.strictEqual(request.id, 'id')
t.assert.strictEqual(request.params, 'params')
t.assert.strictEqual(request.raw, req)
t.assert.strictEqual(request.query, 'query')
t.assert.strictEqual(request.headers, headers)
t.assert.strictEqual(request.log, 'log')
t.assert.strictEqual(request.ip, 'ip')
t.assert.strictEqual(request.ips, undefined)
t.assert.strictEqual(request.hostname, 'hostname')
t.assert.strictEqual(request.body, undefined)
t.assert.strictEqual(request.method, 'GET')
t.assert.strictEqual(request.url, '/')
t.assert.strictEqual(request.originalUrl, '/')
t.assert.strictEqual(request.socket, req.socket)
t.assert.strictEqual(request.protocol, 'http')
// 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()
t.assert.ok(!('undefined' in request))
})
test('Regular request - hostname from authority', t => {
t.plan(2)
t.plan(3)
const headers = {
':authority': 'authority'
}
@@ -152,14 +137,39 @@ test('Regular request - hostname from authority', t => {
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'
},
server: {}
}
})
const request = new Request('id', 'params', req, 'query', 'log')
t.type(request, Request)
t.equal(request.hostname, 'authority')
const request = new Request('id', 'params', req, 'query', 'log', context)
t.assert.ok(request instanceof Request)
t.assert.strictEqual(request.host, 'authority')
t.assert.strictEqual(request.port, null)
})
test('Regular request - host header has precedence over authority', t => {
t.plan(2)
t.plan(3)
const headers = {
host: 'hostname',
':authority': 'authority'
@@ -170,13 +180,38 @@ test('Regular request - host header has precedence over authority', t => {
socket: { remoteAddress: 'ip' },
headers
}
const request = new Request('id', 'params', req, 'query', 'log')
t.type(request, Request)
t.equal(request.hostname, 'hostname')
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'
},
server: {}
}
})
const request = new Request('id', 'params', req, 'query', 'log', context)
t.assert.ok(request instanceof Request)
t.assert.strictEqual(request.host, 'hostname')
t.assert.strictEqual(request.port, null)
})
test('Request with trust proxy', t => {
t.plan(22)
t.plan(18)
const headers = {
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
'x-forwarded-host': 'example.com'
@@ -213,28 +248,24 @@ test('Request with trust proxy', t => {
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)
t.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.id, 'id')
t.assert.strictEqual(request.params, 'params')
t.assert.deepStrictEqual(request.raw, req)
t.assert.strictEqual(request.query, 'query')
t.assert.strictEqual(request.headers, headers)
t.assert.strictEqual(request.log, 'log')
t.assert.strictEqual(request.ip, '2.2.2.2')
t.assert.deepStrictEqual(request.ips, ['ip', '1.1.1.1', '2.2.2.2'])
t.assert.strictEqual(request.host, 'example.com')
t.assert.strictEqual(request.body, undefined)
t.assert.strictEqual(request.method, 'GET')
t.assert.strictEqual(request.url, '/')
t.assert.strictEqual(request.socket, req.socket)
t.assert.strictEqual(request.protocol, 'http')
t.assert.ok(request.validateInput instanceof Function)
t.assert.ok(request.getValidationFunction instanceof Function)
t.assert.ok(request.compileValidationSchema instanceof Function)
})
test('Request with trust proxy, encrypted', t => {
@@ -252,8 +283,8 @@ test('Request with trust proxy, encrypted', t => {
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.protocol, 'https')
t.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.protocol, 'https')
})
test('Request with trust proxy - no x-forwarded-host header', t => {
@@ -268,11 +299,35 @@ test('Request with trust proxy - no x-forwarded-host header', t => {
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'
},
server: {}
}
})
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'hostname')
const request = new TpRequest('id', 'params', req, 'query', 'log', context)
t.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.host, 'hostname')
})
test('Request with trust proxy - no x-forwarded-host header and fallback to authority', t => {
@@ -287,11 +342,35 @@ test('Request with trust proxy - no x-forwarded-host header and fallback to auth
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'
},
server: {}
}
})
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.type(request, TpRequest)
t.equal(request.hostname, 'authority')
const request = new TpRequest('id', 'params', req, 'query', 'log', context)
t.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.host, 'authority')
})
test('Request with trust proxy - x-forwarded-host header has precedence over host', t => {
@@ -310,8 +389,8 @@ test('Request with trust proxy - x-forwarded-host header has precedence over hos
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.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.host, 'example.com')
})
test('Request with trust proxy - handles multiple entries in x-forwarded-host/proto', t => {
@@ -329,9 +408,9 @@ test('Request with trust proxy - handles multiple entries in x-forwarded-host/pr
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')
t.assert.ok(request instanceof TpRequest)
t.assert.strictEqual(request.host, 'example.com')
t.assert.strictEqual(request.protocol, 'https')
})
test('Request with trust proxy - plain', t => {
@@ -349,7 +428,7 @@ test('Request with trust proxy - plain', t => {
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.same(request.protocol, 'http')
t.assert.deepStrictEqual(request.protocol, 'http')
})
test('Request with undefined socket', t => {
@@ -363,25 +442,49 @@ test('Request with undefined socket', t => {
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)
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'
},
server: {}
}
})
const request = new Request('id', 'params', req, 'query', 'log', context)
t.assert.ok(request instanceof Request)
t.assert.strictEqual(request.id, 'id')
t.assert.strictEqual(request.params, 'params')
t.assert.deepStrictEqual(request.raw, req)
t.assert.strictEqual(request.query, 'query')
t.assert.strictEqual(request.headers, headers)
t.assert.strictEqual(request.log, 'log')
t.assert.strictEqual(request.ip, undefined)
t.assert.strictEqual(request.ips, undefined)
t.assert.strictEqual(request.host, 'hostname')
t.assert.deepStrictEqual(request.body, undefined)
t.assert.strictEqual(request.method, 'GET')
t.assert.strictEqual(request.url, '/')
t.assert.strictEqual(request.protocol, undefined)
t.assert.deepStrictEqual(request.socket, req.socket)
t.assert.ok(request.validateInput instanceof Function)
t.assert.ok(request.getValidationFunction instanceof Function)
t.assert.ok(request.compileValidationSchema instanceof Function)
})
test('Request with trust proxy and undefined socket', t => {
@@ -399,5 +502,5 @@ test('Request with trust proxy and undefined socket', t => {
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
t.same(request.protocol, undefined)
t.assert.deepStrictEqual(request.protocol, undefined)
})

View File

@@ -0,0 +1,40 @@
const { sep } = require('node:path')
const { test } = require('node:test')
const Fastify = require('../../fastify')
test('SchemaController are NOT loaded when the controllers are custom', async t => {
const app = Fastify({
schemaController: {
compilersFactory: {
buildValidator: () => () => { },
buildSerializer: () => () => { }
}
}
})
await app.ready()
const loaded = Object.keys(require.cache)
const ajvModule = loaded.find((path) => path.includes(`@fastify${sep}ajv-compiler`))
const stringifyModule = loaded.find((path) => path.includes(`@fastify${sep}fast-json-stringify-compiler`))
t.assert.equal(ajvModule, undefined, 'Ajv compiler is loaded')
t.assert.equal(stringifyModule, undefined, 'Stringify compiler is loaded')
})
test('SchemaController are loaded when the controllers are not custom', async t => {
const app = Fastify()
await app.ready()
const loaded = Object.keys(require.cache)
const ajvModule = loaded.find((path) => path.includes(`@fastify${sep}ajv-compiler`))
const stringifyModule = loaded.find((path) => path.includes(`@fastify${sep}fast-json-stringify-compiler`))
t.after(() => {
delete require.cache[ajvModule]
delete require.cache[stringifyModule]
})
t.assert.ok(ajvModule, 'Ajv compiler is loaded')
t.assert.ok(stringifyModule, 'Stringify compiler is loaded')
})

View File

@@ -1,6 +1,6 @@
'use strict'
const { test } = require('tap')
const { test } = require('node:test')
const proxyquire = require('proxyquire')
const Fastify = require('../../fastify')
@@ -15,7 +15,7 @@ test('start listening', async t => {
const { server, listen } = createServer({}, handler)
await listen.call(Fastify(), { port: 0, host: 'localhost' })
server.close()
t.pass('server started')
t.assert.ok(true, 'server started')
})
test('DNS errors does not stop the main server on localhost - promise interface', async t => {
@@ -29,10 +29,10 @@ test('DNS errors does not stop the main server on localhost - promise interface'
const { server, listen } = createServer({}, handler)
await listen.call(Fastify(), { port: 0, host: 'localhost' })
server.close()
t.pass('server started')
t.assert.ok(true, 'server started')
})
test('DNS errors does not stop the main server on localhost - callback interface', t => {
test('DNS errors does not stop the main server on localhost - callback interface', (t, done) => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
@@ -43,13 +43,14 @@ test('DNS errors does not stop the main server on localhost - callback interface
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
t.assert.ifError(err)
server.close()
t.pass('server started')
t.assert.ok(true, 'server started')
done()
})
})
test('DNS returns empty binding', t => {
test('DNS returns empty binding', (t, done) => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
@@ -60,13 +61,14 @@ test('DNS returns empty binding', t => {
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
t.assert.ifError(err)
server.close()
t.pass('server started')
t.assert.ok(true, 'server started')
done()
})
})
test('DNS returns more than two binding', t => {
test('DNS returns more than two binding', (t, done) => {
t.plan(2)
const { createServer } = proxyquire('../../lib/server', {
'node:dns': {
@@ -81,8 +83,9 @@ test('DNS returns more than two binding', t => {
})
const { server, listen } = createServer({}, handler)
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
t.error(err)
t.assert.ifError(err)
server.close()
t.pass('server started')
t.assert.ok(true, 'server started')
done()
})
})

View File

@@ -1,7 +1,6 @@
'use strict'
const t = require('tap')
const test = t.test
const { test } = require('node:test')
const Ajv = require('ajv')
const ajv = new Ajv({ coerceTypes: true })
@@ -13,11 +12,11 @@ const { kSchemaVisited } = require('../../lib/symbols')
test('Symbols', t => {
t.plan(5)
t.equal(typeof symbols.responseSchema, 'symbol')
t.equal(typeof symbols.bodySchema, 'symbol')
t.equal(typeof symbols.querystringSchema, 'symbol')
t.equal(typeof symbols.paramsSchema, 'symbol')
t.equal(typeof symbols.headersSchema, 'symbol')
t.assert.strictEqual(typeof symbols.responseSchema, 'symbol')
t.assert.strictEqual(typeof symbols.bodySchema, 'symbol')
t.assert.strictEqual(typeof symbols.querystringSchema, 'symbol')
t.assert.strictEqual(typeof symbols.paramsSchema, 'symbol')
t.assert.strictEqual(typeof symbols.headersSchema, 'symbol')
})
;['compileSchemasForValidation',
@@ -26,15 +25,15 @@ test('Symbols', t => {
t.plan(2)
const context = {}
validation[func](context)
t.equal(typeof context[symbols.bodySchema], 'undefined')
t.equal(typeof context[symbols.responseSchema], 'undefined')
t.assert.strictEqual(typeof context[symbols.bodySchema], 'undefined')
t.assert.strictEqual(typeof context[symbols.responseSchema], 'undefined')
})
test(`${func} schema - missing output schema`, t => {
t.plan(1)
const context = { schema: {} }
validation[func](context, null)
t.equal(typeof context[symbols.responseSchema], 'undefined')
t.assert.strictEqual(typeof context[symbols.responseSchema], 'undefined')
})
})
@@ -59,8 +58,8 @@ test('build schema - output schema', t => {
}
}
validation.compileSchemasForSerialization(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.equal(typeof opts[symbols.responseSchema]['2xx'], 'function')
t.equal(typeof opts[symbols.responseSchema]['201'], 'function')
t.assert.strictEqual(typeof opts[symbols.responseSchema]['2xx'], 'function')
t.assert.strictEqual(typeof opts[symbols.responseSchema]['201'], 'function')
})
test('build schema - body schema', t => {
@@ -76,7 +75,7 @@ test('build schema - body schema', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.equal(typeof opts[symbols.bodySchema], 'function')
t.assert.strictEqual(typeof opts[symbols.bodySchema], 'function')
})
test('build schema - body with multiple content type schemas', t => {
@@ -101,15 +100,13 @@ test('build schema - body with multiple content type schemas', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.type(opts[symbols.bodySchema]['application/json'], 'function')
t.type(opts[symbols.bodySchema]['text/plain'], 'function')
t.assert.ok(opts[symbols.bodySchema]['application/json'], 'function')
t.assert.ok(opts[symbols.bodySchema]['text/plain'], 'function')
})
test('build schema - avoid repeated normalize schema', t => {
t.plan(3)
const serverConfig = {
jsonShorthand: true
}
const serverConfig = {}
const opts = {
schema: {
query: {
@@ -120,17 +117,15 @@ test('build schema - avoid repeated normalize schema', t => {
}
}
}
opts.schema = normalizeSchema({}, opts.schema, serverConfig)
t.not(kSchemaVisited, undefined)
t.equal(opts.schema[kSchemaVisited], true)
t.equal(opts.schema, normalizeSchema({}, opts.schema, serverConfig))
opts.schema = normalizeSchema(opts.schema, serverConfig)
t.assert.notStrictEqual(kSchemaVisited, undefined)
t.assert.strictEqual(opts.schema[kSchemaVisited], true)
t.assert.strictEqual(opts.schema, normalizeSchema(opts.schema, serverConfig))
})
test('build schema - query schema', t => {
t.plan(2)
const serverConfig = {
jsonShorthand: true
}
const serverConfig = {}
const opts = {
schema: {
query: {
@@ -141,28 +136,29 @@ test('build schema - query schema', t => {
}
}
}
opts.schema = normalizeSchema({}, opts.schema, serverConfig)
opts.schema = normalizeSchema(opts.schema, serverConfig)
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.type(opts[symbols.querystringSchema].schema.type, 'string')
t.equal(typeof opts[symbols.querystringSchema], 'function')
t.assert.ok(typeof opts[symbols.querystringSchema].schema.type === 'string')
t.assert.strictEqual(typeof opts[symbols.querystringSchema], 'function')
})
test('build schema - query schema abbreviated', t => {
t.plan(2)
const serverConfig = {
jsonShorthand: true
}
const serverConfig = {}
const opts = {
schema: {
query: {
hello: { type: 'string' }
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
opts.schema = normalizeSchema({}, opts.schema, serverConfig)
opts.schema = normalizeSchema(opts.schema, serverConfig)
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.type(opts[symbols.querystringSchema].schema.type, 'string')
t.equal(typeof opts[symbols.querystringSchema], 'function')
t.assert.ok(typeof opts[symbols.querystringSchema].schema.type === 'string')
t.assert.strictEqual(typeof opts[symbols.querystringSchema], 'function')
})
test('build schema - querystring schema', t => {
@@ -178,34 +174,33 @@ test('build schema - querystring schema', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.type(opts[symbols.querystringSchema].schema.type, 'string')
t.equal(typeof opts[symbols.querystringSchema], 'function')
t.assert.ok(typeof opts[symbols.querystringSchema].schema.type === 'string')
t.assert.strictEqual(typeof opts[symbols.querystringSchema], 'function')
})
test('build schema - querystring schema abbreviated', t => {
t.plan(2)
const serverConfig = {
jsonShorthand: true
}
const serverConfig = {}
const opts = {
schema: {
querystring: {
hello: { type: 'string' }
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
opts.schema = normalizeSchema({}, opts.schema, serverConfig)
opts.schema = normalizeSchema(opts.schema, serverConfig)
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.type(opts[symbols.querystringSchema].schema.type, 'string')
t.equal(typeof opts[symbols.querystringSchema], 'function')
t.assert.ok(typeof opts[symbols.querystringSchema].schema.type === 'string')
t.assert.strictEqual(typeof opts[symbols.querystringSchema], 'function')
})
test('build schema - must throw if querystring and query schema exist', t => {
t.plan(2)
try {
const serverConfig = {
jsonShorthand: true
}
const serverConfig = {}
const opts = {
schema: {
query: {
@@ -222,10 +217,10 @@ test('build schema - must throw if querystring and query schema exist', t => {
}
}
}
opts.schema = normalizeSchema({}, opts.schema, serverConfig)
opts.schema = normalizeSchema(opts.schema, serverConfig)
} catch (err) {
t.equal(err.code, 'FST_ERR_SCH_DUPLICATE')
t.equal(err.message, 'Schema with \'querystring\' already present!')
t.assert.strictEqual(err.code, 'FST_ERR_SCH_DUPLICATE')
t.assert.strictEqual(err.message, 'Schema with \'querystring\' already present!')
}
})
@@ -242,7 +237,7 @@ test('build schema - params schema', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.equal(typeof opts[symbols.paramsSchema], 'function')
t.assert.strictEqual(typeof opts[symbols.paramsSchema], 'function')
})
test('build schema - headers schema', t => {
@@ -258,7 +253,7 @@ test('build schema - headers schema', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
t.equal(typeof opts[symbols.headersSchema], 'function')
t.assert.strictEqual(typeof opts[symbols.headersSchema], 'function')
})
test('build schema - headers are lowercase', t => {
@@ -274,38 +269,38 @@ test('build schema - headers are lowercase', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
t.ok(schema.properties['content-type'], 'lowercase content-type exists')
return () => {}
t.assert.ok(schema.properties['content-type'], 'lowercase content-type exists')
return () => { }
})
})
test('build schema - headers are not lowercased in case of custom object', t => {
t.plan(1)
class Headers {}
class Headers { }
const opts = {
schema: {
headers: new Headers()
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
t.type(schema, Headers)
return () => {}
t.assert.ok(schema, Headers)
return () => { }
})
})
test('build schema - headers are not lowercased in case of custom validator provided', t => {
t.plan(1)
class Headers {}
class Headers { }
const opts = {
schema: {
headers: new Headers()
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
t.type(schema, Headers)
return () => {}
t.assert.ok(schema, Headers)
return () => { }
}, true)
})
@@ -322,29 +317,22 @@ test('build schema - uppercased headers are not included', t => {
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
t.notOk('Content-Type' in schema.properties, 'uppercase does not exist')
return () => {}
t.assert.ok(!('Content-Type' in schema.properties), 'uppercase does not exist')
return () => { }
})
})
test('build schema - mixed schema types are individually skipped or normalized', t => {
t.plan(6)
t.plan(2)
class CustomSchemaClass {}
const nonNormalizedSchema = {
hello: { type: 'string' }
}
const normalizedSchema = {
type: 'object',
properties: nonNormalizedSchema
}
class CustomSchemaClass { }
const testCases = [{
schema: {
body: new CustomSchemaClass()
},
assertions: (schema) => {
t.type(schema.body, CustomSchemaClass)
t.assert.ok(schema.body, CustomSchemaClass)
}
}, {
schema: {
@@ -353,34 +341,12 @@ test('build schema - mixed schema types are individually skipped or normalized',
}
},
assertions: (schema) => {
t.type(schema.response[200], CustomSchemaClass)
}
}, {
schema: {
body: nonNormalizedSchema,
response: {
200: new CustomSchemaClass()
}
},
assertions: (schema) => {
t.same(schema.body, normalizedSchema)
t.type(schema.response[200], CustomSchemaClass)
}
}, {
schema: {
body: new CustomSchemaClass(),
response: {
200: nonNormalizedSchema
}
},
assertions: (schema) => {
t.type(schema.body, CustomSchemaClass)
t.same(schema.response[200], normalizedSchema)
t.assert.ok(schema.response[200], CustomSchemaClass)
}
}]
testCases.forEach((testCase) => {
const result = normalizeSchema({}, testCase.schema, { jsonShorthand: true })
const result = normalizeSchema(testCase.schema, {})
testCase.assertions(result)
})
})