Files
qqq-cli/scripts/run-tests.mjs
james.maesandClaude Fable 5.1 16be5c2a6f test: make presence tests wait on state, stop handles in finally, add test deadlines
Windows timers fire in ~15 ms steps, so the fixed tick(30) in the joinPresence
stream test ended before the three stub events (tick(2) each) were applied, and
the failed assertion skipped handle.stop(). The live handle's heartbeat interval
and endless stub stream then kept the test process alive, which CI saw as ten
silent minutes. Tests now poll for the state they assert and stop every handle
in finally. run-tests.mjs gives each test a 2 minute deadline and kills a run
that outlives 8 minutes with a message.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:30:10 -06:00

47 lines
2.2 KiB
JavaScript

#!/usr/bin/env node
/*
* Copyright 2026 QRun.IO, Inc.
* Licensed under the Apache License, Version 2.0 (see LICENSE).
*/
/**
* Runs a package's built tests: `node --test` on every `*.test.js` directly in `dist/test` (or the
* directory given), from the package directory npm runs scripts in. A shell glob such as
* `node --test dist/test/*.test.js` is not expanded by cmd.exe on Windows, and Node 20 does not glob
* its own arguments; `node --test` without arguments would also run helpers such as `test/harness.js`
* (Node 20's default patterns include every file under a `test` directory). So the list is made here.
*
* Every test gets a deadline (`--test-timeout`, 2 minutes unless the caller passes one) and the whole run
* is killed after 8 minutes, so a hang on one platform fails a test (or the step) with a message
* instead of leaving CI silent until its no-output limit kills the job.
*
* Usage: node ../../scripts/run-tests.mjs [dir] (extra node --test options may follow the dir)
*/
import { spawnSync } from 'node:child_process'
import { existsSync, readdirSync } from 'node:fs'
import { join, resolve } from 'node:path'
const [dirArg = 'dist/test', ...options] = process.argv.slice(2)
const dir = resolve(dirArg)
if (!existsSync(dir)) {
console.error(`run-tests: ${dir} does not exist; run npm run build first`)
process.exit(1)
}
const files = readdirSync(dir).filter((name) => name.endsWith('.test.js')).sort().map((name) => join(dir, name))
if (!files.length) {
console.error(`run-tests: no *.test.js in ${dir}`)
process.exit(1)
}
const timeoutOption = options.some((option) => option.startsWith('--test-timeout')) ? [] : ['--test-timeout=120000']
const RUN_LIMIT_MS = 8 * 60 * 1000
const result = spawnSync(process.execPath, ['--test', ...timeoutOption, ...options, ...files], { stdio: 'inherit', timeout: RUN_LIMIT_MS, killSignal: 'SIGKILL' })
if (result.error) {
if ('code' in result.error && result.error.code === 'ETIMEDOUT') {
console.error(`run-tests: killed after ${RUN_LIMIT_MS / 1000}s; a test file kept the process alive (an unclosed server, stream or timer)`)
process.exit(1)
}
throw result.error
}
process.exit(result.status ?? 1)