feat(FN-3826): canonicalize reports plugin manifest ID

Canonicalizes the Fusion reports plugin's manifest ID and exports a reports schema alias from the package index, with tests aligned to the new identifiers.

Fusion-Task-Id: FN-3826
This commit is contained in:
Fusion
2026-05-11 02:06:34 -07:00
committed by gsxdsm
parent 18bbc0cad8
commit 74e7aa8f0c
8 changed files with 336 additions and 4 deletions

View File

@@ -259,8 +259,8 @@ describe("Insights routes", () => {
expect((res.body as { error: string }).error).toContain("Invalid trigger");
});
it("POST /api/insights/run returns 409 when an active run exists for trigger", async () => {
storeA.getInsightStore().createRun("", { trigger: "manual" });
it("POST /api/insights/run returns structured 409 details when active run is still live", async () => {
const activeRun = storeA.getInsightStore().createRun("", { trigger: "manual" });
const res = await request(
app,
@@ -271,6 +271,79 @@ describe("Insights routes", () => {
);
expect(res.status).toBe(409);
expect(res.body).toMatchObject({
error: "Insight generation is already running",
details: {
code: "ACTIVE_RUN_CONFLICT",
activeRunId: activeRun.id,
activeRunStatus: "pending",
trigger: "manual",
},
});
});
it("POST /api/insights/run recovers stale active run older than orphan grace", async () => {
const insightStore = storeA.getInsightStore();
const staleRun = insightStore.createRun("", { trigger: "manual" });
const staleAt = new Date(Date.now() - 45_000).toISOString();
storeA.getDatabase().prepare("UPDATE project_insight_runs SET startedAt = ? WHERE id = ?").run(staleAt, staleRun.id);
const res = await request(
app,
"POST",
"/api/insights/run",
JSON.stringify({ trigger: "manual" }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(201);
const recoveredRun = insightStore.getRun(staleRun.id);
expect(recoveredRun?.status).toBe("failed");
expect(recoveredRun?.lifecycle.terminalReason).toBe("failed");
expect(recoveredRun?.lifecycle.terminalCause).toBe("orphaned_active_run_recovered");
expect(recoveredRun?.lifecycle.failureClass).toBe("non_retryable");
expect(recoveredRun?.lifecycle.retryable).toBe(false);
const events = insightStore.listRunEvents(staleRun.id);
expect(events.some((event) => event.type === "warning" && event.message.includes("Recovered orphaned active run"))).toBe(true);
expect(events.some((event) => event.type === "status_changed" && event.status === "failed")).toBe(true);
});
it("POST /api/insights/run uses createdAt fallback for stale check when startedAt is null", async () => {
const insightStore = storeA.getInsightStore();
const staleRun = insightStore.createRun("", { trigger: "manual" });
const staleCreatedAt = new Date(Date.now() - 45_000).toISOString();
storeA.getDatabase().prepare("UPDATE project_insight_runs SET createdAt = ?, startedAt = NULL WHERE id = ?").run(staleCreatedAt, staleRun.id);
const res = await request(
app,
"POST",
"/api/insights/run",
JSON.stringify({ trigger: "manual" }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(201);
expect(insightStore.getRun(staleRun.id)?.status).toBe("failed");
});
it("POST /api/insights/run does not recover young active runs before grace threshold", async () => {
const insightStore = storeA.getInsightStore();
const run = insightStore.createRun("", { trigger: "manual" });
const youngAt = new Date(Date.now() - 5_000).toISOString();
storeA.getDatabase().prepare("UPDATE project_insight_runs SET startedAt = ? WHERE id = ?").run(youngAt, run.id);
const res = await request(
app,
"POST",
"/api/insights/run",
JSON.stringify({ trigger: "manual" }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(409);
expect(insightStore.getRun(run.id)?.status).toBe("pending");
});
it("POST /api/insights/run persists completed run metadata", async () => {