test(FN-4398): complete Step 4 — add retry storm integration coverage

Fusion-Task-Id: FN-4398
Fusion-Task-Lineage: 8b898b2e-3468-4fa7-8546-b8f6ba52cf46
This commit is contained in:
Fusion
2026-05-14 14:58:15 -07:00
committed by gsxdsm
parent 9230c80fb0
commit 233c5bbd8e

View File

@@ -0,0 +1,80 @@
import { describe, expect, it, vi } from "vitest";
import {
RetryStormError,
serializeRetryStormError,
type TaskDetail,
} from "@fusion/core";
import { recordRetry } from "../retry-burned-logger.js";
function makeTask(overrides: Partial<TaskDetail> = {}): TaskDetail {
return {
id: "FN-4398",
lineageId: "lineage-1",
description: "retry storm",
column: "in-review",
status: "failed",
dependencies: [],
steps: [],
currentStep: 0,
log: [],
prompt: "prompt",
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
...overrides,
};
}
describe("executor retry storm integration", () => {
it("throws and serializes RetryStormError on 26th retry and stays terminal", async () => {
const task = makeTask({
stuckKillCount: 5,
recoveryRetryCount: 5,
taskDoneRetryCount: 5,
workflowStepRetries: 5,
verificationFailureCount: 5,
reviewerContextRetryCount: 0,
});
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
const store = {
updateTask: vi.fn(async (_id: string, patch: Record<string, unknown>) => {
Object.assign(task, patch);
}),
getTask: vi.fn(async () => task),
moveTask: vi.fn(async () => undefined),
};
let thrown: RetryStormError | undefined;
try {
await recordRetry({
store: store as never,
settings: {
maxReviewerContextRetries: 2,
maxTotalRetriesBeforeFail: 25,
},
task,
category: "reviewerContext",
role: "reviewer",
attempt: 1,
});
} catch (error) {
thrown = error as RetryStormError;
const serialized = serializeRetryStormError(thrown);
await store.updateTask(task.id, { error: JSON.stringify(serialized), status: "failed" });
}
expect(thrown).toBeInstanceOf(RetryStormError);
expect(task.error).toContain('"type":"RetryStormError"');
expect(task.error).toContain('"category":"reviewerContext"');
expect(task.error).toContain('"total":26');
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining("[retry-burned] retry-burned"),
expect.objectContaining({ taskId: task.id, category: "reviewerContext", total: expect.any(Number) }),
);
expect(task.column).toBe("in-review");
expect(store.moveTask).not.toHaveBeenCalled();
consoleSpy.mockRestore();
});
});