addition of new dashboard

This commit is contained in:
2026-08-20 15:08:32 +00:00
parent 0622018d95
commit bf59249ed7
161 changed files with 13170 additions and 119 deletions

View File

@@ -0,0 +1,131 @@
import { test, expect } from "@playwright/test";
// API responses mocked in the SAME envelope shapes the real backend returns
// (verified against /api/v1/data/dashboard/*). The smoke asserts each panel
// consumes the contract and renders DATA — and that interactions (verify) work —
// not just that containers exist.
const EVENT_ID = "f6a042ca-efbf-4c4d-b99c-dac41373eb63";
const MOCKS = {
"/api/dashboard/whoami": { username: "dev", modules: ["core", "archive"] },
"/api/dashboard/summary": {
apiUsage: { core: 2005, audit: 2002, total: 4007 },
apiKeys: 2,
endpoints: 2,
storage: {
onDisk: { core: 353712, audit: 1321330, total: 1675042 },
logical: { core: 349712, audit: 1311320, total: 1661032 },
},
},
"/api/dashboard/usage-series": {
series: [
{ date: "2026-06-20", core: 76, audit: 0, total: 76 },
{ date: "2026-06-21", core: 64, audit: 0, total: 64 },
],
},
"/api/dashboard/storage-series": {
series: [
{ date: "2026-06-20", coreDisk: 322617, auditDisk: 0, totalDisk: 322617 },
{ date: "2026-06-21", coreDisk: 333904, auditDisk: 0, totalDisk: 333904 },
],
},
"/api/dashboard/keys": [],
};
const coreRow = {
eventUuid: EVENT_ID,
sender: "did:jlinc:sender",
receiver: "did:jlinc:receiver",
date: "2026-06-23T11:37:40.664Z",
agreementUuid: "9ab250b9-b3e3-48aa-1111-222233334444",
size: 655,
};
// Two audit records for ONE event (produce + process): same eventUuid, distinct
// auditId. On the audit tab it is auditId — not eventUuid — that identifies a row.
const auditRows = [
{ ...coreRow, auditId: 9, auditDigest: "572b052a11aa" },
{ ...coreRow, auditId: 10, auditDigest: "4029d4a6da22" },
];
test.beforeEach(async ({ page }) => {
await page.route("**/api/dashboard/**", async (route) => {
const url = new URL(route.request().url());
const path = url.pathname;
if (path === "/api/dashboard/transactions") {
const audit = url.searchParams.get("type") === "audit";
return route.fulfill({
json: audit
? { rows: auditRows, total: 2, limit: 50, offset: 0 }
: { rows: [coreRow], total: 1, limit: 50, offset: 0 },
});
}
if (path === "/api/dashboard/verify") {
const list = (k) => (url.searchParams.get(k) || "").split(",").filter(Boolean);
const pass = (extra) => ({ status: "verified", verified: true, signatureCount: 1, auditCount: 1, checks: { validId: true }, ...extra });
return route.fulfill({
json: {
results: [
...list("ids").map((eventUuid) => pass({ eventUuid, auditId: null })),
// Audit 9 passes, audit 10 fails. Two rows of the SAME event getting
// different verdicts is only expressible if each row is its own
// verification target.
...list("auditIds").map(Number).map((auditId) =>
auditId === 9
? pass({ eventUuid: EVENT_ID, auditId })
: { eventUuid: EVENT_ID, auditId, status: "invalid", verified: false, signatureCount: 1, auditCount: 1, checks: { validAuditHash: false } }),
],
},
});
}
if (Object.prototype.hasOwnProperty.call(MOCKS, path)) return route.fulfill({ json: MOCKS[path] });
return route.fulfill({ json: {} });
});
});
test("dashboard panels render real data from the API contract", async ({ page }) => {
await page.goto("/");
await expect(page.locator("#api-usage-total")).toHaveText(/\d/, { timeout: 10_000 });
await expect(page.locator("#storage-total")).toHaveText(/\d/);
await expect(page.locator("#usage-chart svg")).toBeAttached({ timeout: 10_000 });
await expect(page.locator("#transaction-rows tr[data-event-id]")).toHaveCount(1);
});
test("Audit tab: verifying one row badges ONLY that row", async ({ page }) => {
await page.goto("/");
await page.locator("#tab-audit").click();
// Two audit records share one event UUID; each is its own row.
const rows = page.locator("#transaction-rows tr[data-event-id]");
await expect(rows).toHaveCount(2);
// The bug: clicking Verify on one row badged BOTH, because rows were keyed by
// the event UUID they share. The request must target the row's audit id.
const [request] = await Promise.all([
page.waitForRequest((r) => r.url().includes("/api/dashboard/verify")),
rows.nth(0).locator(".btn-verify").click(),
]);
expect(new URL(request.url()).searchParams.get("auditIds")).toBe("9");
await expect(rows.nth(0).locator(".verify-badge")).toContainText("Verified");
await expect(rows.nth(1).locator(".verify-badge")).toBeHidden();
});
test("Audit tab: Validate All gives each audit row its own verdict", async ({ page }) => {
await page.goto("/");
await page.locator("#tab-audit").click();
await expect(page.locator("#transaction-rows tr[data-event-id]")).toHaveCount(2);
await page.locator("#validate-all").click();
// Same event, different audit records -> independent results. A per-event
// verification could not produce two different badges here.
const rows = page.locator("#transaction-rows tr[data-event-id]");
await expect(rows.nth(0).locator(".verify-badge")).toContainText("Verified");
await expect(rows.nth(1).locator(".verify-badge")).toContainText("Invalid");
});
test("sidebar switches to the API Keys view", async ({ page }) => {
await page.goto("/");
await expect(page.locator("#api-keys-view")).toBeAttached();
await page.locator("#nav-api-keys").click();
await expect(page.locator("#api-keys-view")).toBeVisible();
});