172 lines
4.2 KiB
TypeScript
172 lines
4.2 KiB
TypeScript
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
|
|
);
|
|
|
|
vi.mock("../auth-context", () => ({
|
|
useAuth: () => ({
|
|
user: mockUser,
|
|
isLoading: mockIsLoading,
|
|
logout: mockLogout,
|
|
hasPermission: mockHasPermission,
|
|
}),
|
|
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
|
|
);
|
|
// 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(<AuditPage />);
|
|
expect(screen.getByText("Loading...")).toBeTruthy();
|
|
});
|
|
|
|
it("redirects to login when not authenticated", async () => {
|
|
mockUser = null;
|
|
render(<AuditPage />);
|
|
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(<AuditPage />);
|
|
await waitFor(() => {
|
|
expect(mockPush).toHaveBeenCalledWith("/");
|
|
});
|
|
});
|
|
|
|
it("displays error message when API fetch fails", async () => {
|
|
mockFetch.mockRejectedValue(new Error("Network error"));
|
|
render(<AuditPage />);
|
|
|
|
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(<AuditPage />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Failed to load counter records")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
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(<AuditPage />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("recorduser@example.com")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
it("shows table headers", async () => {
|
|
render(<AuditPage />);
|
|
|
|
await waitFor(() => {
|
|
// Check for counter table headers
|
|
expect(screen.getByText("Counter Activity")).toBeTruthy();
|
|
expect(screen.getByText("Sum Activity")).toBeTruthy();
|
|
});
|
|
});
|
|
});
|