/** * useContentData Hook 单元测试 */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { renderHook, waitFor } from "@testing-library/react"; import { SWRConfig } from "swr"; import type { ReactNode } from "react"; vi.mock("@/lib/api/client", () => ({ fetchWithAuth: vi.fn(), })); import { fetchWithAuth } from "@/lib/api/client"; const mockFetchWithAuth = vi.mocked(fetchWithAuth); import { useContentData } from "@/lib/hooks/use-content-data"; import type { Content } from "@/lib/api/contents"; import type { KnowledgeBase } from "@/lib/api/knowledge"; const noRetryOptions = { shouldRetryOnError: false }; function createWrapper() { return ({ children }: { children: ReactNode }) => SWRConfig({ value: { provider: () => new Map() }, children }); } describe("useContentData", () => { beforeEach(() => { vi.clearAllMocks(); }); const mockContents: Content[] = [ { id: "content-1", title: "测试内容", body: "内容正文", content_type: "article", status: "draft", author_id: "user-1", tags: ["test"], created_at: "2024-01-01", updated_at: "2024-01-01", }, ]; const mockKnowledgeBases: KnowledgeBase[] = [ { id: "kb-1", name: "测试知识库", type: "enterprise", document_count: 5, status: "ready", created_at: "2024-01-01", }, ]; it("应同时获取内容列表和知识库列表", async () => { mockFetchWithAuth.mockImplementation((url: string) => { if (url.includes("/api/v1/contents/")) return Promise.resolve(mockContents); if (url.includes("/api/v1/knowledge/bases")) return Promise.resolve(mockKnowledgeBases); return Promise.resolve([]); }); const { result } = renderHook( () => useContentData({ swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); expect(result.current.contents).toEqual(mockContents); expect(result.current.knowledgeBases).toEqual(mockKnowledgeBases); }); it("应正确暴露 isLoading 状态", async () => { mockFetchWithAuth.mockImplementation((url: string) => { if (url.includes("/api/v1/contents/")) return Promise.resolve(mockContents); if (url.includes("/api/v1/knowledge/bases")) return Promise.resolve(mockKnowledgeBases); return Promise.resolve([]); }); const { result } = renderHook( () => useContentData({ swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); }); it("应正确暴露 error 状态", async () => { mockFetchWithAuth.mockRejectedValue(new Error("数据加载失败")); const { result } = renderHook( () => useContentData({ swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.error).toBeDefined(); expect(result.current.error?.message).toBe("数据加载失败"); }); }); it("应提供 refresh 方法", async () => { mockFetchWithAuth.mockImplementation((url: string) => { if (url.includes("/api/v1/contents/")) return Promise.resolve(mockContents); if (url.includes("/api/v1/knowledge/bases")) return Promise.resolve(mockKnowledgeBases); return Promise.resolve([]); }); const { result } = renderHook( () => useContentData({ swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); expect(typeof result.current.refreshContents).toBe("function"); expect(typeof result.current.refreshKnowledgeBases).toBe("function"); }); it("数据加载失败时 error 应包含错误信息", async () => { mockFetchWithAuth.mockRejectedValue(new Error("网络异常")); const { result } = renderHook( () => useContentData({ swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.error?.message).toBe("网络异常"); }); }); });