import { render, screen, waitFor, cleanup } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import AuditPage from "./page"; // Mock next/navigation const mockPush = vi.fn(); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: mockPush }), })); // Default mock values for admin user let mockUser: { id: number; email: string; roles: string[]; permissions: string[] } | null = { id: 1, email: "admin@example.com", roles: ["admin"], permissions: ["view_audit"], }; let mockIsLoading = false; const mockLogout = vi.fn(); const mockHasPermission = vi.fn((permission: string) => mockUser?.permissions.includes(permission) ?? false ); const mockHasRole = vi.fn((role: string) => mockUser?.roles.includes(role) ?? false ); vi.mock("../auth-context", () => ({ useAuth: () => ({ user: mockUser, isLoading: mockIsLoading, logout: mockLogout, hasPermission: mockHasPermission, hasRole: mockHasRole, }), Permission: { VIEW_COUNTER: "view_counter", INCREMENT_COUNTER: "increment_counter", USE_SUM: "use_sum", VIEW_AUDIT: "view_audit", }, })); // Mock fetch const mockFetch = vi.fn(); global.fetch = mockFetch; beforeEach(() => { vi.clearAllMocks(); mockUser = { id: 1, email: "admin@example.com", roles: ["admin"], permissions: ["view_audit"], }; mockIsLoading = false; mockHasPermission.mockImplementation((permission: string) => mockUser?.permissions.includes(permission) ?? false ); mockHasRole.mockImplementation((role: string) => mockUser?.roles.includes(role) ?? false ); // Default: successful empty response mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({ records: [], total: 0, page: 1, per_page: 10, total_pages: 1 }), }); }); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); describe("AuditPage", () => { it("shows loading state", () => { mockIsLoading = true; render(); expect(screen.getByText("Loading...")).toBeTruthy(); }); it("redirects to login when not authenticated", async () => { mockUser = null; render(); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith("/login"); }); }); it("redirects to home when user lacks audit permission", async () => { mockUser = { id: 1, email: "user@example.com", roles: ["regular"], permissions: ["view_counter"], }; mockHasPermission.mockReturnValue(false); render(); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith("/"); }); }); it("displays error message when API fetch fails", async () => { mockFetch.mockRejectedValue(new Error("Network error")); render(); await waitFor(() => { // Both tables should show errors since both calls fail const errors = screen.getAllByText("Network error"); expect(errors.length).toBeGreaterThan(0); }); }); it("displays error when API returns non-ok response", async () => { mockFetch.mockResolvedValue({ ok: false, status: 500, json: () => Promise.resolve({ detail: "Internal server error" }), }); render(); await waitFor(() => { const errors = screen.getAllByText("Request failed: 500"); expect(errors.length).toBeGreaterThan(0); }); }); it("displays records when fetch succeeds", async () => { const counterResponse = { records: [ { id: 1, user_email: "recorduser@example.com", value_before: 0, value_after: 1, created_at: "2024-01-01T00:00:00Z", }, ], total: 1, page: 1, per_page: 10, total_pages: 1, }; const sumResponse = { records: [], total: 0, page: 1, per_page: 10, total_pages: 1, }; mockFetch .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(counterResponse), }) .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(sumResponse), }); render(); await waitFor(() => { expect(screen.getByText("recorduser@example.com")).toBeTruthy(); }); }); it("shows table headers", async () => { render(); await waitFor(() => { // Check for counter table headers expect(screen.getByText("Counter Activity")).toBeTruthy(); expect(screen.getByText("Sum Activity")).toBeTruthy(); }); }); });