import { expect, test } from "@playwright/test"; import { type AuditLog, type Consent, installDevApiMock, makeClient, seedAuth, } from "./helpers/devfront-fixtures"; import { captureEvidence } from "./helpers/evidence"; const appNamePlaceholder = /My Awesome Application|예: 멋진 애플리케이션/i; test.describe("DevFront role report", () => { test.beforeEach(async ({ page }) => { page.on("dialog", async (dialog) => { await dialog.accept(); }); }); test("user(tenant_member) is blocked with 안내 문구", async ({ page, }, testInfo) => { await seedAuth(page, "user"); await installDevApiMock(page, { clients: [], consents: [] as Consent[], auditLogs: [] as AuditLog[], }); await page.goto("/clients"); await expect( page.getByText(/관리자 전용 화면|administrator only/i), ).toBeVisible(); await captureEvidence(page, testInfo, "role-user-blocked"); }); test("rp_admin sees only assigned Gitea app and its logs", async ({ page, }, testInfo) => { await seedAuth(page, "rp_admin"); const state = { clients: [makeClient("gitea-client", { name: "Gitea" })], consents: [] as Consent[], auditLogs: [ { event_id: "evt-rp-1", timestamp: "2026-03-04T01:00:00.000Z", user_id: "rp-admin-user", event_type: "CLIENT_UPDATE", status: "success" as const, ip_address: "127.0.0.1", user_agent: "playwright", details: JSON.stringify({ action: "UPDATE_CLIENT", target_id: "gitea-client", tenant_id: "tenant-a", }), }, ] as AuditLog[], }; await installDevApiMock(page, state); await page.goto("/clients"); await expect(page.getByRole("link", { name: /Gitea/ })).toBeVisible(); await expect(page.getByText("gitea-client")).toBeVisible(); await captureEvidence(page, testInfo, "role-rp-admin-clients"); await page.goto("/audit-logs"); await expect(page.getByText("UPDATE_CLIENT")).toBeVisible(); await expect(page.getByText("gitea-client")).toBeVisible(); await captureEvidence(page, testInfo, "role-rp-admin-audit"); }); test("tenant_admin can manage tenant apps and see tenant logs", async ({ page, }, testInfo) => { await seedAuth(page, "tenant_admin"); const state = { clients: [ makeClient("tenant-a-app-1", { name: "Tenant A CRM" }), makeClient("tenant-a-app-2", { name: "Tenant A ERP" }), ], consents: [] as Consent[], auditLogs: [] as AuditLog[], auditLogsByCursor: undefined, }; await installDevApiMock(page, state); await page.goto("/clients"); await expect(page.getByText("Tenant A CRM")).toBeVisible(); await expect(page.getByText("Tenant A ERP")).toBeVisible(); await captureEvidence(page, testInfo, "role-tenant-admin-clients"); await page.goto("/clients/tenant-a-app-1/settings"); await page .getByPlaceholder(appNamePlaceholder) .fill("Tenant A CRM Updated"); const updatePromise = page.waitForResponse( (r) => r.url().includes("/api/v1/dev/clients") && r.request().method() === "PUT", ); await page.getByRole("button", { name: /^저장$|^Save$/i }).click(); await updatePromise; await page.goto("/audit-logs"); await expect(page.getByText("UPDATE_CLIENT")).toBeVisible({ timeout: 30000, }); await expect(page.getByText("tenant-a-app-1")).toBeVisible(); await captureEvidence(page, testInfo, "role-tenant-admin-audit"); }); test("super_admin sees all and can generate log entries", async ({ page, }, testInfo) => { await seedAuth(page, "super_admin"); const state = { clients: [ makeClient("tenant-a-app", { name: "Tenant A App" }), makeClient("tenant-b-app", { name: "Tenant B App" }), ], consents: [] as Consent[], auditLogs: [] as AuditLog[], auditLogsByCursor: undefined, }; await installDevApiMock(page, state); await page.goto("/clients"); await expect(page.getByText("Tenant A App")).toBeVisible(); await expect(page.getByText("Tenant B App")).toBeVisible(); await captureEvidence(page, testInfo, "role-super-admin-clients"); await page.goto("/clients/new"); await page .getByPlaceholder(appNamePlaceholder) .fill("Super Admin Created App"); await page .getByPlaceholder(/https:\/\/app\.example\.com\/callback/i) .fill("https://super-admin.example.com/callback"); const createPromise = page.waitForResponse( (r) => r.url().includes("/api/v1/dev/clients") && r.request().method() === "POST", ); await page.getByRole("button", { name: /앱 생성|Create/i }).click(); await createPromise; await expect(page).toHaveURL(/\/clients\/client-\d+\/settings$/); await expect .poll(() => state.auditLogs.some((item) => { try { return JSON.parse(item.details)?.action === "CREATE_CLIENT"; } catch { return false; } }), ) .toBe(true); await page.goto("/audit-logs"); await expect(page.getByText("CREATE_CLIENT")).toBeVisible({ timeout: 30000, }); await captureEvidence(page, testInfo, "role-super-admin-audit"); }); });