feat(FN-3860): add cadence resolution seam (+7 more)

Commits merged:
- feat(FN-3860): complete Step 8 — documentation and delivery
- test(FN-3860): complete Step 7 — add scaffold seam coverage
- feat(FN-3860): complete Step 6 — re-export scaffold seams
- feat(FN-3860): complete Step 5 — add pipeline orchestrator seam
- feat(FN-3860): complete Step 4 — add in-memory runs store seam
- feat(FN-3860): complete Step 3 — add aggregation seam
- feat(FN-3860): complete Step 2 — add cadence resolution seam
- feat(FN-3846): short-circuit phantom merges for already-landed branches

Files changed:
.changeset/fn-3860-reports-scaffold-files.md       |  5 ++
 plugins/fusion-plugin-reports/README.md            |  9 +++
 .../src/__tests__/scaffold.test.ts                 | 60 +++++++++++++++++++
 plugins/fusion-plugin-reports/src/aggregation.ts   | 23 ++++++++
 plugins/fusion-plugin-reports/src/cadence.ts       | 23 ++++++++
 plugins/fusion-plugin-reports/src/index.ts         |  4 ++
 plugins/fusion-plugin-reports/src/pipeline.ts      | 58 ++++++++++++++++++
 plugins/fusion-plugin-reports/src/runs-store.ts    | 69 ++++++++++++++++++++++
 8 files changed, 251 insertions(+)

Fusion-Task-Id: FN-3860
This commit is contained in:
Fusion
2026-05-11 07:50:58 -07:00
committed by gsxdsm
parent 118e466104
commit 76c113c2f2
8 changed files with 251 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import { aggregateReportData } from "../aggregation.js";
import { resolveEnabledCadences } from "../cadence.js";
import { startReportsPipeline } from "../pipeline.js";
import { createInMemoryReportsRunsStore } from "../runs-store.js";
describe("reports scaffold seams", () => {
it("resolves daily and weekly cadence by default in UTC", () => {
expect(resolveEnabledCadences({})).toEqual([
{ cadence: "daily", timezone: "UTC" },
{ cadence: "weekly", timezone: "UTC" },
]);
});
it("resolves weekly-only cadence with configured timezone", () => {
expect(
resolveEnabledCadences({ dailyEnabled: false, weeklyEnabled: true, timezone: "America/Los_Angeles" }),
).toEqual([{ cadence: "weekly", timezone: "America/Los_Angeles" }]);
});
it("returns empty sections from scaffold aggregator", async () => {
const output = await aggregateReportData({ runId: "run-1", cadence: "daily", settings: {} });
expect(output.sections).toEqual([]);
});
it("runs pipeline to review status on happy path", async () => {
const runsStore = createInMemoryReportsRunsStore();
const result = await startReportsPipeline(
{ runId: "run-1", cadence: "daily", settings: {} },
{ runsStore, aggregate: aggregateReportData },
);
expect(result.status).toBe("review");
const stored = await runsStore.get("run-1");
expect(stored).toEqual(result);
expect(stored?.cadence).toBe("daily");
});
it("marks pipeline run failed when aggregation throws and does not rethrow", async () => {
const runsStore = createInMemoryReportsRunsStore();
const result = await startReportsPipeline(
{ runId: "run-2", cadence: "weekly", settings: {} },
{
runsStore,
aggregate: async () => {
throw new Error("boom");
},
},
);
expect(result.status).toBe("failed");
expect(result.error).toBe("boom");
await expect(runsStore.get("run-2")).resolves.toEqual(result);
});
it("returns undefined when updating unknown run", async () => {
const runsStore = createInMemoryReportsRunsStore();
await expect(runsStore.update("missing", { status: "failed" })).resolves.toBeUndefined();
});
});