mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-18 22:02:49 +08:00
2344ce0722
Every full test run left about 40 directories in the temp dir, even when it passed. On the Windows dev machine about 16,000 had piled up by mid-October. A full run with TEMP pointed at an empty folder showed where each one came from: - function-ref (20) and ts-field-classification (3): each case's `finally` cleared `tmpDir` right after `cg.destroy()`, so the `afterEach` that removes the dir found nothing to remove. - npm-sdk (3) and liveness-watchdog (3) never removed the dirs they made. - extraction's #1502 suite (3) made a dir in `beforeEach` and had no `afterEach`; pr19-improvements' schema-version case (1) made one inline. - setup-home-sandbox (6 on Windows): Vitest runs no hooks for a file whose every test is skipped, so each platform-gated file left its home sandbox. Each sandbox now records itself in a per-run registry, and a global teardown removes whatever is still listed and names any dir still in use. With the fix, the same full run leaves nothing behind but Node's own reused compile cache. test-home-sandbox gains two tests: a file's home is listed in the run's registry, and the teardown removes every listed home, then the registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
3.3 KiB
TypeScript
75 lines
3.3 KiB
TypeScript
/**
|
|
* Give every engine test file a throwaway home directory (#2275).
|
|
*
|
|
* The suite exercises code that writes to the user's GLOBAL state: the Claude
|
|
* prompt hook in `~/.claude/settings.json`, the daemon registry under
|
|
* `~/.codegraph/daemons`, every agent target's global config. A test that
|
|
* forgot to redirect the home dir wrote to the developer's real one — a
|
|
* successful fake `codegraph upgrade` wired the prompt hook into their own
|
|
* Claude profile, and the daemon suites left thousands of registry records
|
|
* behind. CI never noticed: its runners have no Claude profile to edit.
|
|
*
|
|
* So the home dir is redirected here, before any test module loads, rather
|
|
* than file by file. Everything that resolves a home dir is covered:
|
|
* `os.homedir()` reads HOME on POSIX but USERPROFILE on Windows; git on
|
|
* Windows falls back to HOMEDRIVE+HOMEPATH; the per-agent overrides
|
|
* (CLAUDE_CONFIG_DIR, CODEX_HOME, ...) win over either, so they are cleared;
|
|
* and git's global config is pinned to a seeded file, so a test's `git
|
|
* commit` neither reads the developer's identity, signing or hooks nor
|
|
* depends on them. Spawned CLI / MCP processes inherit all of it.
|
|
*
|
|
* Tests that redirect the home dir themselves (spying on `os.homedir`, or
|
|
* setting HOME and restoring it) keep working: they restore to this sandbox.
|
|
*/
|
|
import { afterAll, inject } from 'vitest';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
|
|
// Resolved, so a comparison against a realpath'd cwd (macOS `/var` →
|
|
// `/private/var`) sees the same home everything else does.
|
|
const home = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-test-home-')));
|
|
|
|
// Recorded for the end of the run, which removes the home if the afterAll
|
|
// below never runs (a file whose every test is skipped runs no hooks) or
|
|
// cannot (global-setup-home-sandbox.ts).
|
|
const registry = inject('homeSandboxRegistry');
|
|
const registryEntry = registry ? path.join(registry, path.basename(home)) : undefined;
|
|
if (registryEntry) fs.writeFileSync(registryEntry, home);
|
|
|
|
process.env.HOME = home;
|
|
process.env.USERPROFILE = home;
|
|
if (process.platform === 'win32') {
|
|
const drive = /^([A-Za-z]:)(\\.*)$/.exec(home);
|
|
if (drive) {
|
|
process.env.HOMEDRIVE = drive[1];
|
|
process.env.HOMEPATH = drive[2];
|
|
}
|
|
process.env.APPDATA = path.join(home, 'AppData', 'Roaming');
|
|
process.env.LOCALAPPDATA = path.join(home, 'AppData', 'Local');
|
|
} else {
|
|
// Only ever set on Windows in real life; on POSIX a stray one would point
|
|
// the opencode/Copilot legacy-path logic somewhere real.
|
|
delete process.env.APPDATA;
|
|
delete process.env.LOCALAPPDATA;
|
|
}
|
|
process.env.XDG_CONFIG_HOME = path.join(home, '.config');
|
|
for (const override of ['CLAUDE_CONFIG_DIR', 'CODEX_HOME', 'COPILOT_HOME', 'HERMES_HOME']) {
|
|
delete process.env[override];
|
|
}
|
|
|
|
// Both spellings: GIT_CONFIG_GLOBAL for git >= 2.32, and `$HOME/.gitconfig`
|
|
// (the same file) for older ones.
|
|
const gitConfig = path.join(home, '.gitconfig');
|
|
fs.writeFileSync(gitConfig, '[user]\n\tname = CodeGraph Test\n\temail = test@codegraph.invalid\n');
|
|
process.env.GIT_CONFIG_GLOBAL = gitConfig;
|
|
|
|
afterAll(() => {
|
|
try {
|
|
fs.rmSync(home, { recursive: true, force: true, maxRetries: 3 });
|
|
if (registryEntry) fs.rmSync(registryEntry, { force: true });
|
|
} catch {
|
|
/* a straggling child still holding a file there; the end of the run retries */
|
|
}
|
|
});
|