test(FN-5147): complete Step 6 — stabilize verification coverage

Fusion-Task-Id: FN-5147
Fusion-Task-Lineage: 2a0e5bd4-b2ea-498c-95ef-85126819e824
This commit is contained in:
Fusion (runfusion.ai)
2026-05-19 09:50:15 -07:00
committed by gsxdsm
parent bb3403370b
commit 5e3d57b1cc
3 changed files with 23 additions and 18 deletions

View File

@@ -22,9 +22,14 @@ function makeTask(id: string, overrides: Partial<Task> = {}): Task {
} as Task;
}
function createStore(tasks: Task[]): TaskStore & EventEmitter {
function createStore(tasks: Task[], settingsOverrides: Partial<Settings> = {}): TaskStore & EventEmitter {
const map = new Map(tasks.map((t) => [t.id, t]));
const settings: Settings = { globalPause: false, enginePaused: false, autoMerge: false } as Settings;
const settings: Settings = {
globalPause: false,
enginePaused: false,
autoMerge: false,
...settingsOverrides,
} as Settings;
const emitter = new EventEmitter();
return Object.assign(emitter, {
getSettings: vi.fn(async () => settings),
@@ -52,7 +57,7 @@ const opts = { rootDir: "/repo", getExecutingTaskIds: () => new Set<string>() };
describe("FN-4115 stranded in-review recovery", () => {
it("FN-4115: already-merged retry-exhausted in-review task auto-finalizes to done", async () => {
const task = makeTask("FN-4115-A", { column: "in-review", status: "failed", mergeRetries: 3, branch: "fusion/fn-4115-a" });
const store = createStore([task]);
const store = createStore([task], { autoMerge: true });
const mgr = new SelfHealingManager(store as any, opts);
vi.spyOn(mgr as any, "findAlreadyMergedTaskCommit").mockResolvedValue({ sha: "abc12345", strategy: "task-id-trailer" });
const recovered = await mgr.recoverAlreadyMergedReviewTasks();
@@ -65,7 +70,7 @@ describe("FN-4115 stranded in-review recovery", () => {
const blocked = makeTask("FN-4115-DOWN", { column: "todo", blockedBy: "FN-4115-UP" });
const active = makeTask("FN-4115-ACTIVE", { column: "in-progress" });
const blockedActive = makeTask("FN-4115-DOWN2", { column: "todo", blockedBy: "FN-4115-ACTIVE" });
const store = createStore([done, blocked, active, blockedActive]);
const store = createStore([done, blocked, active, blockedActive], { autoMerge: true });
const mgr = new SelfHealingManager(store as any, opts);
await mgr.clearStaleBlockedBy();
expect((await store.getTask("FN-4115-DOWN"))?.blockedBy).toBeNull();
@@ -85,7 +90,7 @@ describe("FN-4115 stranded in-review recovery", () => {
it("FN-4115: one maintenance cycle finalizes merged review and unblocks downstream work", async () => {
const merged = makeTask("FN-4115-MERGED", { column: "in-review", status: "failed", mergeRetries: 3, branch: "fusion/fn-4115-m" });
const downstream = makeTask("FN-4115-DOWNSTREAM", { column: "todo", blockedBy: "FN-4115-MERGED" });
const store = createStore([merged, downstream]);
const store = createStore([merged, downstream], { autoMerge: true });
const mgr = new SelfHealingManager(store as any, opts);
vi.spyOn(mgr as any, "findAlreadyMergedTaskCommit").mockResolvedValue({ sha: "abc12345", strategy: "task-id-trailer" });
await mgr.recoverAlreadyMergedReviewTasks();

View File

@@ -10,9 +10,9 @@ function makeTask(overrides: Partial<Task> = {}): Task {
description: "d",
column: "in-review",
paused: false,
status: null,
error: null,
steps: [{ id: "1", title: "s", status: "done" as const }],
status: undefined,
error: undefined,
steps: [{ name: "s", status: "done" as const }],
workflowStepResults: [],
dependencies: [],
log: [],
@@ -72,7 +72,7 @@ describe("FN-5147 reliability interactions: in-review autoMerge off", () => {
afterEach(() => vi.useRealTimers());
it("long-quiet in-review remains unchanged across startup + maintenance", async () => {
const task = makeTask({ id: "FN-5147-Q1", steps: [{ id: "1", title: "s", status: "done" as const }] });
const task = makeTask({ id: "FN-5147-Q1", steps: [{ name: "s", status: "done" as const }] });
const store = createStore([task], { taskStuckTimeoutMs: 1_000, inReviewStalledThresholdMs: 1_000 });
const manager = new SelfHealingManager(store, { rootDir: "/tmp/repo" });
@@ -82,7 +82,7 @@ describe("FN-5147 reliability interactions: in-review autoMerge off", () => {
expect(task.column).toBe("in-review");
expect(task.paused).toBe(false);
expect(task.status).toBeNull();
expect(task.status).toBeUndefined();
expect(task.taskDoneRetryCount).toBeUndefined();
expect(task.mergeRetries).toBeUndefined();
expect((store.moveTask as any).mock.calls.length).toBe(0);
@@ -109,7 +109,7 @@ describe("FN-5147 reliability interactions: in-review autoMerge off", () => {
status: "failed",
error: "Agent exited without calling fn_task_done",
taskDoneRetryCount: 1,
steps: [{ id: "1", title: "s1", status: "done" as const }, { id: "2", title: "s2", status: "pending" as any }],
steps: [{ name: "s1", status: "done" as const }, { name: "s2", status: "pending" as const }],
});
const store = createStore([task]);
const manager = new SelfHealingManager(store, { rootDir: "/tmp/repo" });
@@ -124,8 +124,8 @@ describe("FN-5147 reliability interactions: in-review autoMerge off", () => {
it("incomplete in-review task is not moved by stale-incomplete sweep", async () => {
const task = makeTask({
id: "FN-5147-Q4",
status: null,
steps: [{ id: "1", title: "s1", status: "pending" as any }],
status: undefined,
steps: [{ name: "s1", status: "pending" as const }],
});
const store = createStore([task], { taskStuckTimeoutMs: 1_000 });
const manager = new SelfHealingManager(store, { rootDir: "/tmp/repo" });
@@ -143,7 +143,7 @@ describe("FN-5147 reliability interactions: in-review autoMerge off", () => {
status: "failed",
error: "Failed to create worktree after 3 attempts: missing worktree",
worktree: "/tmp/missing",
steps: [{ id: "1", title: "s1", status: "done" as const }, { id: "2", title: "s2", status: "pending" as any }],
steps: [{ name: "s1", status: "done" as const }, { name: "s2", status: "pending" as const }],
});
const store = createStore([task]);
const manager = new SelfHealingManager(store, { rootDir: "/tmp/repo" });

View File

@@ -42,7 +42,7 @@ function createStore(tasks: TaskMap, settings: Partial<Settings> = {}): TaskStor
enginePaused: false,
maintenanceIntervalMs: 0,
taskStuckTimeoutMs: 60_000,
autoMerge: false,
autoMerge: true,
...settings,
} as Settings;
@@ -254,7 +254,7 @@ describeIfGit("SelfHealingManager recoverAlreadyMergedReviewTasks (real git)", (
git(repo, `git worktree add ${JSON.stringify(worktreePath)} fusion/fn-test-misbound`);
const tasks: TaskMap = new Map([
["FN-TEST-MISBOUND", makeTask({ id: "FN-TEST-MISBOUND", column: "in-review", paused: false, baseBranch: "main", branch: "fusion/fn-test-misbound", worktree: worktreePath })],
["FN-TEST-MISBOUND", makeTask({ id: "FN-TEST-MISBOUND", column: "in-review", status: "failed", paused: false, baseBranch: "main", branch: "fusion/fn-test-misbound", worktree: worktreePath })],
]);
const store = createStore(tasks);
const manager = new SelfHealingManager(store, { rootDir: repo, getExecutingTaskIds: () => new Set() });
@@ -302,12 +302,12 @@ describeIfGit("SelfHealingManager recoverAlreadyMergedReviewTasks (real git)", (
["FN-TEST-5", makeTask({ id: "FN-TEST-5", column: "in-review", status: "failed", mergeRetries: 3, paused: false, baseBranch: "main", branch: "fusion/fn-test-5" })],
]);
const globalPausedStore = createStore(tasks, { globalPause: true, enginePaused: false });
const globalPausedStore = createStore(tasks, { autoMerge: true, globalPause: true, enginePaused: false });
const globalPausedManager = new SelfHealingManager(globalPausedStore, { rootDir: repo, getExecutingTaskIds: () => new Set() });
await globalPausedManager.recoverAlreadyMergedReviewTasks();
expect(globalPausedStore.listTasks).not.toHaveBeenCalled();
const enginePausedStore = createStore(tasks, { globalPause: false, enginePaused: true });
const enginePausedStore = createStore(tasks, { autoMerge: true, globalPause: false, enginePaused: true });
const enginePausedManager = new SelfHealingManager(enginePausedStore, { rootDir: repo, getExecutingTaskIds: () => new Set() });
await enginePausedManager.recoverAlreadyMergedReviewTasks();
expect(enginePausedStore.listTasks).not.toHaveBeenCalled();