diff --git a/apps/loopover-miner-ui/src/lib/demo-data.test.ts b/apps/loopover-miner-ui/src/lib/demo-data.test.ts index 90e51bb6d1..3339a2165f 100644 --- a/apps/loopover-miner-ui/src/lib/demo-data.test.ts +++ b/apps/loopover-miner-ui/src/lib/demo-data.test.ts @@ -78,6 +78,30 @@ describe("demo portfolio-queue items (#5963)", () => { expect(getDemoPortfolioQueueItems().length).toBeGreaterThan(0); }); + it("its in_progress/done counts match DEMO_PORTFOLIO_QUEUE_SUMMARY both fleet-wide AND per repo (#7227)", () => { + const items = getDemoPortfolioQueueItems(); + const count = (rows: typeof items, status: string) => rows.filter((i) => i.status === status).length; + + // Fleet-wide: the table can't disagree with the status cards above it. + expect(count(items, "in_progress")).toBe(DEMO_PORTFOLIO_QUEUE_SUMMARY.byStatus.in_progress); + expect(count(items, "done")).toBe(DEMO_PORTFOLIO_QUEUE_SUMMARY.byStatus.done); + + // Per repo: each repo's actionable split matches its summary byStatus (only `queued`, which an actionable + // item can't represent, is absent) -- so the fixture is internally consistent at every level. + for (const repo of DEMO_PORTFOLIO_QUEUE_SUMMARY.repos) { + const rows = items.filter((i) => i.repoFullName === repo.repoFullName); + expect(count(rows, "in_progress")).toBe(repo.byStatus.in_progress); + expect(count(rows, "done")).toBe(repo.byStatus.done); + } + }); + + it("references only the four synthetic demo repos, never a real repo name (#7227)", () => { + const allowed = new Set(DEMO_PORTFOLIO_QUEUE_SUMMARY.repos.map((r) => r.repoFullName)); + for (const item of getDemoPortfolioQueueItems()) { + expect(allowed.has(item.repoFullName)).toBe(true); + } + }); + it("removeDemoPortfolioQueueItem removes and returns the matching item", () => { const beforeCount = getDemoPortfolioQueueItems().length; const target = { ...getDemoPortfolioQueueItems()[0]! }; diff --git a/apps/loopover-miner-ui/src/lib/demo-data.ts b/apps/loopover-miner-ui/src/lib/demo-data.ts index a205d8cc16..4e30d57ff2 100644 --- a/apps/loopover-miner-ui/src/lib/demo-data.ts +++ b/apps/loopover-miner-ui/src/lib/demo-data.ts @@ -97,26 +97,35 @@ export const DEMO_PORTFOLIO_QUEUE_SUMMARY: PortfolioQueueSummary = { oldestQueuedAgeMs: 6 * 60 * 60 * 1000, // 6h }; +// Every actionable (in_progress/done) row the fleet's DEMO_PORTFOLIO_QUEUE_SUMMARY reports (#7227): 3 +// in_progress + 15 done = 18, so the demo "Queue actions" table can't visibly disagree with the status cards. +// The per-repo in_progress/done split also matches each repo's summary byStatus (widgets 1/6, api-gateway 1/4, +// docs-site 0/3, inventory 1/2) -- only the summary's `queued` rows, which PortfolioQueueActionItem can't +// represent, are absent. Entirely synthetic: only the four demo repos and the existing wgt-/gw-/docs-/inv- ids. +const FORGE = "https://forge.example.com"; const DEFAULT_DEMO_PORTFOLIO_QUEUE_ITEMS: PortfolioQueueActionItem[] = [ - { - apiBaseUrl: "https://forge.example.com", - repoFullName: "acme/widgets", - identifier: "wgt-2451", - status: "in_progress", - }, - { - apiBaseUrl: "https://forge.example.com", - repoFullName: "acme/api-gateway", - identifier: "gw-118", - status: "in_progress", - }, - { apiBaseUrl: "https://forge.example.com", repoFullName: "acme/widgets", identifier: "wgt-2438", status: "done" }, - { - apiBaseUrl: "https://forge.example.com", - repoFullName: "northwind/inventory", - identifier: "inv-77", - status: "done", - }, + // acme/widgets — 1 in_progress, 6 done + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2451", status: "in_progress" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2438", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2402", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2377", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2340", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2311", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/widgets", identifier: "wgt-2288", status: "done" }, + // acme/api-gateway — 1 in_progress, 4 done + { apiBaseUrl: FORGE, repoFullName: "acme/api-gateway", identifier: "gw-118", status: "in_progress" }, + { apiBaseUrl: FORGE, repoFullName: "acme/api-gateway", identifier: "gw-104", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/api-gateway", identifier: "gw-97", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/api-gateway", identifier: "gw-83", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/api-gateway", identifier: "gw-76", status: "done" }, + // acme/docs-site — 0 in_progress, 3 done + { apiBaseUrl: FORGE, repoFullName: "acme/docs-site", identifier: "docs-58", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/docs-site", identifier: "docs-51", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "acme/docs-site", identifier: "docs-49", status: "done" }, + // northwind/inventory — 1 in_progress, 2 done + { apiBaseUrl: FORGE, repoFullName: "northwind/inventory", identifier: "inv-77", status: "in_progress" }, + { apiBaseUrl: FORGE, repoFullName: "northwind/inventory", identifier: "inv-71", status: "done" }, + { apiBaseUrl: FORGE, repoFullName: "northwind/inventory", identifier: "inv-64", status: "done" }, ]; // Mutable, in-memory, browser-session-only copy -- release/requeue removes the item from this actionable list