mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-15 17:35:29 +02:00
fix(test): remove all 8 delayed process.exit teardown bombs — the tier-1 gate can finally fail
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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3f176d2226
commit
e0bfc8fff5
+21
-10
@@ -361,16 +361,27 @@ describe("daemon /shutdown", () => {
|
||||
await fetchHandler(
|
||||
req("POST", `/boards/${board.id}/api/feedback`, { regenerated: false }),
|
||||
);
|
||||
// Now non-done count is 0 — handler should return shuttingDown:true.
|
||||
// We DON'T let the real gracefulShutdown timer fire (it calls process.exit
|
||||
// after 50ms which would tear down the test runner); instead we just
|
||||
// observe the immediate response.
|
||||
const r = await fetchHandler(req("POST", "/shutdown"));
|
||||
expect(r.status).toBe(200);
|
||||
const body = (await r.json()) as any;
|
||||
expect(body.shuttingDown).toBe(true);
|
||||
// Reset state for subsequent tests; the shutdown timer will be a no-op
|
||||
// because the next resetForTest flips shuttingDown back to false.
|
||||
// The handler arms setTimeout(gracefulShutdown, 50), and gracefulShutdown
|
||||
// arms setTimeout(process.exit, 50). bun test runs ALL files in one
|
||||
// process, so letting that exit fire would kill the whole suite ~100ms
|
||||
// later (exit 0, no summary — see test/no-suicide-exit.test.ts). Stub
|
||||
// process.exit, wait past both timers so they fire harmlessly while
|
||||
// stubbed, then restore. (resetForTest does NOT defuse the timers: the
|
||||
// exit callback is unconditional.)
|
||||
const origExit = process.exit;
|
||||
(process as any).exit = (() => undefined) as any;
|
||||
try {
|
||||
const r = await fetchHandler(req("POST", "/shutdown"));
|
||||
expect(r.status).toBe(200);
|
||||
const body = (await r.json()) as any;
|
||||
expect(body.shuttingDown).toBe(true);
|
||||
// Let both 50ms timers (gracefulShutdown, then its process.exit) fire
|
||||
// against the stub before restoring the real process.exit.
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
} finally {
|
||||
(process as any).exit = origExit;
|
||||
}
|
||||
// Reset state for subsequent tests (gracefulShutdown set shuttingDown).
|
||||
resetDaemon();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user