fix(test): isolate $HOME in mechanical.test.ts so E2E suite stops clobbering user config (#434)

mechanical.test.ts shells out to `gbrain init --non-interactive`,
`gbrain import`, and similar commands via Bun.spawnSync. The four
`cliEnv()` helpers in this file forward `process.env` unchanged, so
`gbrain init` ends up calling saveConfig() against the developer's real
$HOME/.gbrain/config.json, overwriting their production database_url
with the test container's URL on every `bun run test:e2e` invocation.

Sibling test/e2e/migration-flow.test.ts already solved this with a
module-level temp HOME and an afterAll restore. Mirror that pattern in
mechanical.test.ts.

Verified by md5'ing ~/.gbrain/config.json before and after running the
Setup Journey, Init Edge Cases, Schema Idempotency, RLS Verification,
Doctor Command, and Parallel Import describe blocks — config hash is
identical pre and post (26 passing tests, 0 failures, 0 mutations to
the user's real config).

Co-authored-by: Seth Armbrust <setharmbrust@seth.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lloyd
2026-07-23 13:24:40 -07:00
committed by GitHub
co-authored by Seth Armbrust Claude Opus 4.7
parent 8901dc0f45
commit 38f446bb6f
+23
View File
@@ -24,6 +24,29 @@ import { importFromContent } from '../../src/core/import-file.ts';
const skip = !hasDatabase();
const describeE2E = skip ? describe.skip : describe;
// HOME isolation. Several tests in this file shell out to `gbrain init` and
// `gbrain import` via Bun.spawnSync. `gbrain init` calls saveConfig() which
// writes to $HOME/.gbrain/config.json, and `gbrain import` writes a sync
// bookmark to the same directory. Without isolating $HOME, these tests
// clobber the user's real production gbrain config every time `bun run
// test:e2e` is executed. Sibling test/e2e/migration-flow.test.ts solved
// this with a module-level temp HOME; mirror that pattern here so the
// E2E suite stops mutating user state.
let _origHome: string | undefined;
let _tmpHome: string | undefined;
if (!skip) {
_origHome = process.env.HOME;
_tmpHome = mkdtempSync(join(tmpdir(), 'gbrain-e2e-mechanical-home-'));
process.env.HOME = _tmpHome;
}
afterAll(() => {
if (skip) return;
if (_origHome === undefined) delete process.env.HOME;
else process.env.HOME = _origHome;
try { if (_tmpHome) rmSync(_tmpHome, { recursive: true, force: true }); } catch { /* best-effort */ }
});
function makeCtx(opts: { remote?: boolean } = {}): OperationContext {
return {
engine: getEngine(),