Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions examples/plugin-with-preloaded.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* global GLOBAL_MODULE_1, GLOBAL_MODULE_3 */
'use strict'
const t = require('tap')

const assert = require('node:assert/strict')

module.exports = async function (fastify, options) {
fastify.get('/', async function (req, reply) {
return { hasPreloaded: GLOBAL_MODULE_1 && GLOBAL_MODULE_3 }
})
fastify.addHook('onReady', function () {
t.ok(GLOBAL_MODULE_1)
t.ok(GLOBAL_MODULE_3)
assert.ok(GLOBAL_MODULE_1)
assert.ok(GLOBAL_MODULE_3)
})
}
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"unit:suites": "node should-skip-test-suites.js || npm run all-suites",
"all-suites": "npm run unit:cjs && npm run unit:esm && npm run unit:ts-cjs && npm run unit:ts-esm",
"unit:cli-js-esm": "node suite-runner.js \"test/esm/**/*.test.js\"",
"unit:cli-js": "tap \"test/**/*.test.js\" --no-coverage --timeout 400 --jobs 1 --color -R specy",
"unit:cli-ts": "cross-env TS_NODE_PROJECT=./test/configs/ts-cjs.tsconfig.json tap \"test/**/*.test.ts\" --no-coverage --timeout 400 --jobs 1 --color -R specy",
"unit:cli-js": "node suite-runner.js \"test/**/*.test.js\"",
"unit:cli-ts": "cross-env TS_NODE_PROJECT=./test/configs/ts-cjs.tsconfig.json node -r ts-node/register suite-runner.js \"test/**/*.test.ts\"",
"unit:cli": "npm run unit:cli-js && npm run unit:cli-ts && npm run unit:cli-js-esm",
"test:cli-and-typescript": "npm run unit:cli && npm run test:typescript",
"test:typescript": "tsd templates/plugin -t ./../../index.d.ts && tsc --project templates/app-ts/tsconfig.json --noEmit && tsc --project templates/app-ts-esm/tsconfig.json --noEmit"
Expand Down Expand Up @@ -66,7 +66,6 @@
"@fastify/autoload": "^6.0.0",
"@fastify/sensible": "^6.0.0",
"@types/node": "^25.0.3",
"@types/tap": "^15.0.5",
"c8": "^11.0.0",
"concurrently": "^9.0.0",
"cross-env": "^10.0.0",
Expand All @@ -79,7 +78,6 @@
"rimraf": "^6.1.0",
"sinon": "^21.0.0",
"strip-ansi": "^6.0.1",
"tap": "^16.1.0",
"ts-node": "^10.4.0",
"ts-standard": "^12.0.1",
"tsd": "^0.33.0",
Expand Down
36 changes: 19 additions & 17 deletions test/graceful-shutdown.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
'use strict'

const t = require('tap')
const { once } = require('node:events')
const { test: nodeTest, beforeEach, afterEach } = require('node:test')
const assert = require('node:assert/strict')
// Tests skip on win32 platforms due SIGINT signal is not supported across all windows platforms
const test = (process.platform === 'win32') ? t.skip : t.test
const test = (process.platform === 'win32') ? (name, fn) => nodeTest(name, { skip: true }, fn) : nodeTest
const sinon = require('sinon')
const start = require('../start')

Expand All @@ -17,38 +19,38 @@ let fastify = null
let signalCounter = null
const sandbox = sinon.createSandbox()

t.beforeEach(async () => {
beforeEach(async () => {
signalCounter = process.listenerCount('SIGINT')

const argv = ['-p', getPort(), './examples/plugin.js']
fastify = await start.start(argv)
spy = sinon.spy(fastify, 'close')
})

t.afterEach(async () => {
afterEach(async () => {
sandbox.restore()
})

test('should add and remove SIGINT listener as expected ', async t => {
t.plan(2)

t.equal(process.listenerCount('SIGINT'), signalCounter + 1)
test('should add and remove SIGINT listener as expected ', async () => {
assert.equal(process.listenerCount('SIGINT'), signalCounter + 1)

await fastify.close()

t.equal(process.listenerCount('SIGINT'), signalCounter)

t.end()
assert.equal(process.listenerCount('SIGINT'), signalCounter)
})

test('should have called fastify.close() when receives a SIGINT signal', async t => {
process.once('SIGINT', () => {
sinon.assert.called(spy)

t.end()
test('should have called fastify.close() when receives a SIGINT signal', async (t) => {
const exit = process.exit
process.exit = sinon.spy()

process.exit()
t.after(() => {
process.exit = exit
})

const sigintPromise = once(process, 'SIGINT')

process.kill(process.pid, 'SIGINT')
await sigintPromise

sinon.assert.called(spy)
})
118 changes: 93 additions & 25 deletions test/start.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,76 @@ const fs = require('node:fs')
const path = require('node:path')
const crypto = require('node:crypto')
const semver = require('semver')
const os = require('node:os')
const baseFilename = path.join(__dirname, 'fixtures', `test_${crypto.randomBytes(16).toString('hex')}`)
const { fork } = require('node:child_process')
const { test: nodeTest } = require('node:test')
const assert = require('node:assert/strict')
const moduleSupport = semver.satisfies(process.version, '>= 14 || >= 12.17.0 < 13.0.0')

const t = require('tap')
const test = t.test
const sinon = require('sinon')
const proxyquire = require('proxyquire').noPreserveCache()
const start = require('../start')

const writeFile = util.promisify(fs.writeFile)
const readFile = util.promisify(fs.readFile)

function createTestDir (fixtures) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fastify-cli-'))

function writeFixtures (baseDir, entries) {
for (const [name, value] of Object.entries(entries)) {
const filename = path.join(baseDir, name)
if (value && typeof value === 'object' && !Buffer.isBuffer(value)) {
fs.mkdirSync(filename, { recursive: true })
writeFixtures(filename, value)
} else {
fs.mkdirSync(path.dirname(filename), { recursive: true })
fs.writeFileSync(filename, value)
}
}
}

writeFixtures(dir, fixtures)
return dir
}

function tapCompatTest (name, options, fn) {
if (typeof options === 'function') {
fn = options
options = undefined
}

return nodeTest(name, options, async (t) => {
let planned = null
let assertions = 0
const count = (method) => (...args) => {
assertions++
return method(...args)
}

t.plan = (total) => {
planned = total
}
t.equal = count(assert.strictEqual)
t.same = count(assert.deepStrictEqual)
t.ok = count(assert.ok)
t.pass = count(() => assert.ok(true))
t.fail = count((message) => assert.fail(message))
t.end = () => {}
t.teardown = (hook) => t.after(async () => hook())
t.testdir = createTestDir

await fn(t)

if (planned !== null) {
assert.strictEqual(assertions, planned, `plan expected ${planned} assertions but received ${assertions}`)
}
})
}

const test = tapCompatTest

function requireUncached (module) {
delete require.cache[require.resolve(module)]
return require(module)
Expand Down Expand Up @@ -307,30 +364,38 @@ test('should warn on file not found', t => {
start.start(argv)
})

test('should throw on package not found', t => {
test('should throw on package not found', async t => {
t.plan(1)

const oldStop = start.stop
t.teardown(() => { start.stop = oldStop })
start.stop = function (err) {
t.ok(/Cannot find module 'unknown-package'/.test(err.message), err.message)
}

const argv = ['-p', getPort(), './test/data/package-not-found.js']
start.start(argv)
await new Promise((resolve) => {
start.stop = function (err) {
t.ok(/Cannot find module 'unknown-package'/.test(err.message), err.message)
resolve()
}

const argv = ['-p', getPort(), './test/data/package-not-found.js']
start.start(argv)
})
})

test('should throw on parsing error', t => {
test('should throw on parsing error', async t => {
t.plan(1)

const oldStop = start.stop
t.teardown(() => { start.stop = oldStop })
start.stop = function (err) {
t.equal(err.constructor, SyntaxError)
}

const argv = ['-p', getPort(), './test/data/parsing-error.js']
start.start(argv)
await new Promise((resolve) => {
start.stop = function (err) {
t.equal(err.constructor, SyntaxError)
resolve()
}

const argv = ['-p', getPort(), './test/data/parsing-error.js']
start.start(argv)
})
})

test('should start the server with an async/await plugin', async t => {
Expand Down Expand Up @@ -372,17 +437,21 @@ test('should exit without error on help', t => {
t.end()
})

test('should throw the right error on require file', t => {
test('should throw the right error on require file', async t => {
t.plan(1)

const oldStop = start.stop
t.teardown(() => { start.stop = oldStop })
start.stop = function (err) {
t.ok(/undefinedVariable is not defined/.test(err.message), err.message)
}

const argv = ['-p', getPort(), './test/data/undefinedVariable.js']
start.start(argv)
await new Promise((resolve) => {
start.stop = function (err) {
t.ok(/undefinedVariable is not defined/.test(err.message), err.message)
resolve()
}

const argv = ['-p', getPort(), './test/data/undefinedVariable.js']
start.start(argv)
})
})

test('should respond 413 - Payload too large', async t => {
Expand Down Expand Up @@ -712,14 +781,13 @@ test('should read env variables from .env file', async (t) => {
await fastify.close()
})

test('crash on unhandled rejection', t => {
test('crash on unhandled rejection', async t => {
t.plan(1)

const argv = ['-p', getPort(), './test/data/rejection.js']
const child = fork(path.join(__dirname, '..', 'start.js'), argv, { silent: true })
child.on('close', function (code) {
t.equal(code, 1)
})
const [code] = await once(child, 'close')
t.equal(code, 1)
})

test('should start the server with inspect options and the defalut port is 9320', async t => {
Expand Down Expand Up @@ -925,7 +993,7 @@ test('preloading a built-in module works', async t => {
test('preloading a module in node_modules works', async t => {
t.plan(1)

const argv = ['-r', 'tap', './examples/plugin.js']
const argv = ['-r', 'semver', './examples/plugin.js']
const fastify = await start.start(argv)
await fastify.close()
t.pass('server closed')
Expand Down