import assert from "node:assert/strict"; import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; const api = readFileSync("src/api/campaigns.ts", "utf8"); const page = readFileSync("src/features/reports/AggregateReportsPage.tsx", "utf8"); const reportPage = readFileSync("src/features/campaigns/CampaignReportPage.tsx", "utf8"); const reviewPage = readFileSync("src/features/campaigns/ReviewSendPage.tsx", "utf8"); const operatorPage = readFileSync("src/features/operator/OperatorQueuePage.tsx", "utf8"); const deliveryMode = readFileSync("src/features/campaigns/utils/deliveryMode.ts", "utf8"); const translationCatalog = readFileSync("src/i18n/generatedTranslations.ts", "utf8"); assert(api.includes('"/api/v1/campaigns/aggregate-reports"'), "the report list uses the safe aggregate collection"); assert(api.includes("/api/v1/campaigns/aggregate-reports/${encodeURIComponent(campaignId)}"), "the selected report uses the safe aggregate projection"); assert(page.includes("listAggregateReportCampaigns"), "the page loads only the safe Campaign selector projection"); assert(page.includes("getAggregateCampaignReport"), "the page loads only the safe aggregate report projection"); assert(!page.includes("getCampaignReport"), "the aggregate page cannot load the recipient-aware full report"); assert(!page.includes("getCampaignJobs"), "the aggregate page cannot cache recipient jobs"); assert(!page.includes("getCampaignJobDetail"), "the aggregate page has no detail route"); assert(!page.includes("downloadCampaignJobsCsv"), "the aggregate page has no export path"); assert(!page.includes("localStorage"), "the aggregate page does not persist report data in local storage"); assert(!page.includes("sessionStorage"), "the aggregate page does not persist report data in session storage"); assert(page.includes("TableActionGroup"), "campaign selection uses the central icon-only table action group"); assert(page.includes("disabled: campaign.id === selectedFromUrl"), "the selected row action stays visible and disabled"); assert(page.includes('columnType: "from-list"'), "campaign status uses the stable shared list-filter model"); const expectedCampaignStatuses = [ "draft", "validated", "needs_review", "ready_to_queue", "queued", "sending", "sent", "partially_completed", "outcome_unknown", "failed", "cancelled", "archived" ]; for (const status of expectedCampaignStatuses) { assert(page.includes(`{ value: "${status}", label:`), `the aggregate list declares the ${status} status explicitly`); } const outcomeCardsStart = page.indexOf("Aggregate outcome cards are deliberately not filter shortcuts"); const outcomeCardsEnd = page.indexOf('