// Vitest setup file: isolates every test from the developer's shell environment. // // codeburn discovers sessions through a long list of provider-specific env // vars (CLAUDE_CONFIG_DIR, CODEX_HOME, CRUSH_GLOBAL_DATA, …) and via HOME / // XDG_* / APPDATA / LOCALAPPDATA. Without this file, any value set in the // developer's shell (e.g. CLAUDE_CONFIG_DIRS=/Users/me/.claude:…) bleeds into // fixture-based tests: the parser reads the developer's REAL sessions instead // of the temp-dir fixture, producing nonsense totals and false failures that // pass on a clean CI runner. // // What this file does: // 1. Mints an empty sandbox temp dir once per worker. // 2. REDIRECTED vars (HOME / XDG_* / APPDATA / LOCALAPPDATA) point at the // sandbox so any fallback to homedir() / platform defaults lands in an // empty filesystem. // 3. CLEARED vars (every provider's explicit override) are deleted so a test // that does NOT set one gets "unconfigured" rather than the dev's value. // 4. PRESERVED vars (PATH, COLUMNS, …) are snapshotted from the dev's shell // and restored every test. We can't wipe them - Node uses PATH for spawn // and module resolution, terminal code uses COLUMNS - but a test that // mutates them shouldn't leak the change into the next test. // 5. Re-asserts the above before EVERY test (global beforeEach), so a test // that mutates an env var doesn't leak its value into the next test. // Tests can freely set process.env['HOME'] = customDir without saving the // previous value - the next test gets a fresh sandbox baseline. // // CAVEAT: env vars set in a test file's beforeAll() get overwritten by this // file's beforeEach before each test runs. Use beforeEach (not beforeAll) when // the test body depends on a specific env var value. import { mkdtempSync } from 'fs' import { tmpdir } from 'os' import { join } from 'path' import { beforeEach } from 'vitest' const sandbox = mkdtempSync(join(tmpdir(), 'codeburn-test-env-')) const REDIRECTED = [ 'HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'APPDATA', 'LOCALAPPDATA', ] as const const CLEARED = [ // Provider session-discovery dirs 'CLAUDE_CONFIG_DIR', 'CLAUDE_CONFIG_DIRS', 'CODEX_HOME', 'CRUSH_GLOBAL_DATA', 'CODEBUFF_DATA_DIR', 'FACTORY_DIR', 'GOOSE_PATH_ROOT', 'GROK_HOME', 'KIRO_HOME', 'KIMI_SHARE_DIR', 'MUX_ROOT', 'QWEN_DATA_DIR', 'VIBE_HOME', 'WARP_DB_PATH', 'ZS_DATA_DIR', // codeburn override dirs / paths 'CODEBURN_CACHE_DIR', 'CODEBURN_COPILOT_JETBRAINS_DIR', 'CODEBURN_COPILOT_OTEL_DB', 'CODEBURN_COPILOT_SESSION_STATE_DIR', 'CODEBURN_COPILOT_WS_STORAGE_DIR', 'CODEBURN_DESKTOP_SESSIONS_DIR', 'CODEBURN_MUX_DIR', 'CODEBURN_ANTIGRAVITY_SETTINGS_PATH', // codeburn behavior toggles (set by the dev to tweak local runs) 'CODEBURN_COPILOT_DISABLE_OTEL', 'CODEBURN_TZ', 'CODEBURN_VERBOSE', 'CODEBURN_CURSOR_MAX_BUBBLES', 'CODEBURN_FORCE_MACOS_MAJOR', // Provider model/credential overrides 'KIMI_MODEL_NAME', 'AI_GATEWAY_API_KEY', 'VERCEL_OIDC_TOKEN', // Read by detectBashBloat - a dev's real shell limit must not bleed in 'BASH_MAX_OUTPUT_LENGTH', ] as const // Snapshotted from the dev's shell and restored every test. These can't be // wiped (Node needs PATH for spawn / module resolution, dashboard/table layout // reads COLUMNS) but a test that mutates them shouldn't leak. const PRESERVED = ['PATH', 'COLUMNS'] as const const preservedSnapshot = new Map() for (const key of PRESERVED) preservedSnapshot.set(key, process.env[key]) function applyIsolation(): void { for (const key of REDIRECTED) process.env[key] = sandbox for (const key of CLEARED) delete process.env[key] for (const key of PRESERVED) { const original = preservedSnapshot.get(key) if (original === undefined) delete process.env[key] else process.env[key] = original } // Pin the timezone so date grouping is deterministic regardless of the dev's // shell TZ. Clearing it is not enough (Node falls back to the OS zone); a // non-UTC TZ would otherwise shift day buckets versus a clean CI runner. A // test that needs a specific zone can still set process.env.TZ in beforeEach. process.env.TZ = 'UTC' } applyIsolation() beforeEach(applyIsolation)