import { render, screen, waitFor } from "@testing-library/react"; import type { ReactNode } from "react"; import { useAccessStore } from "@/store/accessStore"; import { useAuthStore } from "@/store/authStore"; import { App } from "./RefineContext"; let mockSessionState: { data: { accessToken: string; user: { id: string; name: string }; } | null; status: "authenticated" | "unauthenticated"; } = { data: { accessToken: "expired-access-token", user: { id: "user-1", name: "Test User" }, }, status: "authenticated", }; jest.mock("next-auth/react", () => ({ SessionProvider: ({ children }: { children: ReactNode }) => children, signIn: jest.fn().mockResolvedValue(undefined), useSession: () => mockSessionState, })); jest.mock("next/navigation", () => ({ usePathname: () => "/network-simulation", })); jest.mock("@refinedev/core", () => ({ Refine: ({ children }: { children: ReactNode }) => children, })); jest.mock("@refinedev/kbar", () => ({ RefineKbar: () => null, RefineKbarProvider: ({ children }: { children: ReactNode }) => children, })); jest.mock("@refinedev/mui", () => ({ RefineSnackbarProvider: ({ children }: { children: ReactNode }) => children, })); jest.mock("@refinedev/nextjs-router", () => ({})); jest.mock("@providers/data-provider", () => ({ dataProvider: {} })); jest.mock("@/providers/notification-provider/useAppNotificationProvider", () => ({ useAppNotificationProvider: {}, })); jest.mock("@contexts/color-mode", () => ({ ColorModeContextProvider: ({ children }: { children: ReactNode }) => children, })); jest.mock("@/contexts/ProjectContext", () => ({ ProjectProvider: ({ children }: { children: ReactNode }) => children, })); jest.mock("@/lib/authToken", () => ({ getAccessToken: jest.fn().mockResolvedValue("expired-access-token"), })); describe("RefineContext access authentication", () => { const originalFetch = global.fetch; const originalRequest = global.Request; beforeEach(() => { mockSessionState = { data: { accessToken: "expired-access-token", user: { id: "user-1", name: "Test User" }, }, status: "authenticated", }; useAuthStore.setState({ accessToken: null, sessionExpired: false, sessionExpiryReason: null, }); useAccessStore.setState({ context: null, permissions: [], loading: true, }); global.Request = class TestRequest {} as unknown as typeof Request; global.fetch = jest.fn().mockResolvedValue({ ok: false, status: 401, headers: new Headers(), } as Response); }); afterEach(() => { jest.restoreAllMocks(); global.fetch = originalFetch; global.Request = originalRequest; }); it("marks the session expired when access-context rejects an expired token", async () => { render(
应用内容
, ); await waitFor(() => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining("/api/v1/access-context"), expect.not.objectContaining({ skipAuthRedirect: true }), ); }); await waitFor(() => { expect(useAuthStore.getState()).toMatchObject({ sessionExpired: true, sessionExpiryReason: "unauthorized", }); }); }); it("shows an authorization message when access-context rejects the account", async () => { global.fetch = jest.fn().mockResolvedValue({ ok: false, status: 403, headers: new Headers(), json: async () => ({ detail: "User authorization pending" }), } as Response); render(
应用内容
, ); expect( await screen.findByText("账号尚未获得访问权限"), ).toBeInTheDocument(); expect( screen.getByRole("button", { name: "退出登录" }), ).toBeInTheDocument(); expect(screen.getByText("Test User")).toBeInTheDocument(); expect(screen.queryByText("应用内容")).not.toBeInTheDocument(); expect(useAuthStore.getState()).toMatchObject({ sessionExpired: false, sessionExpiryReason: null, }); }); it("keeps project recovery available when the selected project is denied", async () => { global.fetch = jest.fn().mockResolvedValue({ ok: false, status: 403, headers: new Headers(), json: async () => ({ detail: "No access to project" }), } as Response); render(
应用内容
, ); expect(await screen.findByText("无权访问当前项目")).toBeInTheDocument(); expect( screen.getByRole("button", { name: "切换项目" }), ).toBeInTheDocument(); expect(screen.queryByText("应用内容")).not.toBeInTheDocument(); }); it("prioritizes an unauthenticated session over route permissions", async () => { mockSessionState = { data: null, status: "unauthenticated" }; render(
应用内容
, ); expect(screen.getByText("登录状态已失效")).toBeInTheDocument(); expect(screen.queryByText("无权访问此功能")).not.toBeInTheDocument(); await waitFor(() => { expect(useAuthStore.getState()).toMatchObject({ sessionExpired: true, sessionExpiryReason: "unauthorized", }); }); }); });