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:
80
packages/engine/src/__tests__/executor-retry-storm.test.ts
Normal file
80
packages/engine/src/__tests__/executor-retry-storm.test.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user