import { expect, test } from "@playwright/test"; test.describe("Audit Logs Management", () => { test.beforeEach(async ({ page }) => { await page.addInitScript(() => { window.localStorage.setItem("locale", "ko"); window.localStorage.setItem("admin_session", "fake-token"); ( window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean } )._IS_TEST_MODE = true; const authData = { access_token: "fake-token", token_type: "Bearer", profile: { sub: "admin-user", name: "Admin", role: "super_admin" }, expires_at: Math.floor(Date.now() / 1000) + 36000, }; window.localStorage.setItem( "oidc.user:http://localhost:5000/oidc:adminfront", JSON.stringify(authData), ); }); await page.route("**/api/v1/user/me", async (route) => { return route.fulfill({ json: { id: "admin-user", name: "Admin", role: "super_admin", manageableTenants: [], }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.route("**/oidc/**", async (route) => { await route.fulfill({ json: { issuer: "http://localhost:5000/oidc" } }); }); }); const generateMockLogs = (count: number, offset = 0) => { return Array.from({ length: count }, (_, i) => { const id = offset + i; return { event_id: `evt-${id}`, timestamp: new Date(Date.now() - id * 10000).toISOString(), user_id: id % 2 === 0 ? "user-even" : "user-odd", event_type: "API_REQUEST", status: id % 5 === 0 ? "failure" : "success", ip_address: "192.168.1.1", user_agent: "Playwright", details: JSON.stringify({ action: id % 3 === 0 ? "CREATE_TENANT" : "ROTATE_SECRET", method: "POST", path: `/v1/admin/tenants`, }), }; }); }; test("should load initial logs and display correctly", async ({ page }) => { await page.route("**/api/v1/audit*", async (route) => { return route.fulfill({ json: { items: generateMockLogs(20, 0), total: 20 }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.goto("/audit-logs"); // Check header await expect(page.getByText(/감사 로그|Audit Logs/i).first()).toBeVisible(); // Check if table rows are rendered await expect(page.locator("tbody tr")).toHaveCount(20); // Check specific data visible in the row await expect(page.locator("tbody")).toContainText("user-even"); await expect(page.locator("tbody")).toContainText("CREATE_TENANT"); }); test("should load more logs on scroll (infinite scroll)", async ({ page, }) => { let callCount = 0; await page.route("**/api/v1/audit*", async (route) => { const logs = generateMockLogs(20, callCount * 20); callCount++; return route.fulfill({ json: { items: logs, next_cursor: "fake-cursor" }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.goto("/audit-logs"); await expect(page.locator("tbody tr")).toHaveCount(20); const loadMoreBtn = page.getByRole("button", { name: /더 보기|Load more/i, }); await expect(loadMoreBtn).toBeVisible(); await loadMoreBtn.click(); // Wait for the next page to load await expect(page.locator("tbody tr")).toHaveCount(40); // user-even and CREATE_TENANT should still be visible in the newly loaded rows await expect(page.locator("tbody")).toContainText("user-even"); }); test("should filter logs by Action and User ID locally", async ({ page }) => { await page.route("**/api/v1/audit*", async (route) => { return route.fulfill({ json: { items: generateMockLogs(20, 0), total: 20 }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.goto("/audit-logs"); await expect(page.locator("tbody tr")).toHaveCount(20); // The filtering in AuditLogsPage is done locally using useDeferredValue // Search by User ID const userIdInput = page.getByTestId("audit-search-user-id"); await userIdInput.fill("user-even"); // Wait for deferred value to apply await page.waitForTimeout(500); // Half of 20 logs are user-even await expect(page.locator("tbody tr")).toHaveCount(10); await expect(page.locator("tbody")).not.toContainText("user-odd"); // Clear User ID await userIdInput.clear(); await page.waitForTimeout(500); await expect(page.locator("tbody tr")).toHaveCount(20); // Search by Action const actionInput = page.getByTestId("audit-search-action"); await actionInput.fill("ROTATE_SECRET"); await page.waitForTimeout(500); // Check that we only see ROTATE_SECRET (20 - 7 = 13) await expect(page.locator("tbody tr")).toHaveCount(13); await expect(page.locator("tbody")).not.toContainText("CREATE_TENANT"); }); test("should filter logs by Status locally", async ({ page }) => { await page.route("**/api/v1/audit*", async (route) => { return route.fulfill({ json: { items: generateMockLogs(20, 0), total: 20 }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.goto("/audit-logs"); await expect(page.locator("tbody tr")).toHaveCount(20); // Select "Failure" status await page.getByTestId("audit-filter-status").selectOption("failure"); // ID % 5 === 0 are status "failure" // IDs: 0, 5, 10, 15 => 4 items await expect(page.locator("tbody tr")).toHaveCount(4); // Select "Success" status await page.getByTestId("audit-filter-status").selectOption("success"); // IDs: 1,2,3,4, 6,7,8,9, 11,12,13,14, 16,17,18,19 => 16 items await expect(page.locator("tbody tr")).toHaveCount(16); }); });