test(FN-4763): strengthen pr-conflict reclaim interaction coverage

Fusion-Task-Id: FN-4763
Fusion-Task-Lineage: b3628e63-682b-4a39-9a7d-2c96278c5366
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 12:56:05 -07:00
committed by gsxdsm
parent 86c3063d9d
commit f9bcf2acff
2 changed files with 35 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ import type { Settings, Task, TaskStore } from "@fusion/core";
import { SelfHealingManager } from "../../self-healing.js";
import * as branchConflicts from "../../branch-conflicts.js";
import * as worktreePool from "../../worktree-pool.js";
import { activeSessionRegistry } from "../../active-session-registry.js";
function task(overrides: Partial<Task> = {}): Task {
return {
@@ -51,6 +52,7 @@ function store(t: Task): TaskStore & EventEmitter {
describe("reliability interaction: pr conflict reclaim", () => {
beforeEach(() => {
vi.restoreAllMocks();
activeSessionRegistry.clear();
vi.spyOn(worktreePool, "isUsableTaskWorktree").mockResolvedValue(true);
});
@@ -64,11 +66,22 @@ describe("reliability interaction: pr conflict reclaim", () => {
expect(branchConflicts.inspectBranchConflict).toHaveBeenCalled();
});
it("skips checked-out task during pr conflict reclaim", async () => {
const t = task({ checkedOutBy: "agent-1" });
it("skips active heartbeat-style session during pr conflict reclaim", async () => {
const t = task();
const s = store(t);
activeSessionRegistry.registerPath(t.worktree!, { taskId: t.id, kind: "executor", ownerKey: t.id });
const manager = new SelfHealingManager(s as any, { rootDir: "/tmp/test" } as any);
const result = await manager.reclaimPrConflictForTask(t.id);
expect(result).toEqual({ outcome: "skipped", reason: "checked-out" });
expect(result).toEqual({ outcome: "skipped", reason: "active-session" });
});
it("keeps paused-review reclaim path resumable", async () => {
const t = task();
const s = store(t);
vi.spyOn(branchConflicts, "inspectBranchConflict").mockResolvedValue({ kind: "reclaimable", livePath: t.worktree, tipSha: "abc123", taskAttributedCommitCount: 2, strandedCommits: [{ sha: "abc123" }] } as any);
const manager = new SelfHealingManager(s as any, { rootDir: "/tmp/test" } as any);
const result = await manager.reclaimPrConflictForTask(t.id);
expect(result.outcome).toBe("reclaimed");
expect(t.column).toBe("todo");
});
});

View File

@@ -89,6 +89,25 @@ describe("SelfHealingManager.reclaimPrConflictForTask", () => {
expect(sweepSpy).toHaveBeenCalled();
});
it("returns reclaimed for reclaimable conflicts", async () => {
const task = makeTask({ column: "in-review", paused: true, pausedReason: "branch-conflict-unrecoverable" as any });
const store = makeStore(task);
vi.spyOn(branchConflicts, "inspectBranchConflict").mockResolvedValue({ kind: "reclaimable", livePath: task.worktree, tipSha: "abc123", taskAttributedCommitCount: 1, strandedCommits: [{ sha: "abc123" }] } as any);
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
const result = await manager.reclaimPrConflictForTask(task.id);
expect(result.outcome).toBe("reclaimed");
expect((store.moveTask as any).mock.calls.some((c: any[]) => c[1] === "todo")).toBe(true);
});
it("returns reclaimed for fully-subsumed conflicts", async () => {
const task = makeTask({ branch: "feature/non-fusion-branch" });
const store = makeStore(task);
vi.spyOn(branchConflicts, "inspectBranchConflict").mockResolvedValue({ kind: "fully-subsumed", livePath: task.worktree, tipSha: "abc123", taskAttributedCommitCount: 0, strandedCommits: [] } as any);
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
const result = await manager.reclaimPrConflictForTask(task.id);
expect(result.outcome).toBe("reclaimed");
});
it("returns paused-unrecoverable when conflict is unrecoverable and dispatcher pauses", async () => {
const task = makeTask();
const store = makeStore(task);