/** * Agent监控Dashboard 单元测试 * * 覆盖:执行记录列表、状态筛选、统计摘要、执行详情 */ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { agentsApi, type AgentTask, type TaskLog, type ExecutionStats } from "@/lib/api/agents"; // ── Mock next-auth/react ────────────────────────────────────────────────────── vi.mock("next-auth/react", () => ({ getSession: vi.fn(() => Promise.resolve({ accessToken: "test-token-123" }) ), })); // ── Mock global fetch ───────────────────────────────────────────────────────── const mockFetch = vi.fn(); const originalFetch = global.fetch; beforeEach(() => { global.fetch = mockFetch; vi.clearAllMocks(); }); afterEach(() => { global.fetch = originalFetch; }); // ── 辅助 ────────────────────────────────────────────────────────────────────── function mockFetchResponse(data: T, ok = true, status = 200) { return { ok, status, json: () => Promise.resolve(data), }; } // ── 模拟数据 ───────────────────────────────────────────────────────────────── const mockTasks: AgentTask[] = [ { id: "task-1", agent_id: "agent-1", task_type: "geo_optimizer", status: "completed", priority: 1, input_data: { query: "test" }, output_data: { result: "optimized" }, error_message: null, started_at: "2024-01-15T10:00:00Z", completed_at: "2024-01-15T10:00:12Z", created_at: "2024-01-15T09:59:50Z", }, { id: "task-2", agent_id: "agent-2", task_type: "citation_detector", status: "completed", priority: 0, input_data: { url: "https://example.com" }, output_data: { citations: [] }, error_message: null, started_at: "2024-01-15T11:00:00Z", completed_at: "2024-01-15T11:00:08Z", created_at: "2024-01-15T10:59:55Z", }, { id: "task-3", agent_id: "agent-3", task_type: "content_gen", status: "running", priority: 2, input_data: { topic: "AI trends" }, output_data: null, error_message: null, started_at: "2024-01-15T12:00:00Z", completed_at: null, created_at: "2024-01-15T11:59:50Z", }, { id: "task-4", agent_id: "agent-1", task_type: "geo_optimizer", status: "failed", priority: 1, input_data: { query: "error test" }, output_data: null, error_message: "Connection timeout", started_at: "2024-01-15T13:00:00Z", completed_at: "2024-01-15T13:00:05Z", created_at: "2024-01-15T12:59:50Z", }, ]; const mockLogs: TaskLog[] = [ { id: "log-1", task_id: "task-1", agent_id: "agent-1", log_level: "INFO", message: "Task started", metadata: { step: 1 }, created_at: "2024-01-15T10:00:00Z", }, { id: "log-2", task_id: "task-1", agent_id: "agent-1", log_level: "INFO", message: "Processing query: test", metadata: { step: 2 }, created_at: "2024-01-15T10:00:05Z", }, { id: "log-3", task_id: "task-1", agent_id: "agent-1", log_level: "DEBUG", message: "Result: optimized", metadata: { step: 3 }, created_at: "2024-01-15T10:00:10Z", }, ]; // ── listTasks ───────────────────────────────────────────────────────────────── describe("Agent监控Dashboard - 执行记录列表", () => { it("应返回执行记录列表", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: mockTasks, total: 4 }) ); const result = await agentsApi.listTasks("test-token"); expect(result.items).toHaveLength(4); expect(result.total).toBe(4); expect(result.items[0].task_type).toBe("geo_optimizer"); }); it("应包含执行时间、状态、耗时信息", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: mockTasks, total: 4 }) ); const result = await agentsApi.listTasks("test-token"); const task = result.items[0]; expect(task.started_at).toBeDefined(); expect(task.completed_at).toBeDefined(); expect(task.status).toBe("completed"); }); }); // ── 状态筛选 ────────────────────────────────────────────────────────────────── describe("Agent监控Dashboard - 状态筛选", () => { it("应支持按 running 状态筛选", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: [mockTasks[2]], total: 1 }) ); const result = await agentsApi.listTasks("test-token", { status: "running" }); expect(result.items).toHaveLength(1); expect(result.items[0].status).toBe("running"); }); it("应支持按 completed 状态筛选", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: mockTasks.filter(t => t.status === "completed"), total: 2 }) ); const result = await agentsApi.listTasks("test-token", { status: "completed" }); expect(result.items).toHaveLength(2); expect(result.items.every(t => t.status === "completed")).toBe(true); }); it("应支持按 failed 状态筛选", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: [mockTasks[3]], total: 1 }) ); const result = await agentsApi.listTasks("test-token", { status: "failed" }); expect(result.items).toHaveLength(1); expect(result.items[0].error_message).toBe("Connection timeout"); }); }); // ── 执行详情和日志 ──────────────────────────────────────────────────────────── describe("Agent监控Dashboard - 执行详情和日志", () => { it("应获取单个执行详情", async () => { mockFetch.mockResolvedValueOnce(mockFetchResponse(mockTasks[0])); const result = await agentsApi.getTask("test-token", "task-1"); expect(result.id).toBe("task-1"); expect(result.task_type).toBe("geo_optimizer"); }); it("应获取执行日志列表", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: mockLogs, total: 3 }) ); const result = await agentsApi.getTaskLogs("test-token", "task-1"); expect(result.items).toHaveLength(3); expect(result.items[0].message).toBe("Task started"); }); it("错误信息应清晰展示", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: mockTasks.filter(t => t.status === "failed"), total: 1 }) ); const result = await agentsApi.listTasks("test-token", { status: "failed" }); const failedTask = result.items[0]; expect(failedTask.status).toBe("failed"); expect(failedTask.error_message).toBe("Connection timeout"); }); }); // ── 统计摘要 ───────────────────────────────────────────────────────────────── describe("Agent监控Dashboard - 统计摘要", () => { it("应计算总执行次数", () => { const tasks = mockTasks; const total = tasks.length; expect(total).toBe(4); }); it("应计算成功率统计", () => { const tasks = mockTasks; const successCount = tasks.filter(t => t.status === "completed").length; const total = tasks.length; const successRate = (successCount / total) * 100; expect(successCount).toBe(2); expect(successRate).toBe(50); }); it("应计算平均耗时", () => { const completedTasks = mockTasks.filter(t => t.status === "completed" && t.started_at && t.completed_at); const durations = completedTasks.map(t => { const start = new Date(t.started_at!).getTime(); const end = new Date(t.completed_at!).getTime(); return (end - start) / 1000; // 转换为秒 }); const avgDuration = durations.reduce((sum, d) => sum + d, 0) / durations.length; expect(avgDuration).toBe(10); // (12 + 8) / 2 = 10秒 }); it("应显示运行中的任务数量", () => { const runningCount = mockTasks.filter(t => t.status === "running").length; expect(runningCount).toBe(1); }); }); // ── getTaskLogs 参数 ────────────────────────────────────────────────────────── describe("Agent监控Dashboard - getTaskLogs 参数", () => { it("应正确传递 skip 和 limit 参数", async () => { mockFetch.mockResolvedValueOnce( mockFetchResponse({ items: [], total: 0 }) ); await agentsApi.getTaskLogs("test-token", "task-1", { skip: 10, limit: 20 }); const [url] = mockFetch.mock.calls[0]; expect(url).toContain("/api/v1/agents/tasks/task-1/logs"); expect(url).toContain("skip=10"); expect(url).toContain("limit=20"); }); });