test: add filesystem isolation preload for CLI tests (#1250)

Redirects HOME and XDG dirs to a temp directory before tests run,
preventing any test from accidentally writing to the real user's
home directory (e.g. ~/.claude/settings.json, ~/.zshrc).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ahmed Abushagur 2026-02-16 18:04:18 -08:00 committed by GitHub
parent 7388c1eaec
commit 378b2c7d1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 0 deletions

2
cli/bunfig.toml Normal file
View file

@ -0,0 +1,2 @@
[test]
preload = ["./src/__tests__/preload.ts"]

View file

@ -0,0 +1,41 @@
/**
* Test preload script filesystem isolation for CLI tests.
*
* Loaded before every test file via bunfig.toml `preload`.
* Redirects HOME and XDG dirs to a temp directory so no test
* can accidentally write to the real user's home directory
* (e.g. ~/.claude/settings.json, ~/.zshrc).
*
* This prevents the class of bugs where a test (or the code under test)
* overwrites real config files on the developer's machine.
*/
import { mkdirSync, rmSync, mkdtempSync } from "fs";
import { join } from "path";
import { tmpdir } from "os";
// ── Create isolated HOME ────────────────────────────────────────────────────
const TEST_HOME = mkdtempSync(join(tmpdir(), "spawn-test-home-"));
// Redirect all user-directory env vars to the isolated temp
process.env.HOME = TEST_HOME;
process.env.XDG_CACHE_HOME = join(TEST_HOME, ".cache");
process.env.XDG_CONFIG_HOME = join(TEST_HOME, ".config");
process.env.XDG_DATA_HOME = join(TEST_HOME, ".local", "share");
// Pre-create common directories tests might expect
mkdirSync(join(TEST_HOME, ".cache"), { recursive: true });
mkdirSync(join(TEST_HOME, ".config"), { recursive: true });
mkdirSync(join(TEST_HOME, ".claude"), { recursive: true });
mkdirSync(join(TEST_HOME, ".local", "share"), { recursive: true });
// ── Cleanup on exit ─────────────────────────────────────────────────────────
process.on("exit", () => {
try {
rmSync(TEST_HOME, { recursive: true, force: true });
} catch {
// Best-effort cleanup
}
});