Files
fusion/packages/engine/src/__tests__/worktree-acquisition-backend.test.ts
Fusion (runfusion.ai) 1bffa22ca9 feat(FN-5479): fix identity-guard merger bypass on detached HEAD and gate l
The merge delivers the FN-5483 identity-guard bypass for merger-driven commits on detached HEAD, plus Steps 2 and 6 of FN-5479 which gate the limbo counter by enqueue acceptance and document the associated invariant. It also restores the dashboard's PWA and theme-boot contract in index.html. New reg

Fusion-Task-Id: FN-5479
2026-05-22 08:31:12 -07:00

176 lines
6.3 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../worktree-hooks.js", () => ({
installTaskWorktreeIdentityGuard: vi.fn().mockResolvedValue(undefined),
IDENTITY_GUARD_BYPASS_ENV: "FUSION_MERGER_BYPASS_IDENTITY_GUARD",
}));
import { acquireTaskWorktree } from "../worktree-acquisition.js";
import type { WorktreeBackend } from "../worktree-backend.js";
vi.mock("../worktree-pool.js", async () => {
const actual = await vi.importActual<any>("../worktree-pool.js");
return { ...actual, isUsableTaskWorktree: vi.fn().mockResolvedValue(true) };
});
vi.mock("../worktree-db-hydrate.js", () => ({
hydrateWorktreeDb: vi.fn().mockResolvedValue({ degraded: false, tasksCopied: 1, documentsCopied: 1 }),
}));
const { execMock, existsSyncMock, accessMock } = vi.hoisted(() => {
const mock = vi.fn();
(mock as any)[Symbol.for("nodejs.util.promisify.custom")] = mock;
return { execMock: mock, existsSyncMock: vi.fn(), accessMock: vi.fn().mockResolvedValue(undefined) };
});
vi.mock("node:child_process", () => ({ exec: execMock }));
vi.mock("node:fs", () => ({ existsSync: existsSyncMock }));
vi.mock("node:fs/promises", () => ({ access: accessMock }));
describe("acquireTaskWorktree backend wiring", () => {
const task = { id: "FN-1", title: "Task", description: "Desc", branch: null, worktree: null } as any;
const store = {
updateTask: vi.fn().mockResolvedValue(undefined),
pauseTask: vi.fn().mockResolvedValue(undefined),
logEntry: vi.fn().mockResolvedValue(undefined),
} as any;
beforeEach(() => {
execMock.mockReset();
existsSyncMock.mockReset();
existsSyncMock.mockReturnValue(true);
accessMock.mockReset();
accessMock.mockResolvedValue(undefined);
store.updateTask.mockClear();
store.logEntry.mockClear();
store.pauseTask.mockClear();
});
it("uses native backend by default and emits no worktrunk audit", async () => {
execMock.mockResolvedValue({ stdout: "", stderr: "" });
const audit = {
git: vi.fn().mockResolvedValue(undefined),
database: vi.fn().mockResolvedValue(undefined),
filesystem: vi.fn().mockResolvedValue(undefined),
sandbox: vi.fn().mockResolvedValue(undefined),
};
const result = await acquireTaskWorktree({
task,
rootDir: "/repo",
store,
settings: { worktreeNaming: "task-id" } as any,
audit,
});
expect(result.branch).toBe("fusion/fn-1");
expect(result.worktreePath).toBe("/repo/.worktrees/fn-1");
expect(execMock).toHaveBeenCalledWith(
'git worktree add -b "fusion/fn-1" "/repo/.worktrees/fn-1"',
expect.objectContaining({ cwd: "/repo" }),
);
expect(audit.git).not.toHaveBeenCalledWith(
expect.objectContaining({ type: "worktree:worktrunk-create" }),
);
});
it("routes through worktrunk backend when enabled and emits audit once", async () => {
execMock.mockImplementation((command: string) => {
if (command.includes('"config" "show"')) return Promise.resolve({ stdout: "", stderr: "" });
if (command.includes('"switch" "--create"')) return Promise.resolve({ stdout: "", stderr: "" });
if (command === "git worktree list --porcelain") {
return Promise.resolve({
stdout: "worktree /repo/.worktrees/fusion/fn-1\nbranch refs/heads/fusion/fn-1\n",
stderr: "",
});
}
return Promise.resolve({ stdout: "", stderr: "" });
});
const audit = {
git: vi.fn().mockResolvedValue(undefined),
database: vi.fn().mockResolvedValue(undefined),
filesystem: vi.fn().mockResolvedValue(undefined),
sandbox: vi.fn().mockResolvedValue(undefined),
};
await acquireTaskWorktree({
task,
rootDir: "/repo",
store,
settings: { worktreeNaming: "task-id", worktrunk: { enabled: true, binaryPath: "wt" } } as any,
audit,
});
expect(execMock.mock.calls.some((call) => String(call[0]).includes('"wt" "switch" "--create" "fusion/fn-1"'))).toBe(true);
expect(audit.git).toHaveBeenCalledWith(
expect.objectContaining({
type: "worktree:worktrunk-create",
metadata: expect.objectContaining({ branch: "fusion/fn-1" }),
}),
);
expect(
audit.git.mock.calls.filter(([event]) => event?.type === "worktree:worktrunk-create"),
).toHaveLength(1);
expect(audit.git).toHaveBeenCalledWith(
expect.objectContaining({ type: "worktree:create" }),
);
});
it("throws worktrunk_binary_missing with no binaryPath", async () => {
await expect(
acquireTaskWorktree({
task,
rootDir: "/repo",
store,
settings: { worktreeNaming: "task-id", worktrunk: { enabled: true } } as any,
}),
).rejects.toMatchObject({ name: "WorktrunkOperationError", code: "worktrunk_binary_missing" });
expect(execMock).not.toHaveBeenCalled();
});
it("throws worktrunk_operation_failed and preserves stderr", async () => {
execMock.mockRejectedValue({ stderr: "worktrunk exploded", status: 17 });
const explicitBinaryPath = "/opt/wt";
await expect(
acquireTaskWorktree({
task,
rootDir: "/repo",
store,
settings: { worktreeNaming: "task-id", worktrunk: { enabled: true, binaryPath: explicitBinaryPath } } as any,
}),
).rejects.toMatchObject({
name: "WorktrunkOperationError",
code: "worktrunk_operation_failed",
stderr: "worktrunk exploded",
exitCode: 17,
});
expect(execMock.mock.calls.some((call) => String(call[0]).includes(`"${explicitBinaryPath}" "switch" "--create"`))).toBe(true);
});
it("uses explicit backend override", async () => {
const create = vi.fn().mockResolvedValue({ path: "/tmp/backend", branch: "fusion/fn-backend" });
const backend: WorktreeBackend = {
kind: "native",
create,
remove: vi.fn(),
sync: vi.fn().mockResolvedValue({ skipped: true as const }),
prune: vi.fn(),
resolveWorktreePath: vi.fn().mockResolvedValue("/tmp/custom-path"),
};
const result = await acquireTaskWorktree({
task,
rootDir: "/repo",
store,
settings: { worktreeNaming: "task-id", worktrunk: { enabled: true } } as any,
backend,
});
expect(result.worktreePath).toBe("/tmp/backend");
expect(result.branch).toBe("fusion/fn-backend");
expect(create).toHaveBeenCalledTimes(1);
expect(execMock).not.toHaveBeenCalled();
});
});