FN-6282: isolate vitest worker temp roots

Use per-invocation Vitest worker roots to keep compound-engineering tests from timing out on stale temp fixtures.

- Allocate a fresh FUSION_TEST_WORKER_ROOT during Vitest global setup and remove it during teardown.
- Preserve per-worker fallback root creation when global setup is not available.
- Clean compound-engineering harness project roots on close and cover the setup invariants with regression tests.

Files changed:
 packages/core/src/__test-utils__/vitest-setup.ts   | 20 +++++++---
 .../core/src/__test-utils__/vitest-teardown.ts     | 38 +++++++++++--------
 .../src/__tests__/_harness.ts                      |  7 +++-
 .../src/__tests__/setup-invariant.test.ts          | 43 ++++++++++++++++++++++
 4 files changed, 85 insertions(+), 23 deletions(-)

Fusion-Task-Id: FN-6282

Fusion-Task-Lineage: 8b842dd0-4f51-44de-b2db-8e8bfa97239c
This commit is contained in:
gsxdsm
2026-06-12 04:19:42 -07:00
parent ec4b247a86
commit 89654b2a90
4 changed files with 85 additions and 23 deletions

View File

@@ -164,11 +164,21 @@ if (!process.env.FUSION_MASTER_KEY_DISABLE_KEYCHAIN) {
process.env.FUSION_MASTER_KEY_DISABLE_KEYCHAIN = "1";
}
// Shared parent directory for all worker temp dirs in this run.
// globalTeardown wipes this at the end of the suite.
const WORKER_ROOT = join(tmpdir(), "fusion-test-workers");
try { mkdirSync(WORKER_ROOT, { recursive: true }); } catch { /* ignore */ }
process.env.FUSION_TEST_WORKER_ROOT = WORKER_ROOT;
// Shared parent directory for all worker temp dirs in this Vitest invocation.
// Keep this per-run (globalSetup seeds FUSION_TEST_WORKER_ROOT) instead of a
// single long-lived tmpdir/fusion-test-workers directory: redirect setup does a
// bounded one-level sweep of WORKER_ROOT, and a static root can accumulate enough
// stale worker/home dirs after interrupted runs to make every mkdtempSync call
// take seconds.
const WORKER_ROOT = (() => {
const fromEnv = process.env.FUSION_TEST_WORKER_ROOT;
const root = fromEnv && fromEnv.trim().length > 0
? resolve(fromEnv)
: realpathSync(mkdtempSync(join(tmpdir(), "fusion-test-workers-")));
try { mkdirSync(root, { recursive: true }); } catch { /* ignore */ }
process.env.FUSION_TEST_WORKER_ROOT = root;
return root;
})();
const REAL_TMPDIR = (() => {
try {

View File

@@ -1,28 +1,34 @@
/**
* Vitest globalSetup hook.
*
* We only publish the shared worker-root env var here. Teardown is intentionally
* a no-op because deleting shared temp roots during teardown can race with
* still-running suites in some Vitest pool modes and trigger uv_cwd failures.
* Worker dirs are cleaned by vitest-setup.ts on process exit.
* We publish a per-invocation worker-root env var. Teardown removes that private
* root after the project finishes so workspace isolation checks do not report
* the run-local worker/home directories as leaks.
*/
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const WORKER_ROOT = join(tmpdir(), "fusion-test-workers");
import { join, resolve } from "node:path";
export default function setup(): () => Promise<void> {
// Set the env var here too so vitest-setup.ts workers pick it up even if
// their own mkdir runs after globalSetup.
process.env.FUSION_TEST_WORKER_ROOT = WORKER_ROOT;
// Use a fresh root for each Vitest invocation. A static shared root makes the
// setup-time redirect sweep proportional to stale directories left by every
// prior interrupted run.
const workerRoot = resolve(mkdtempSync(join(tmpdir(), "fusion-test-workers-")));
process.env.FUSION_TEST_WORKER_ROOT = workerRoot;
return async function teardown() {
// Intentionally no-op.
//
// Worker temp dirs are cleaned by vitest-setup.ts using process.on("exit")
// after first chdir-ing out of the worker dir. Deleting shared temp roots
// from global teardown is unsafe under some Vitest pool modes because it
// can run while other suites are still active, causing ENOENT uv_cwd.
try {
process.chdir(tmpdir());
} catch {
// Ignore — cleanup below is best-effort and uses an absolute path.
}
try {
rmSync(workerRoot, { recursive: true, force: true });
} catch {
// Ignore — interrupted or still-active workers may leave a per-run root
// behind, but future runs no longer sweep it because every invocation gets
// a fresh root.
}
};
}

View File

@@ -1,4 +1,4 @@
import { mkdtempSync } from "node:fs";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { vi } from "vitest";
@@ -50,7 +50,10 @@ export function makeHarness(): TestHarness {
projectRoot,
ctx,
emitted,
close: () => db.close(),
close: () => {
db.close();
rmSync(projectRoot, { recursive: true, force: true });
},
};
}

View File

@@ -0,0 +1,43 @@
import { existsSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { basename, join, resolve, sep } from "node:path";
import { describe, expect, it } from "vitest";
import { makeHarness } from "./_harness.js";
function workerRoot(): string {
const root = process.env.FUSION_TEST_WORKER_ROOT;
if (!root) throw new Error("FUSION_TEST_WORKER_ROOT is not set");
return resolve(root);
}
describe("compound-engineering setup invariants", () => {
it("uses a per-run worker temp root for redirected temp fixtures", () => {
const root = workerRoot();
// Regression guard for FN-6282: this must not be the old static
// tmpdir()/fusion-test-workers directory whose one-level redirect sweep made
// setup proportional to stale directories from prior interrupted runs.
expect(basename(root)).toMatch(/^fusion-test-workers-/);
expect(root).not.toBe(resolve(tmpdir(), "fusion-test-workers"));
const tempFixture = mkdtempSync(join(tmpdir(), "ce-setup-guard-"));
try {
expect(resolve(tempFixture).startsWith(root + sep)).toBe(true);
} finally {
rmSync(tempFixture, { recursive: true, force: true });
}
});
it("closes the CE harness and removes its redirected project root", () => {
const root = workerRoot();
const harness = makeHarness();
const projectRoot = resolve(harness.projectRoot);
expect(projectRoot.startsWith(root + sep)).toBe(true);
expect(existsSync(projectRoot)).toBe(true);
harness.close();
expect(existsSync(projectRoot)).toBe(false);
});
});