fix(test): bound the temp-HOME removal by a deadline, not an attempt count

Merge-time fix for #660. afterAll runs under vitest's 10 s hookTimeout, and
each failed removal attempt costs rmSync's own retry sleeps (50 + 100 + 150
ms) plus the 250 ms wait, so 20 attempts overran it: with a writer still
filling $HOME/.claude/backups, the file failed with "Hook timed out in
10000ms" after its test passed, and the "leave it to the exit backstop" branch
was never reached. The loop now stops after about 5 s. Checked with a
temporary (uncommitted) test that leaves a child process writing there for
20 s: before, the file failed at 11 s with the hook timeout; after, it passes
in under 6 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-22 19:25:04 +02:00
1 parent 710ed73585
commit 6310868696
1 file changed
+8 -3
+8 -3
View File
@@ -125,13 +125,18 @@ afterAll(async () => {
// with ENOTEMPTY after every test has passed. The usual culprit is a real agent CLI on a developer
// machine (the `claude` a test's code path probes or spawns) flushing its own `~/.claude.json` and
// `~/.claude/backups/` into the temp HOME a moment after the test ends; CI has no such CLI. Keep
// removing until it goes quiet, and if it never does, leave the temp dir to the `exit` backstop
// below: a leaked temp folder must not fail a test file that passed.
for (let attempt = 0; attempt < 20; attempt++) {
// removing until it goes quiet, for at most about 5 s, and if it never does, leave the temp dir to
// the `exit` backstop below: a leaked temp folder must not fail a test file that passed.
// ⚠️ The bound is a deadline, not an attempt count: this hook runs under vitest's 10 s
// `hookTimeout`, and each failed attempt costs rmSync's own retry sleeps (50 + 100 + 150 ms) plus
// the wait below, so 20 attempts overran it and failed the file with "Hook timed out".
const deadline = Date.now() + 5_000;
for (;;) {
try {
rmSync(testHome, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
break;
} catch {
if (Date.now() >= deadline) break;
await new Promise((resolve) => setTimeout(resolve, 250));
}
}