feat(audit): expose structured before/after evidence
audit_events already stored before_json/after_json, but the API and UI only ever surfaced metadata -- the audit trail could say something happened but never show what changed. Add before/after to AuditEventOut, resolve a safe entity_ref/entity_link for vehicle/booking/data-quality-issue entities (customer stays label-only; no customer detail route exists in this PoC), and render a human-readable change summary in the UI with the raw before/after/metadata JSON kept behind a <details> disclosure rather than shown by default.
This commit is contained in:
@@ -113,6 +113,24 @@ test("bookings page: pagination renders at most 25 rows and page 2 differs from
|
||||
await expect(prevButton).toBeEnabled();
|
||||
});
|
||||
|
||||
test("return preview correctly reports blocked (not maintenance) for damage reported", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
await resetDemoData(request);
|
||||
await page.goto("/bookings/BK-DEMO-RETURN");
|
||||
await page.getByLabel("End odometer (km)").fill("55000");
|
||||
await page.getByLabel("Fuel level (%)").fill("40");
|
||||
await page.getByRole("checkbox", { name: "Damage reported" }).check();
|
||||
await page.getByRole("button", { name: "Review return" }).click();
|
||||
|
||||
// The preview is the server's authoritative evaluation: damage always routes to
|
||||
// "blocked", never "maintenance" -- this used to be guessed client-side and wrong.
|
||||
await expect(page.getByText("Damage was reported on return.")).toBeVisible();
|
||||
const statusRegion = page.locator(".impact-preview");
|
||||
await expect(statusRegion.getByText("blocked", { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test("data quality page: status and rule-type filters work", async ({ page }) => {
|
||||
await page.goto("/data-quality");
|
||||
await expect(page.locator(".data-table")).toBeVisible();
|
||||
@@ -174,6 +192,38 @@ test("audit page: action filter works", async ({ page }) => {
|
||||
expect(actions.every((a) => a.includes("demo login"))).toBeTruthy();
|
||||
});
|
||||
|
||||
test("audit page: shows human-readable before/after and a safe entity link", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
await resetDemoData(request);
|
||||
// demo/reset deletes the acting session's own cookie, so submit the return through
|
||||
// page.request instead -- it shares the browser context's still-valid OM session from
|
||||
// beforeEach rather than the now-logged-out standalone `request` fixture.
|
||||
const submitted = await page.request.post("/api/v1/bookings/BK-DEMO-RETURN/return", {
|
||||
data: {
|
||||
end_odometer_km: 60000,
|
||||
fuel_level_percent: 55,
|
||||
cleanliness_ok: true,
|
||||
damage_reported: false,
|
||||
technical_warning: false,
|
||||
},
|
||||
headers: { "Idempotency-Key": "e2e-audit-before-after-check" },
|
||||
});
|
||||
expect(submitted.ok()).toBeTruthy();
|
||||
|
||||
await page.goto("/audit");
|
||||
await page.getByLabel("Action").fill("return_registered");
|
||||
await expect(page.locator(".data-table tbody tr").first()).toBeVisible();
|
||||
|
||||
const changeCell = page.locator(".data-table tbody tr").first().locator("td").nth(4);
|
||||
await expect(changeCell).toContainText("status");
|
||||
await expect(changeCell).toContainText("returned");
|
||||
|
||||
const entityCell = page.locator(".data-table tbody tr").first().locator("td").nth(3);
|
||||
await expect(entityCell.locator("a")).toHaveAttribute("href", /\/bookings\/BK-/);
|
||||
});
|
||||
|
||||
test("knowledge page: form submits and clears input", async ({ page }) => {
|
||||
await page.goto("/knowledge");
|
||||
const input = page.getByPlaceholder(/What must I do when a vehicle returns with damage/);
|
||||
|
||||
Reference in New Issue
Block a user