mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-10 15:09:00 +02:00
bun test runs every file in ONE process, so a 500ms setTimeout(process.exit(0)) armed in afterAll fired mid-way through a LATER file and killed the entire suite with exit 0 and no summary — only ~16 of 434 files ran, and every downstream failure was invisible (observed live throughout this wave's enumeration). Changes, all guarded by fault injection: - Replace every delayed-exit teardown with a time-boxed close of the file's own browser (8 files across browse/ and design/); stub the daemon /shutdown timer instead of letting its unconditional process.exit tear the runner down. - test/no-suicide-exit.test.ts: static tripwire — no *.test.ts may schedule a delayed process.exit again. - test/exit-propagation.test.ts + fixtures: fault injection with REAL bun output proves the truncation shape (exit 0, no summary) and that scripts/test-free-shards.ts now detects it: a shard exiting 0 WITHOUT bun's final summary line is treated as FAILED (exit code alone is not evidence of completion). - handoff: the three headed-mode integration tests are darwin-skipped with a pointer to the known macOS headed-launch breakage (#2242/#2554); they keep running on Linux CI. Un-skip in the browse-daemon wave. - feedback-roundtrip: repair the handler call sites unmasked by the fix — handlers take (command, args, session, bm); passing the manager where a session belongs broke all six tests. - user-slug-fallback: HOME isolation makes endpoint_hash deterministic. Fixes #2421, #2435. Contributed by @sneakygriff (PR #2172) with repairs from @time-attack (PR #2230 feedback-roundtrip hunks); supersedes PR #2252 by @whd4 (same defect, credited). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
50 lines
2.1 KiB
TypeScript
50 lines
2.1 KiB
TypeScript
/**
|
|
* Guard: no test file may schedule a delayed process.exit().
|
|
*
|
|
* `bun test` runs EVERY test file in one process. The pattern of arming a
|
|
* 500ms timer in afterAll whose callback calls process.exit(0) — once used
|
|
* in several browse/design tests as a "bm.close() can hang" workaround —
|
|
* assumes each file gets its own process. It doesn't: the armed timer fires
|
|
* 500ms later, mid-way through a LATER test file, and kills the entire
|
|
* suite with exit code 0 and no summary. The truncated run silently masks
|
|
* every downstream failure (observed: only ~16 of 434 files ran, shell
|
|
* exit 0).
|
|
*
|
|
* This test statically scans every *.test.ts in the repo and fails if any
|
|
* schedules process.exit via setTimeout. Teardown must only release the
|
|
* file's own resources (e.g. `await bm.close()` — BrowserManager.close()
|
|
* is already time-boxed internally) — never terminate the shared runner.
|
|
*
|
|
* If a future test legitimately needs this pattern inside a child-process
|
|
* script (template literal passed to `bun -e`), split the child script
|
|
* into a fixture file instead of exempting it here.
|
|
*/
|
|
import { test, expect } from 'bun:test';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
const repoRoot = path.resolve(import.meta.dir, '..');
|
|
|
|
// Matches a setTimeout whose arrow callback (with or without an argument)
|
|
// immediately calls process.exit. Doesn't match its own escaped source text
|
|
// (the backslashes in this regex literal prevent a literal-text match).
|
|
const DELAYED_EXIT = /setTimeout\(\s*(?:\(\s*\)|\(?\w+\)?)\s*=>\s*process\.exit\(/;
|
|
|
|
test('no test file schedules a delayed process.exit (kills the whole bun test run)', () => {
|
|
const glob = new Bun.Glob('**/*.test.ts');
|
|
const violations: string[] = [];
|
|
|
|
for (const rel of glob.scanSync({ cwd: repoRoot })) {
|
|
if (rel.includes('node_modules/')) continue;
|
|
const source = fs.readFileSync(path.join(repoRoot, rel), 'utf-8');
|
|
const lines = source.split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (DELAYED_EXIT.test(lines[i])) {
|
|
violations.push(`${rel}:${i + 1}: ${lines[i].trim()}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
expect(violations).toEqual([]);
|
|
});
|