/** * useSuggestionsData 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); vi.mock("next-auth/react", () => ({ useSession: vi.fn(() => ({ data: { accessToken: "test-token" }, status: "authenticated", })), })); import { useSuggestionsData } from "@/lib/hooks/use-suggestions-data"; import type { SuggestionListResponse } from "@/types/suggestion"; const noRetryOptions = { shouldRetryOnError: false }; function createWrapper() { return ({ children }: { children: ReactNode }) => SWRConfig({ value: { provider: () => new Map() }, children }); } describe("useSuggestionsData", () => { beforeEach(() => { vi.clearAllMocks(); }); const mockSuggestionsResponse: SuggestionListResponse = { suggestions: [ { id: "sug-1", brand_id: "brand-1", type: "content_optimization", priority: "high", title: "优化内容", description: "建议优化内容", action: null, expected_impact: null, difficulty: "easy", status: "pending", generated_at: "2024-01-01", updated_at: "2024-01-01", batch_id: "batch-1", source: "rule", }, ], total: 1, }; it("brandId 为空时应暂停建议请求", () => { mockFetchWithAuth.mockResolvedValue({}); const { result } = renderHook(() => useSuggestionsData(), { wrapper: createWrapper() }); expect(result.current.suggestions).toBeUndefined(); }); it("brandId 存在时应获取建议列表", async () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.suggestions).toEqual(mockSuggestionsResponse.suggestions); }); }); it("应支持筛选参数", async () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", filters: { type: "content_optimization", priority: "high", status: "pending" }, swrOptions: noRetryOptions, }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.suggestions).toEqual(mockSuggestionsResponse.suggestions); }); const suggestionCall = mockFetchWithAuth.mock.calls.find( (call) => typeof call[0] === "string" && call[0].includes("/suggestions") ); expect(suggestionCall).toBeDefined(); const calledUrl = suggestionCall![0] as string; expect(calledUrl).toContain("type=content_optimization"); expect(calledUrl).toContain("priority=high"); expect(calledUrl).toContain("status=pending"); }); it("应正确暴露 isLoading 状态", async () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); }); it("应正确暴露 error 状态", async () => { mockFetchWithAuth.mockRejectedValue(new Error("加载建议失败")); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); await waitFor(() => { expect(result.current.error).toBeDefined(); expect(result.current.error?.message).toBe("加载建议失败"); }); }); it("应提供 regenerate 方法", () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); expect(typeof result.current.regenerate).toBe("function"); }); it("应提供 updateStatus 方法", () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); expect(typeof result.current.updateStatus).toBe("function"); }); it("应提供 refresh 方法", () => { mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse); const { result } = renderHook( () => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }), { wrapper: createWrapper() } ); expect(typeof result.current.refresh).toBe("function"); }); });