import { expect, test } from "@playwright/test"; type ServiceSnapshot = { service: { id: string; name: string; depends_on: string[]; }; latest_probe: { status: string } | null; effective_status: string; }; const topologyOf = (snapshots: ServiceSnapshot[]) => snapshots .map(({ service }) => ({ id: service.id, name: service.name, depends_on: [...service.depends_on].sort(), })) .sort((left, right) => left.id.localeCompare(right.id)); const isDemoMutation = (path: string) => (response: { url: () => string; request: () => { method: () => string }; }) => response.url().endsWith(path) && response.request().method() === "POST"; test("Explore demo exposes evidence-backed root cause and reset-safe exit", async ({ page, request }) => { test.setTimeout(60_000); // The Playwright server uses a persistent temporary database, so restore any // session left by an interrupted run before taking this test's baseline. const cleanup = await request.post("/api/v1/demo/exit"); expect(cleanup.ok()).toBeTruthy(); const baselineResponse = await request.get("/api/v1/services"); expect(baselineResponse.ok()).toBeTruthy(); const baselineTopology = topologyOf(await baselineResponse.json()); await page.goto("/"); const explore = page.getByRole("button", { name: "Explore demo", exact: true }); await expect(explore).toBeEnabled({ timeout: 30_000 }); const loadResponsePromise = page.waitForResponse(isDemoMutation("/api/v1/demo")); await explore.click(); if (baselineTopology.length > 0) { const confirmation = page .getByRole("alert") .filter({ hasText: "temporarily replace this topology" }); await expect(confirmation).toBeVisible(); await confirmation.getByRole("button", { name: "Explore demo", exact: true }).click(); } const loadResponse = await loadResponsePromise; expect(loadResponse.ok()).toBeTruthy(); expect(await loadResponse.json()).toMatchObject({ active: true, synthetic: true, scenario_name: "Northstar Platform", service_count: 9, total: 9, root_cause_service_name: "PostgreSQL", affected_count: 5, }); const demoServicesResponse = await request.get("/api/v1/services"); expect(demoServicesResponse.ok()).toBeTruthy(); const demoServices: ServiceSnapshot[] = await demoServicesResponse.json(); expect(demoServices).toHaveLength(9); const postgres = demoServices.find(({ service }) => service.name === "PostgreSQL"); expect(postgres).toBeDefined(); expect(postgres!.latest_probe?.status).toBe("down"); expect(postgres!.effective_status).toBe("down"); const [incidentsResponse, historyResponse, availabilityResponse] = await Promise.all([ request.get("/api/v1/incidents"), request.get(`/api/v1/services/${postgres!.service.id}/history?limit=50`), request.get(`/api/v1/services/${postgres!.service.id}/availability?hours=24`), ]); expect(incidentsResponse.ok()).toBeTruthy(); const incident = (await incidentsResponse.json()) as { active: boolean; candidates: Array<{ service_name: string; affected_service_ids: string[]; }>; }; expect(incident.active).toBe(true); expect(incident.candidates[0]?.service_name).toBe("PostgreSQL"); expect(incident.candidates[0]?.affected_service_ids).toHaveLength(5); expect(historyResponse.ok()).toBeTruthy(); expect(await historyResponse.json()).toHaveLength(24); expect(availabilityResponse.ok()).toBeTruthy(); expect(await availabilityResponse.json()).toMatchObject({ total_checks: 24, down_checks: 2, uptime_percent: 91.67, }); const demoBanner = page.getByRole("status", { name: "Explore demo scenario" }); await expect(demoBanner).toBeVisible(); await expect(demoBanner).toHaveAttribute("aria-busy", "false", { timeout: 30_000 }); await expect(page.getByText("Demo scenario", { exact: false })).toBeVisible(); await expect(page.getByText("PostgreSQL", { exact: true }).first()).toBeVisible(); await expect(page.getByText("Likely root cause", { exact: true })).toBeVisible(); await expect(page.getByText("Health evidence", { exact: true })).toBeVisible(); const workspaces = page.getByRole("navigation", { name: "ServiceMap workspaces" }); await workspaces.getByRole("button", { name: "Overview" }).click(); await expect(page.getByRole("heading", { name: "9/9 with current health evidence" })).toBeVisible(); await expect(page.getByRole("button", { name: "Review unresolved" })).toBeDisabled(); await page.locator(".overview-lead").getByRole("button", { name: /1 down/i }).click(); await expect(page.getByRole("combobox", { name: "Status", exact: true })).toHaveValue("down"); await workspaces.getByRole("button", { name: "Incidents" }).click(); await expect( page.getByRole("heading", { name: "Active and historical incidents" }), ).toBeVisible(); await page.getByLabel("Search incidents").fill("does not exist"); await expect(page.getByText("No incidents match these filters.")).toBeVisible(); await page.getByLabel("Search incidents").fill(""); await workspaces.getByRole("button", { name: "Topology", exact: true }).click(); await expect(page.getByRole("region", { name: "Topology commands" })).toBeVisible(); await page.getByRole("region", { name: "Topology commands" }).locator("summary").filter({ hasText: "Filters" }).click(); await expect(page.getByRole("combobox", { name: "Filter topology by status" })).toBeVisible(); const inspector = page.getByRole("complementary"); await expect(inspector.getByRole("heading", { name: "PostgreSQL" })).toBeVisible(); await expect(inspector.getByText("Likely root cause", { exact: true })).toBeVisible(); await expect(inspector.getByText("Health evidence", { exact: true })).toBeVisible(); const reset = page.locator("header").getByRole("button", { name: "Reset demo" }); const resetResponsePromise = page.waitForResponse(isDemoMutation("/api/v1/demo/reset")); await reset.click(); const resetResponse = await resetResponsePromise; expect(resetResponse.ok()).toBeTruthy(); expect(await resetResponse.json()).toMatchObject({ active: true, root_cause_service_name: "PostgreSQL", }); // A rendered PostgreSQL node can be left over from before Reset completed. // Wait for the demo region to leave its busy state before attempting Exit. await expect(demoBanner).toHaveAttribute("aria-busy", "false", { timeout: 30_000 }); await expect(page.getByText("PostgreSQL", { exact: true }).first()).toBeVisible(); const exit = page.getByRole("button", { name: "Exit demo", exact: true }); await expect(exit).toBeEnabled(); const exitResponsePromise = page.waitForResponse(isDemoMutation("/api/v1/demo/exit")); await exit.click(); const exitResponse = await exitResponsePromise; expect(exitResponse.ok()).toBeTruthy(); expect(await exitResponse.json()).toMatchObject({ active: false, restored: true }); await expect(demoBanner).toBeHidden({ timeout: 30_000 }); await expect( page.getByText("Demo closed. Your previous topology was restored.", { exact: true }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Explore demo", exact: true }), ).toBeEnabled({ timeout: 30_000 }); const restoredDemoResponse = await request.get("/api/v1/demo"); expect(restoredDemoResponse.ok()).toBeTruthy(); expect(await restoredDemoResponse.json()).toMatchObject({ active: false }); const restoredServicesResponse = await request.get("/api/v1/services"); expect(restoredServicesResponse.ok()).toBeTruthy(); expect(topologyOf(await restoredServicesResponse.json())).toEqual(baselineTopology); });