From 6c9a05f544b153079500f4f4d019500653cc7dc8 Mon Sep 17 00:00:00 2001 From: Naadir Jeewa Date: Mon, 10 Aug 2026 12:16:07 +0100 Subject: [PATCH 1/3] fix(storage): honour MAGIC_CONTEXT_TEST_DATA_DIR in the shared storage resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test-preload.ts documents MAGIC_CONTEXT_TEST_DATA_DIR as the guard that "cannot be defeated", but only resolveDatabasePath() consulted it. Every other caller of getMagicContextStorageDir() bypassed it — including the CLI doctors, which build join(getMagicContextStorageDir(), "context.db") themselves and run PRAGMA integrity_check against it. A test cannot restore isolation by setting process.env.HOME: bun caches os.homedir() at startup, so getDataDir() still resolves to the real home. Any test that deletes XDG_DATA_HOME to exercise path fallbacks therefore reached the user's production database. Observed as two doctor tests taking 8s and 16.5s against a 1.6 GB live DB — a read, not a migration, but the same escape that migrated the live DB in the v26 and v41 incidents. Move the guard into getMagicContextStorageDir() so it covers every caller. XDG_DATA_HOME still wins, so tests that manage their own data home are unaffected. resolveDatabasePath() drops the now-redundant branch and keeps the NODE_ENV backstop, which needs a memoized throwaway dir that a pure path helper has no business creating; its condition now defers to the test data dir so precedence is unchanged. --- .../src/features/magic-context/storage-db.ts | 34 +++++++----------- packages/plugin/src/shared/data-path.test.ts | 36 ++++++++++++++++++- packages/plugin/src/shared/data-path.ts | 20 ++++++++++- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/packages/plugin/src/features/magic-context/storage-db.ts b/packages/plugin/src/features/magic-context/storage-db.ts index 9fa00518a..52048a248 100644 --- a/packages/plugin/src/features/magic-context/storage-db.ts +++ b/packages/plugin/src/features/magic-context/storage-db.ts @@ -158,27 +158,13 @@ export function resolveDatabasePath(dbPathOverride?: string): { dbDir: string; d if (dbPathOverride) { return { dbDir: dirname(dbPathOverride), dbPath: dbPathOverride }; } - // Test-isolation guard. Under the test runner the preload - // (bunfig.toml `[test] preload`) sets MAGIC_CONTEXT_TEST_DATA_DIR to a - // throwaway temp dir AND XDG_DATA_HOME to the same dir. Tests that manage - // their OWN XDG_DATA_HOME (per-test temp dirs) keep working — we honor XDG - // below via getMagicContextStorageDir(). The guard fires ONLY when - // XDG_DATA_HOME is UNSET: that is the dangerous window, because - // getMagicContextStorageDir() would otherwise fall back to the REAL - // ~/.local/share and a bare openDatabase() would run migrations on the - // user's production DB. Some tests delete XDG_DATA_HOME to exercise - // path-fallback behavior (2026-06-01 incident: a dormant test migrated the - // live DB to v26 and fail-closed every running v25 binary); in that window - // we resolve into the dedicated test dir instead of the real path. No test - // mutates MAGIC_CONTEXT_TEST_DATA_DIR, so the guard cannot be defeated. It - // is never set in production. - const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; - if (testDataDir && !process.env.XDG_DATA_HOME) { - const dbDir = join(testDataDir, "cortexkit", "magic-context"); - return { dbDir, dbPath: join(dbDir, "context.db") }; - } - // CWD-INDEPENDENT TEST BACKSTOP. The MAGIC_CONTEXT_TEST_DATA_DIR / XDG guard - // above only fires when the bunfig `[test] preload` ran — which depends on + // Test-isolation guard: MAGIC_CONTEXT_TEST_DATA_DIR is honored inside + // getMagicContextStorageDir() below (see its doc comment), so it covers + // this resolver and every other caller alike. The NODE_ENV backstop below + // stays here: it needs a memoized throwaway dir, which a pure path helper + // has no business creating. + // CWD-INDEPENDENT TEST BACKSTOP. The MAGIC_CONTEXT_TEST_DATA_DIR guard only + // fires when the bunfig `[test] preload` ran — which depends on // `bun test`'s CWD having a bunfig with `[test] preload`. A `bun test` from a // dir WITHOUT that wiring (monorepo root, a package missing its bunfig, or a // brand-new package) recursively runs every *.test.ts with NO preload, so a @@ -198,7 +184,11 @@ export function resolveDatabasePath(dbPathOverride?: string): { dbDir: string; d // per-test temp dir, e.g. to exercise path fallbacks or share a DB across // helper calls), getMagicContextStorageDir() already points inside that // controlled dir — honor it, do not override. - if (process.env.NODE_ENV === "test" && !process.env.XDG_DATA_HOME) { + if ( + process.env.NODE_ENV === "test" && + !process.env.XDG_DATA_HOME && + !process.env.MAGIC_CONTEXT_TEST_DATA_DIR + ) { // Memoized per-process so repeated openDatabase() calls in the same // unisolated test resolve to the SAME path (openDatabase caches by path; // a fresh temp dir per call would defeat the cache and hand back diff --git a/packages/plugin/src/shared/data-path.test.ts b/packages/plugin/src/shared/data-path.test.ts index 3d7bfc5fd..065986e43 100644 --- a/packages/plugin/src/shared/data-path.test.ts +++ b/packages/plugin/src/shared/data-path.test.ts @@ -20,6 +20,7 @@ const savedEnv = { XDG_DATA_HOME: process.env.XDG_DATA_HOME, LOCALAPPDATA: process.env.LOCALAPPDATA, MAGIC_CONTEXT_LOG_PATH: process.env.MAGIC_CONTEXT_LOG_PATH, + MAGIC_CONTEXT_TEST_DATA_DIR: process.env.MAGIC_CONTEXT_TEST_DATA_DIR, }; describe("data-path", () => { @@ -44,6 +45,9 @@ describe("data-path", () => { if (savedEnv.MAGIC_CONTEXT_LOG_PATH !== undefined) process.env.MAGIC_CONTEXT_LOG_PATH = savedEnv.MAGIC_CONTEXT_LOG_PATH; else delete process.env.MAGIC_CONTEXT_LOG_PATH; + if (savedEnv.MAGIC_CONTEXT_TEST_DATA_DIR !== undefined) + process.env.MAGIC_CONTEXT_TEST_DATA_DIR = savedEnv.MAGIC_CONTEXT_TEST_DATA_DIR; + else delete process.env.MAGIC_CONTEXT_TEST_DATA_DIR; }); test("getCacheDir falls back to /.cache when XDG_CACHE_HOME is unset (all platforms)", () => { @@ -89,8 +93,38 @@ describe("data-path", () => { // Cross-harness shared path: both OpenCode and Pi plugins read/write here, // unlike the legacy opencode/storage/plugin/magic-context location which // was OpenCode-specific. See ARCHITECTURE_DECISIONS memory for rationale. + // Production shape, so the test-isolation dir set by test-preload.ts is + // lifted for the duration of this assertion. + const savedTestDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + delete process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + try { + expect(getMagicContextStorageDir()).toBe( + path.join(os.homedir(), ".local", "share", "cortexkit", "magic-context"), + ); + } finally { + if (savedTestDir !== undefined) process.env.MAGIC_CONTEXT_TEST_DATA_DIR = savedTestDir; + } + }); + + test("getMagicContextStorageDir honors MAGIC_CONTEXT_TEST_DATA_DIR when XDG_DATA_HOME is unset", () => { + // The hole this closes: a test that deletes XDG_DATA_HOME to exercise + // path fallbacks used to resolve to the user's REAL shared storage, + // because bun caches os.homedir() and a mutated process.env.HOME cannot + // move getDataDir(). Callers that build their own context.db path (the + // CLI doctors) then ran integrity checks against production data. + process.env.MAGIC_CONTEXT_TEST_DATA_DIR = "/tmp/mc-test-isolation"; expect(getMagicContextStorageDir()).toBe( - path.join(os.homedir(), ".local", "share", "cortexkit", "magic-context"), + path.join("/tmp/mc-test-isolation", "cortexkit", "magic-context"), + ); + }); + + test("getMagicContextStorageDir prefers XDG_DATA_HOME over MAGIC_CONTEXT_TEST_DATA_DIR", () => { + // A test managing its own per-test data home is already controlled, and + // several suites depend on that dir being honored. + process.env.MAGIC_CONTEXT_TEST_DATA_DIR = "/tmp/mc-test-isolation"; + process.env.XDG_DATA_HOME = "/tmp/custom-data"; + expect(getMagicContextStorageDir()).toBe( + path.join("/tmp/custom-data", "cortexkit", "magic-context"), ); }); diff --git a/packages/plugin/src/shared/data-path.ts b/packages/plugin/src/shared/data-path.ts index e0f064cb4..9a3f2a12b 100644 --- a/packages/plugin/src/shared/data-path.ts +++ b/packages/plugin/src/shared/data-path.ts @@ -167,9 +167,27 @@ export function getOpenCodeStorageDir(): string { * - Future cross-harness session migration * * Layout: /cortexkit/magic-context/ + * + * TEST-ISOLATION GUARD. `openDatabase()` has been guarded in + * `resolveDatabasePath()` since the 2026-06-01 (v26) and 2026-06-19 (v41) + * incidents, in which unisolated tests migrated the user's REAL shared DB. + * Every OTHER caller bypassed that guard — notably the CLI doctors, which + * build `join(getMagicContextStorageDir(), "context.db")` themselves and run + * `PRAGMA integrity_check` against it. A test that deletes XDG_DATA_HOME to + * exercise path fallbacks cannot restore isolation by setting + * `process.env.HOME`: bun caches `os.homedir()` at startup, so `getDataDir()` + * still resolves to the real home. Honoring MAGIC_CONTEXT_TEST_DATA_DIR here — + * set by `packages/plugin/test-preload.ts` and mutated by no test — makes the + * preload's "cannot be defeated" guarantee true for every caller, not just + * `openDatabase()`. It is never set in production. + * + * XDG_DATA_HOME still wins: a test that manages its own data home is already + * controlled, and production has no test dir set at all. */ export function getMagicContextStorageDir(): string { - return path.join(getDataDir(), "cortexkit", "magic-context"); + const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + const base = testDataDir && !process.env.XDG_DATA_HOME ? testDataDir : getDataDir(); + return path.join(base, "cortexkit", "magic-context"); } /** From 9cfcad57110614d1d9836ac2f65d6a65535f5362 Mon Sep 17 00:00:00 2001 From: Naadir Jeewa Date: Mon, 10 Aug 2026 13:45:49 +0100 Subject: [PATCH 2/3] fix(storage): hoist the NODE_ENV test backstop into the storage resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback on #296 (cubic, storage-db.ts:187): the PR claimed uniform coverage, but only MAGIC_CONTEXT_TEST_DATA_DIR moved into getMagicContextStorageDir(). The CWD-independent NODE_ENV backstop stayed in resolveDatabasePath(), so in the no-preload window the claim was false for exactly the callers this PR was about. Reproduced with NODE_ENV=test and neither guard var set: getMagicContextStorageDir() -> ~/.local/share/cortexkit/magic-context resolveDatabasePath() -> /tmp/mc-test-db-backstop-*/…/context.db The CLI doctors build join(getMagicContextStorageDir(), "context.db") themselves and run PRAGMA integrity_check on it, so they took the first path — production — while the DB resolver was safely redirected. Same for announcement.ts and models-dev-cache.ts. Move the backstop next to the guard it backs up. resolveDatabasePath() now has no isolation branch at all; both guards live in one place and every caller inherits them. Path shape is unchanged: the backstop dir already ended in cortexkit/magic-context, so the resolver still appends context.db to the same root. The warning uses console.warn rather than the logger because logger.ts imports data-path.ts. Also correct the doc comment's claim that no test mutates the guard var (cubic, data-path.ts:180) — data-path.test.ts sets and restores it. --- .../src/features/magic-context/storage-db.ts | 65 ++---------------- packages/plugin/src/shared/data-path.test.ts | 26 ++++++- packages/plugin/src/shared/data-path.ts | 67 ++++++++++++++++--- 3 files changed, 86 insertions(+), 72 deletions(-) diff --git a/packages/plugin/src/features/magic-context/storage-db.ts b/packages/plugin/src/features/magic-context/storage-db.ts index 52048a248..6f3e88f06 100644 --- a/packages/plugin/src/features/magic-context/storage-db.ts +++ b/packages/plugin/src/features/magic-context/storage-db.ts @@ -5,13 +5,11 @@ import { type Dirent, existsSync, mkdirSync, - mkdtempSync, readdirSync, readFileSync, statSync, unlinkSync, } from "node:fs"; -import { tmpdir } from "node:os"; import { basename, dirname, join } from "node:path"; import { bootQuietRemainingMs, scheduleAfterBootQuiet } from "../../plugin/boot-quiet"; import { @@ -158,69 +156,14 @@ export function resolveDatabasePath(dbPathOverride?: string): { dbDir: string; d if (dbPathOverride) { return { dbDir: dirname(dbPathOverride), dbPath: dbPathOverride }; } - // Test-isolation guard: MAGIC_CONTEXT_TEST_DATA_DIR is honored inside - // getMagicContextStorageDir() below (see its doc comment), so it covers - // this resolver and every other caller alike. The NODE_ENV backstop below - // stays here: it needs a memoized throwaway dir, which a pure path helper - // has no business creating. - // CWD-INDEPENDENT TEST BACKSTOP. The MAGIC_CONTEXT_TEST_DATA_DIR guard only - // fires when the bunfig `[test] preload` ran — which depends on - // `bun test`'s CWD having a bunfig with `[test] preload`. A `bun test` from a - // dir WITHOUT that wiring (monorepo root, a package missing its bunfig, or a - // brand-new package) recursively runs every *.test.ts with NO preload, so a - // bare openDatabase() would resolve to the user's REAL shared DB and run - // migrations on it. That is exactly how the live DB was migrated to v41 by a - // worktree whose LATEST was 41 (a re-run of the 2026-06-01 v26 incident). - // - // Bun sets NODE_ENV=test for EVERY `bun test` regardless of CWD/bunfig (and - // it is never "test" in the plugin runtime — production never sets it). So if - // we are under the test runner with neither the test data dir nor an explicit - // override, we MUST NOT touch real storage: redirect into a throwaway temp dir - // so the live DB is physically unreachable. This makes it structurally - // impossible for ANY test, from ANY CWD, to read or migrate production data. - // Fire ONLY when XDG_DATA_HOME is unset: that is the dangerous window where - // getMagicContextStorageDir() below would otherwise resolve to the REAL - // ~/.local/share shared DB. When a test sets its own XDG_DATA_HOME (a - // per-test temp dir, e.g. to exercise path fallbacks or share a DB across - // helper calls), getMagicContextStorageDir() already points inside that - // controlled dir — honor it, do not override. - if ( - process.env.NODE_ENV === "test" && - !process.env.XDG_DATA_HOME && - !process.env.MAGIC_CONTEXT_TEST_DATA_DIR - ) { - // Memoized per-process so repeated openDatabase() calls in the same - // unisolated test resolve to the SAME path (openDatabase caches by path; - // a fresh temp dir per call would defeat the cache and hand back - // different DB handles). - const dbDir = getTestBackstopDbDir(); - if (!testBackstopWarned) { - testBackstopWarned = true; - log( - "[magic-context] TEST BACKSTOP: NODE_ENV=test with no MAGIC_CONTEXT_TEST_DATA_DIR " + - `— redirecting DB to a throwaway temp dir (${dbDir}) so no test can touch the ` + - "user's real shared database. Wire `[test] preload` in this package's bunfig.toml.", - ); - } - return { dbDir, dbPath: join(dbDir, "context.db") }; - } + // Test-isolation guards (MAGIC_CONTEXT_TEST_DATA_DIR + the CWD-independent + // NODE_ENV backstop) both live in getMagicContextStorageDir(), so this + // resolver and every direct caller of that helper are covered by one + // implementation. See its doc comment for the incident history. const dbDir = getMagicContextStorageDir(); return { dbDir, dbPath: join(dbDir, "context.db") }; } -let testBackstopDbDir: string | null = null; -let testBackstopWarned = false; -function getTestBackstopDbDir(): string { - if (!testBackstopDbDir) { - testBackstopDbDir = join( - mkdtempSync(join(tmpdir(), "mc-test-db-backstop-")), - "cortexkit", - "magic-context", - ); - } - return testBackstopDbDir; -} - export function getDatabasePath(db: Database): string | null { return pathByDatabase.get(db) ?? null; } diff --git a/packages/plugin/src/shared/data-path.test.ts b/packages/plugin/src/shared/data-path.test.ts index 065986e43..ef8478143 100644 --- a/packages/plugin/src/shared/data-path.test.ts +++ b/packages/plugin/src/shared/data-path.test.ts @@ -93,14 +93,38 @@ describe("data-path", () => { // Cross-harness shared path: both OpenCode and Pi plugins read/write here, // unlike the legacy opencode/storage/plugin/magic-context location which // was OpenCode-specific. See ARCHITECTURE_DECISIONS memory for rationale. - // Production shape, so the test-isolation dir set by test-preload.ts is + // Production shape, so both test-isolation guards (the preload's data + // dir and the NODE_ENV backstop bun sets for every `bun test`) are // lifted for the duration of this assertion. const savedTestDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + const savedNodeEnv = process.env.NODE_ENV; delete process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + delete process.env.NODE_ENV; try { expect(getMagicContextStorageDir()).toBe( path.join(os.homedir(), ".local", "share", "cortexkit", "magic-context"), ); + } finally { + if (savedTestDir !== undefined) process.env.MAGIC_CONTEXT_TEST_DATA_DIR = savedTestDir; + if (savedNodeEnv !== undefined) process.env.NODE_ENV = savedNodeEnv; + } + }); + + test("getMagicContextStorageDir backstops to a temp dir under NODE_ENV=test with no guard set", () => { + // CWD-independent backstop: a `bun test` from a dir whose bunfig has no + // `[test] preload` runs every suite with neither guard env var set. The + // DB resolver used to own this branch, so direct callers of this helper + // (the CLI doctors' own PRAGMA integrity_check) still reached the real + // shared DB. Memoized, so repeated calls must agree — openDatabase() + // caches by path. + const savedTestDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + delete process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + process.env.NODE_ENV = "test"; + try { + const resolved = getMagicContextStorageDir(); + expect(resolved).not.toContain(path.join(os.homedir(), ".local", "share")); + expect(resolved.endsWith(path.join("cortexkit", "magic-context"))).toBe(true); + expect(getMagicContextStorageDir()).toBe(resolved); } finally { if (savedTestDir !== undefined) process.env.MAGIC_CONTEXT_TEST_DATA_DIR = savedTestDir; } diff --git a/packages/plugin/src/shared/data-path.ts b/packages/plugin/src/shared/data-path.ts index 9a3f2a12b..78ec9ffc4 100644 --- a/packages/plugin/src/shared/data-path.ts +++ b/packages/plugin/src/shared/data-path.ts @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { getHarness, type HarnessId } from "./harness"; @@ -176,18 +176,65 @@ export function getOpenCodeStorageDir(): string { * `PRAGMA integrity_check` against it. A test that deletes XDG_DATA_HOME to * exercise path fallbacks cannot restore isolation by setting * `process.env.HOME`: bun caches `os.homedir()` at startup, so `getDataDir()` - * still resolves to the real home. Honoring MAGIC_CONTEXT_TEST_DATA_DIR here — - * set by `packages/plugin/test-preload.ts` and mutated by no test — makes the - * preload's "cannot be defeated" guarantee true for every caller, not just - * `openDatabase()`. It is never set in production. + * still resolves to the real home. MAGIC_CONTEXT_TEST_DATA_DIR — set by + * `packages/plugin/test-preload.ts`, and restored by any test that touches it + * — is honored here, so the preload's "cannot be defeated" guarantee holds for + * every caller, not just `openDatabase()`. It is never set in production. * - * XDG_DATA_HOME still wins: a test that manages its own data home is already - * controlled, and production has no test dir set at all. + * CWD-INDEPENDENT BACKSTOP. The preload only runs when `bun test`'s CWD has a + * bunfig wiring `[test] preload`. A run from a dir WITHOUT that wiring (a + * package missing its bunfig, or a brand-new one) executes every *.test.ts with + * NO preload, and this resolver would hand back the REAL shared path — which is + * how the live DB reached v41. Bun sets NODE_ENV=test for EVERY `bun test` + * regardless of CWD, and production never sets it, so that window redirects to + * a memoized throwaway dir instead. It lives here rather than in + * `resolveDatabasePath()` so direct callers (the CLI doctors' own + * `PRAGMA integrity_check`, announcements, the models.dev cache) are covered + * too — they never go through the DB resolver. + * + * XDG_DATA_HOME still wins over both: a test that manages its own data home is + * already controlled, and production has no test dir set at all. */ export function getMagicContextStorageDir(): string { - const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; - const base = testDataDir && !process.env.XDG_DATA_HOME ? testDataDir : getDataDir(); - return path.join(base, "cortexkit", "magic-context"); + if (!process.env.XDG_DATA_HOME) { + const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + if (testDataDir) { + return path.join(testDataDir, "cortexkit", "magic-context"); + } + if (process.env.NODE_ENV === "test") { + return getTestBackstopStorageDir(); + } + } + return path.join(getDataDir(), "cortexkit", "magic-context"); +} + +let testBackstopStorageDir: string | null = null; +let testBackstopWarned = false; + +/** + * Memoized per process so repeated calls in the same unisolated test resolve to + * the SAME path — `openDatabase()` caches by path, and a fresh temp dir per call + * would defeat that cache and hand back different DB handles. + */ +function getTestBackstopStorageDir(): string { + if (!testBackstopStorageDir) { + testBackstopStorageDir = path.join( + mkdtempSync(path.join(os.tmpdir(), "mc-test-db-backstop-")), + "cortexkit", + "magic-context", + ); + } + if (!testBackstopWarned) { + testBackstopWarned = true; + // Deliberately console, not the logger: logger.ts imports this module. + console.warn( + "[magic-context] TEST BACKSTOP: NODE_ENV=test with no MAGIC_CONTEXT_TEST_DATA_DIR " + + `— redirecting storage to a throwaway temp dir (${testBackstopStorageDir}) so no ` + + "test can touch the user's real shared database. Wire `[test] preload` in this " + + "package's bunfig.toml.", + ); + } + return testBackstopStorageDir; } /** From 86a7d56f3f024ae2af5f26f7362b5505d60a2c60 Mon Sep 17 00:00:00 2001 From: Naadir Jeewa Date: Mon, 10 Aug 2026 14:04:41 +0100 Subject: [PATCH 3/3] test(data-path): restore-or-delete every guard var the suite lifts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The suite's afterEach restored a saved value only when it was defined, so a var that started unset stayed set after the test that set it. Two tests lift a guard to assert production shape (the layout test deletes MAGIC_CONTEXT_TEST_DATA_DIR and NODE_ENV; the backstop test sets NODE_ENV), which made the result order-dependent in any run where those vars are not already populated — and NODE_ENV was not saved at all. Replace the hand-rolled restores with a loop over the saved snapshot that deletes when the original was undefined, and add NODE_ENV to it. Verified with --rerun-each 5 (125 pass) and against the full plugin suite. --- packages/plugin/src/shared/data-path.test.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/plugin/src/shared/data-path.test.ts b/packages/plugin/src/shared/data-path.test.ts index ef8478143..a12be3bd1 100644 --- a/packages/plugin/src/shared/data-path.test.ts +++ b/packages/plugin/src/shared/data-path.test.ts @@ -21,6 +21,7 @@ const savedEnv = { LOCALAPPDATA: process.env.LOCALAPPDATA, MAGIC_CONTEXT_LOG_PATH: process.env.MAGIC_CONTEXT_LOG_PATH, MAGIC_CONTEXT_TEST_DATA_DIR: process.env.MAGIC_CONTEXT_TEST_DATA_DIR, + NODE_ENV: process.env.NODE_ENV, }; describe("data-path", () => { @@ -37,17 +38,14 @@ describe("data-path", () => { }); afterEach(() => { - if (savedEnv.XDG_CACHE_HOME !== undefined) - process.env.XDG_CACHE_HOME = savedEnv.XDG_CACHE_HOME; - if (savedEnv.XDG_DATA_HOME !== undefined) - process.env.XDG_DATA_HOME = savedEnv.XDG_DATA_HOME; - if (savedEnv.LOCALAPPDATA !== undefined) process.env.LOCALAPPDATA = savedEnv.LOCALAPPDATA; - if (savedEnv.MAGIC_CONTEXT_LOG_PATH !== undefined) - process.env.MAGIC_CONTEXT_LOG_PATH = savedEnv.MAGIC_CONTEXT_LOG_PATH; - else delete process.env.MAGIC_CONTEXT_LOG_PATH; - if (savedEnv.MAGIC_CONTEXT_TEST_DATA_DIR !== undefined) - process.env.MAGIC_CONTEXT_TEST_DATA_DIR = savedEnv.MAGIC_CONTEXT_TEST_DATA_DIR; - else delete process.env.MAGIC_CONTEXT_TEST_DATA_DIR; + // Restore-or-delete every var this suite touches. Several tests lift a + // guard (NODE_ENV, MAGIC_CONTEXT_TEST_DATA_DIR) to assert production + // shape, so a restore that skips the unset case would leak state into + // the next test and make results order-dependent. + for (const [key, value] of Object.entries(savedEnv)) { + if (value !== undefined) process.env[key] = value; + else delete process.env[key]; + } }); test("getCacheDir falls back to /.cache when XDG_CACHE_HOME is unset (all platforms)", () => {