test(FN-4763): expand reclaim guard-path coverage
Fusion-Task-Id: FN-4763 Fusion-Task-Lineage: b3628e63-682b-4a39-9a7d-2c96278c5366
This commit is contained in:
committed by
gsxdsm
parent
893fb32b69
commit
86c3063d9d
@@ -5,6 +5,7 @@ import { SelfHealingManager } from "../self-healing.js";
|
|||||||
import { AutoRecoveryDispatcher } from "../auto-recovery.js";
|
import { AutoRecoveryDispatcher } from "../auto-recovery.js";
|
||||||
import * as branchConflicts from "../branch-conflicts.js";
|
import * as branchConflicts from "../branch-conflicts.js";
|
||||||
import * as worktreePool from "../worktree-pool.js";
|
import * as worktreePool from "../worktree-pool.js";
|
||||||
|
import { activeSessionRegistry } from "../active-session-registry.js";
|
||||||
|
|
||||||
function makeTask(overrides: Partial<Task> = {}): Task {
|
function makeTask(overrides: Partial<Task> = {}): Task {
|
||||||
return {
|
return {
|
||||||
@@ -27,19 +28,19 @@ function makeTask(overrides: Partial<Task> = {}): Task {
|
|||||||
} as Task;
|
} as Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeStore(task: Task, paused = false): TaskStore & EventEmitter {
|
function makeStore(task: Task | null, paused = false, enginePaused = false): TaskStore & EventEmitter {
|
||||||
const emitter = new EventEmitter();
|
const emitter = new EventEmitter();
|
||||||
const settings = { globalPause: paused, enginePaused: false, autoRecovery: { mode: "deterministic-only", maxRetries: 3 } } as Settings;
|
const settings = { globalPause: paused, enginePaused, autoRecovery: { mode: "deterministic-only", maxRetries: 3 } } as Settings;
|
||||||
return Object.assign(emitter, {
|
return Object.assign(emitter, {
|
||||||
getSettings: vi.fn(async () => settings),
|
getSettings: vi.fn(async () => settings),
|
||||||
getTask: vi.fn((id: string) => (id === task.id ? task : null)),
|
getTask: vi.fn((id: string) => (task && id === task.id ? task : null)),
|
||||||
listTasks: vi.fn(async ({ column }: { column?: string } = {}) => {
|
listTasks: vi.fn(async ({ column }: { column?: string } = {}) => {
|
||||||
|
if (!task) return [];
|
||||||
if (!column) return [task];
|
if (!column) return [task];
|
||||||
if (column === "in-progress") return [task];
|
if (column === "in-progress") return [task];
|
||||||
return [];
|
return [];
|
||||||
}),
|
}),
|
||||||
updateTask: vi.fn(async (_id: string, updates: Partial<Task>) => Object.assign(task, updates)),
|
updateTask: vi.fn(async (_id: string, updates: Partial<Task>) => (task ? Object.assign(task, updates) : null)), moveTask: vi.fn(async (_id: string, column: Task["column"]) => {
|
||||||
moveTask: vi.fn(async (_id: string, column: Task["column"]) => {
|
|
||||||
task.column = column;
|
task.column = column;
|
||||||
return task;
|
return task;
|
||||||
}),
|
}),
|
||||||
@@ -55,6 +56,7 @@ function makeStore(task: Task, paused = false): TaskStore & EventEmitter {
|
|||||||
describe("SelfHealingManager.reclaimPrConflictForTask", () => {
|
describe("SelfHealingManager.reclaimPrConflictForTask", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.restoreAllMocks();
|
vi.restoreAllMocks();
|
||||||
|
activeSessionRegistry.clear();
|
||||||
vi.spyOn(worktreePool, "isUsableTaskWorktree").mockResolvedValue(true);
|
vi.spyOn(worktreePool, "isUsableTaskWorktree").mockResolvedValue(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,6 +108,14 @@ describe("SelfHealingManager.reclaimPrConflictForTask", () => {
|
|||||||
expect(result).toEqual({ outcome: "skipped", reason: "worktrunk-paused" });
|
expect(result).toEqual({ outcome: "skipped", reason: "worktrunk-paused" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("skips when engine pause is active", async () => {
|
||||||
|
const task = makeTask();
|
||||||
|
const store = makeStore(task, false, true);
|
||||||
|
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
|
||||||
|
const result = await manager.reclaimPrConflictForTask(task.id);
|
||||||
|
expect(result).toEqual({ outcome: "skipped", reason: "engine-paused" });
|
||||||
|
});
|
||||||
|
|
||||||
it("skips when global pause is active", async () => {
|
it("skips when global pause is active", async () => {
|
||||||
const task = makeTask();
|
const task = makeTask();
|
||||||
const store = makeStore(task, true);
|
const store = makeStore(task, true);
|
||||||
@@ -113,4 +123,38 @@ describe("SelfHealingManager.reclaimPrConflictForTask", () => {
|
|||||||
const result = await manager.reclaimPrConflictForTask(task.id);
|
const result = await manager.reclaimPrConflictForTask(task.id);
|
||||||
expect(result).toEqual({ outcome: "skipped", reason: "engine-paused" });
|
expect(result).toEqual({ outcome: "skipped", reason: "engine-paused" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns task-not-found for missing task", async () => {
|
||||||
|
const store = makeStore(null);
|
||||||
|
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
|
||||||
|
const result = await manager.reclaimPrConflictForTask("FN-404");
|
||||||
|
expect(result).toEqual({ outcome: "skipped", reason: "task-not-found" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips when branch or worktree is missing", async () => {
|
||||||
|
const task = makeTask({ branch: null });
|
||||||
|
const store = makeStore(task);
|
||||||
|
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
|
||||||
|
const result = await manager.reclaimPrConflictForTask(task.id);
|
||||||
|
expect(result).toEqual({ outcome: "skipped", reason: "missing-branch-or-worktree" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips when worktree has an active session", async () => {
|
||||||
|
const task = makeTask();
|
||||||
|
const store = makeStore(task);
|
||||||
|
activeSessionRegistry.registerPath(task.worktree!, { taskId: task.id, kind: "executor", ownerKey: task.id });
|
||||||
|
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
|
||||||
|
const result = await manager.reclaimPrConflictForTask(task.id);
|
||||||
|
expect(result).toEqual({ outcome: "skipped", reason: "active-session" });
|
||||||
|
activeSessionRegistry.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips unusable worktree", async () => {
|
||||||
|
const task = makeTask();
|
||||||
|
const store = makeStore(task);
|
||||||
|
vi.spyOn(worktreePool, "isUsableTaskWorktree").mockResolvedValueOnce(false);
|
||||||
|
const manager = new SelfHealingManager(store as any, { rootDir: "/tmp/test" } as any);
|
||||||
|
const result = await manager.reclaimPrConflictForTask(task.id);
|
||||||
|
expect(result).toEqual({ outcome: "skipped", reason: "unusable-worktree" });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user