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:
committed by
gsxdsm
parent
bb3403370b
commit
5e3d57b1cc
@@ -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();
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user