review
This commit is contained in:
parent
b173b47925
commit
66bc4c5a45
10 changed files with 367 additions and 320 deletions
172
frontend/app/audit/page.test.tsx
Normal file
172
frontend/app/audit/page.test.tsx
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue