/* eslint-disable @typescript-eslint/no-unsafe-argument */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { fetchTasks } from "./tasks"; import { TaskStatus, TaskPriority, type Task } from "@mosaic/shared"; // Mock the API client vi.mock("./client", () => ({ apiGet: vi.fn(), })); const { apiGet } = await import("./client"); describe("Task API Client", (): void => { beforeEach((): void => { vi.clearAllMocks(); }); it("should fetch tasks successfully", async (): Promise => { const mockTasks: Task[] = [ { id: "task-1", title: "Complete project setup", description: "Set up the development environment", status: TaskStatus.IN_PROGRESS, priority: TaskPriority.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: TaskStatus.NOT_STARTED, priority: TaskPriority.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", undefined); expect(result).toEqual(mockTasks); }); it("should handle errors when fetching tasks", async (): Promise => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Network error")); await expect(fetchTasks()).rejects.toThrow("Network error"); }); it("should fetch tasks with filters", async (): Promise => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ status: TaskStatus.IN_PROGRESS }); expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS", undefined); }); it("should fetch tasks with multiple filters", async (): Promise => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ status: TaskStatus.IN_PROGRESS, priority: TaskPriority.HIGH }); expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS&priority=HIGH", undefined); }); it("should fetch tasks with workspace ID", async (): Promise => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ workspaceId: "workspace-123" }); // WorkspaceId should be sent via header (second param), not query string expect(apiGet).toHaveBeenCalledWith("/api/tasks", "workspace-123"); }); it("should fetch tasks with filters and workspace ID", async (): Promise => { const mockTasks: Task[] = []; vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks }); await fetchTasks({ status: TaskStatus.IN_PROGRESS, workspaceId: "workspace-456", }); // Status in query, workspace in header expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS", "workspace-456"); }); describe("error handling", (): void => { it("should handle network errors when fetching tasks", async (): Promise => { 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 (): Promise => { vi.mocked(apiGet).mockResolvedValueOnce({ data: null, }); const result = await fetchTasks(); expect(result).toBeNull(); }); it("should handle auth token expiration (401 error)", async (): Promise => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Authentication required")); await expect(fetchTasks()).rejects.toThrow("Authentication required"); }); it("should handle server 500 errors", async (): Promise => { 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 (): Promise => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Access denied")); await expect(fetchTasks()).rejects.toThrow("Access denied"); }); it("should handle rate limiting errors", async (): Promise => { 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 (): Promise => { 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", undefined); }); it("should handle empty response data", async (): Promise => { vi.mocked(apiGet).mockResolvedValueOnce({}); const result = await fetchTasks(); expect(result).toBeUndefined(); }); it("should handle timeout errors", async (): Promise => { vi.mocked(apiGet).mockRejectedValueOnce(new Error("Request timeout")); await expect(fetchTasks()).rejects.toThrow("Request timeout"); }); }); });