import { expect, loginAs, test } from "./fixtures"; test.describe("role-based authorization, driven end to end from the browser", () => { test("a viewer never sees the client-create form; an admin does", async ({ page }) => { await loginAs(page, "hub-viewer"); await page.goto("/clients"); await expect(page.getByRole("button", { name: "Create pending client" })).toHaveCount(0); await expect(page.getByText("Client creation requires the admin role.")).toBeVisible(); await loginAs(page, "hub-admin"); await page.goto("/clients"); await expect(page.getByRole("button", { name: "Create pending client" })).toBeVisible(); }); test("client.create is still server-enforced even if the hidden form were bypassed", async ({ page, request }) => { // Proves the security property independent of the UI: the hidden // button is a usability affordance, not the authorization boundary. await loginAs(page, "hub-viewer"); const cookies = await page.context().cookies(); const sessionCookie = cookies.find((cookie) => cookie.name.toLowerCase().includes("session")); if (!sessionCookie) throw new Error("expected a session cookie to be set after login"); const sessionResponse = await request.get("/auth/session", { headers: { Cookie: `${sessionCookie.name}=${sessionCookie.value}` } }); const { csrf_token: csrfToken } = await sessionResponse.json(); const response = await request.post("/api/client-management", { headers: { Cookie: `${sessionCookie.name}=${sessionCookie.value}`, "X-CSRF-Token": csrfToken }, data: { name: "e2e-bypass-attempt", project: "ludarium", environment: "prod", kind: "native-2026" }, }); expect(response.status()).toBe(403); }); test("an auditor can read the audit log; a viewer never sees the Audit nav link at all", async ({ page }) => { await loginAs(page, "hub-viewer"); await page.goto("/"); await expect(page.getByRole("link", { name: "Audit" })).toHaveCount(0); await loginAs(page, "hub-auditor"); await page.goto("/"); await expect(page.getByRole("link", { name: "Audit" })).toBeVisible(); await page.goto("/audit"); await expect(page.getByRole("alert")).toHaveCount(0); await expect(page.getByText(/total ยท server paginated/)).toBeVisible(); }); test("a viewer cannot select the audit section of a project workspace", async ({ page }) => { await loginAs(page, "hub-viewer"); await page.goto("/projects/ludarium?tab=Audit"); await expect(page.getByRole("link", { name: "Audit" })).toHaveCount(0); await expect(page.getByRole("link", { name: "Summary" })).toHaveAttribute("aria-current", "page"); }); test("an operator sees state-appropriate client actions; a viewer does not", async ({ page }) => { await loginAs(page, "hub-admin"); await page.goto("/clients"); await page.getByLabel("Name").fill("e2e-operator-target"); await page.getByRole("button", { name: "Create pending client" }).click(); await expect(page.getByText(/Client created: e2e-operator-target/)).toBeVisible(); await loginAs(page, "hub-viewer"); await page.goto("/clients"); const viewerRow = page.getByRole("row", { name: /e2e-operator-target/ }); await expect(viewerRow).toBeVisible(); await expect(viewerRow.getByRole("button", { name: "Disable" })).toHaveCount(0); await loginAs(page, "hub-operator"); await page.goto("/clients"); const operatorRow = page.getByRole("row", { name: /e2e-operator-target/ }); await expect(operatorRow.getByRole("button", { name: "Disable" })).toBeDisabled(); await operatorRow.getByRole("button", { name: "Revoke" }).click(); // The confirmation modal's title is dynamic ("Revoke e2e-operator-target?"), // which makes an exact accessible-name match on the confirm button awkward // to pin reliably against the row-scoped locator above. This is the one // deliberate exception to this file's role/text-based query convention, // justified by the dynamic title: DataTable.tsx's modal confirm button // carries data-testid="confirm-action" specifically for this case. await page.getByTestId("confirm-action").click(); await expect(page.getByText(/completed\./)).toBeVisible(); }); test("tool readiness is hidden below operator and visible to an operator", async ({ page }) => { await loginAs(page, "hub-viewer"); await page.goto("/tools"); await expect(page.getByRole("button", { name: "Readiness" })).toHaveCount(0); await loginAs(page, "hub-operator"); await page.goto("/tools"); await expect(page.getByRole("button", { name: "Readiness" }).first()).toBeVisible(); }); });