mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-16 20:53:57 +08:00
7bbc1fb7f6
Running the suite on a machine with Claude Code configured could wire the CodeGraph prompt hook into the developer's own ~/.claude/settings.json: every upgrade test whose fake upgrade succeeded ran the real post-upgrade prompt-hook self-heal, which detected the real global Claude profile and wrote it. Daemon suites likewise left records in the real ~/.codegraph/daemons. On Windows a HOME-only redirect would not have helped, since os.homedir() reads USERPROFILE there. - The prompt-hook writer is now injected through UpgradeDeps (wirePromptHook); the CLI passes the real defaultWirePromptHook and the upgrade tests a recorder. New tests pin that a successful upgrade calls the recorder and that the real writer never touches a configured profile. - A vitest setupFiles entry gives every engine test file a throwaway home: HOME/USERPROFILE (plus HOMEDRIVE/HOMEPATH/APPDATA/LOCALAPPDATA on Windows), XDG_CONFIG_HOME, and GIT_CONFIG_GLOBAL (seeded with a dummy identity) point into it, and CLAUDE_CONFIG_DIR/CODEX_HOME/COPILOT_HOME/ HERMES_HOME are cleared. Spawned CLI/MCP children inherit it. A sandbox test asserts os.homedir(), the Claude profile, the daemon registry, a child process and git's global config all resolve inside it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
2.9 KiB
TypeScript
67 lines
2.9 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 } 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-')));
|
|
|
|
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 });
|
|
} catch {
|
|
/* a straggling child still holding a file there; it's in the temp dir */
|
|
}
|
|
});
|