feat(FN-4018): isolate split merger temp workspaces with settings persisten
Merged FN-4014 to fix dual-scope GitHub tracking settings persistence and validation (mission vs task scope), scoped repo saves to the relevant section, and documented the dual defaults. Also landed FN-4018 to isolate split merger temp workspaces and add regression checks for split-suite test isolat Fusion-Task-Id: FN-4018
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import { commitOrAmendMergeWithFixes } from "../merger.js";
|
||||
@@ -20,6 +20,12 @@ function initRepo(dir: string): void {
|
||||
git(dir, 'git commit -m "chore: initial"');
|
||||
}
|
||||
|
||||
function assertIsolatedWorkspace(dir: string): void {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
if (!repoRoot) return;
|
||||
expect(resolve(dir).startsWith(resolve(repoRoot))).toBe(false);
|
||||
}
|
||||
|
||||
function runFinalize(dir: string, taskId: string, branch: string, preAttemptHeadSha: string) {
|
||||
return commitOrAmendMergeWithFixes(
|
||||
dir,
|
||||
@@ -45,7 +51,8 @@ describe("commitOrAmendMergeWithFixes ancestor/equivalent-content short-circuit"
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-ancestor-shortcircuit-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-ancestor-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import type { TaskStore } from "@fusion/core";
|
||||
@@ -44,6 +44,12 @@ function stashList(dir: string): string {
|
||||
return git(dir, 'git stash list --format="%H %gd %s"');
|
||||
}
|
||||
|
||||
function assertIsolatedWorkspace(dir: string): void {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
if (!repoRoot) return;
|
||||
expect(resolve(dir).startsWith(resolve(repoRoot))).toBe(false);
|
||||
}
|
||||
|
||||
function makeStore(tasks: Record<string, string>, opts?: { throwOnGetTask?: boolean }): TaskStore {
|
||||
return {
|
||||
getTask: async (taskId: string) => {
|
||||
@@ -69,7 +75,8 @@ describe("sweepStaleAutostashes", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-autostash-stale-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-autostash-stale-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
@@ -145,7 +152,8 @@ describe("sweepAutostashOrphans", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-autostash-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-autostash-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import { __test__ } from "../merger.js";
|
||||
@@ -38,11 +38,18 @@ function createAutostash(dir: string, label: string, content: string): string {
|
||||
return sha;
|
||||
}
|
||||
|
||||
function assertIsolatedWorkspace(dir: string): void {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
if (!repoRoot) return;
|
||||
expect(resolve(dir).startsWith(resolve(repoRoot))).toBe(false);
|
||||
}
|
||||
|
||||
describe("autostash orphan surface", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-autostash-surface-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-autostash-surface-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import { commitOrAmendMergeWithFixes } from "../merger.js";
|
||||
@@ -33,6 +33,12 @@ function stageSquashThenClear(dir: string, branch: string, file: string, content
|
||||
return preAttemptSha;
|
||||
}
|
||||
|
||||
function assertIsolatedWorkspace(dir: string): void {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
if (!repoRoot) return;
|
||||
expect(resolve(dir).startsWith(resolve(repoRoot))).toBe(false);
|
||||
}
|
||||
|
||||
const STUB_SETTINGS = {
|
||||
...DEFAULT_SETTINGS,
|
||||
commitAuthorEnabled: false,
|
||||
@@ -42,7 +48,8 @@ describe("commitOrAmendMergeWithFixes no-op finalize", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-noop-finalize-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-noop-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import { snapshotDirtyFiles, commitOrAmendMergeWithFixes } from "../merger.js";
|
||||
@@ -74,6 +74,12 @@ function squashBranch(dir: string, branchName: string, fileName: string, content
|
||||
// Minimal stub settings / args used by commitOrAmendMergeWithFixes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function assertIsolatedWorkspace(dir: string): void {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
if (!repoRoot) return;
|
||||
expect(resolve(dir).startsWith(resolve(repoRoot))).toBe(false);
|
||||
}
|
||||
|
||||
const STUB_SETTINGS = {
|
||||
...DEFAULT_SETTINGS,
|
||||
commitAuthorEnabled: false, // skip --author flag to avoid user config issues
|
||||
@@ -87,7 +93,8 @@ describe("snapshotDirtyFiles", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-snapshot-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-snapshot-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
@@ -135,7 +142,8 @@ describe("snapshotDirtyFiles", () => {
|
||||
});
|
||||
|
||||
it("returns empty set when rootDir is not a git repo (error swallowed)", async () => {
|
||||
const nonRepo = mkdtempSync(join(tmpdir(), "fn-non-repo-"));
|
||||
const nonRepo = mkdtempSync(join(tmpdir(), "fusion-test-merger-non-repo-"));
|
||||
assertIsolatedWorkspace(nonRepo);
|
||||
try {
|
||||
const snapshot = await snapshotDirtyFiles(nonRepo);
|
||||
expect(snapshot.size).toBe(0);
|
||||
@@ -150,7 +158,8 @@ describe("commitOrAmendMergeWithFixes — staging allowlist", () => {
|
||||
let warnSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-allowlist-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-allowlist-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
warnSpy = vi.spyOn(mergerLog, "warn");
|
||||
});
|
||||
@@ -480,7 +489,8 @@ describe("snapshotDirtyFiles — paths with embedded spaces", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-snapshot-spaces-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-snapshot-spaces-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
});
|
||||
|
||||
@@ -532,7 +542,8 @@ describe("commitOrAmendMergeWithFixes — embedded-space paths round-trip", () =
|
||||
let warnSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "fn-allowlist-spaces-"));
|
||||
dir = mkdtempSync(join(tmpdir(), "fusion-test-merger-allowlist-spaces-"));
|
||||
assertIsolatedWorkspace(dir);
|
||||
initRepo(dir);
|
||||
warnSpy = vi.spyOn(mergerLog, "warn");
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { tmpdir } from "node:os";
|
||||
import { getFusionAuthPath } from "../auth-storage.js";
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
describe("test isolation guard", () => {
|
||||
it("overrides HOME to a temp fn-test-home directory", () => {
|
||||
@@ -20,4 +22,15 @@ describe("test isolation guard", () => {
|
||||
expect(authPath.startsWith(home!)).toBe(true);
|
||||
expect(authPath).toContain(".fusion");
|
||||
});
|
||||
|
||||
it("creates temp workspaces outside the real repo root", () => {
|
||||
const workspace = mkdtempSync(join(tmpdir(), "fusion-test-guard-workspace-"));
|
||||
try {
|
||||
const repoRoot = process.env.FUSION_TEST_REAL_ROOT;
|
||||
expect(repoRoot).toBeDefined();
|
||||
expect(resolve(workspace).startsWith(resolve(repoRoot!))).toBe(false);
|
||||
} finally {
|
||||
rmSync(workspace, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user