import { describe, it, expect, vi, beforeEach } from "vitest"; import { fetchTasks } from "./tasks"; import type { Task } from "@mosaic/shared"; // Mock the API client vi.mock("./client", () => ({ apiGet: vi.fn(), })); const { apiGet } = await import("./client"); describe("Task API Client", () => { beforeEach(() => { vi.clearAllMocks(); }); it("should fetch tasks successfully", async () => { const mockTasks: Task[] = [ { id: "task-1", title: "Complete project setup", description: "Set up the development environment", status: "active", priority: "high", dueDate: new Date("2026-02-01"), creatorId: "user-1", assigneeId: "user-1", workspaceId: "workspace-1", projectId: null, parentId: null, sortOrder: 0, metadata: {}, completedAt: null, createdAt: new Date("2026-01-28"), updatedAt: new Date("2026-01-28"), }, { id: "task-2", title: "Review documentation", description: "Review and update project docs", status: "upcoming", priority: "medium", dueDate: new Date("2026-02-05"), creatorId: "user-1", assigneeId: "user-1", workspaceId: "workspace-1", projectId: null, parentId: null, sortOrder: 1, metadata: {}, completedAt: null, createdAt: new Date("2026-01-28"), updatedAt: new Date("2026-01-28"), }, ]; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); const result = await fetchTasks(); expect(apiGet).toHaveBeenCalledWith("/api/tasks"); expect(result).toEqual(mockTasks); }); it("should handle errors when fetching tasks", async () => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Network error")); await expect(fetchTasks()).rejects.toThrow("Network error"); }); it("should fetch tasks with filters", async () => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ status: "active" }); expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=active"); }); it("should fetch tasks with multiple filters", async () => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ status: "active", priority: "high" }); expect(apiGet).toHaveBeenCalledWith( "/api/tasks?status=active&priority=high" ); }); describe("error handling", () => { it("should handle network errors when fetching tasks", async () => { vi.mocked(apiGet).mockRejectedValueOnce( new Error("Network request failed") ); await expect(fetchTasks()).rejects.toThrow("Network request failed"); }); it("should handle API returning malformed data", async () => { vi.mocked(apiGet).mockResolvedValueOnce({ data: null, }); const result = await fetchTasks(); expect(result).toBeNull(); }); it("should handle auth token expiration (401 error)", async () => { vi.mocked(apiGet).mockRejectedValueOnce( new Error("Authentication required") ); await expect(fetchTasks()).rejects.toThrow("Authentication required"); }); it("should handle server 500 errors", async () => { vi.mocked(apiGet).mockRejectedValueOnce( new Error("Internal server error") ); await expect(fetchTasks()).rejects.toThrow("Internal server error"); }); it("should handle forbidden access (403 error)", async () => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Access denied")); await expect(fetchTasks()).rejects.toThrow("Access denied"); }); it("should handle rate limiting errors", async () => { vi.mocked(apiGet).mockRejectedValueOnce( new Error("Too many requests. Please try again later.") ); await expect(fetchTasks()).rejects.toThrow( "Too many requests. Please try again later." ); }); it("should ignore malformed filter parameters", async () => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); // eslint-disable-next-line @typescript-eslint/no-explicit-any await fetchTasks({ invalidFilter: "value" } as any); // Function should ignore invalid filters and call without query params expect(apiGet).toHaveBeenCalledWith("/api/tasks"); }); it("should handle empty response data", async () => { vi.mocked(apiGet).mockResolvedValueOnce({}); const result = await fetchTasks(); expect(result).toBeUndefined(); }); it("should handle timeout errors", async () => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Request timeout")); await expect(fetchTasks()).rejects.toThrow("Request timeout"); }); }); });