import { beforeEach, describe, expect, it, vi } from "vitest"; import { TRPCError } from "@trpc/server"; import { buildBaseProject, buildBaseResource, createQuickAssignInput, createToolContext, executeTool, quickAssignPermissions, quickAssignRole, } from "./assistant-tools-timeline-quick-assign-test-helpers.js"; describe("assistant timeline quick-assign error tools", () => { beforeEach(() => { vi.clearAllMocks(); }); it("returns a stable conflict error for quick-assign timeline mutations", async () => { const db = { project: { findUnique: vi.fn().mockResolvedValue(buildBaseProject()), }, resource: { findUnique: vi.fn().mockResolvedValue(buildBaseResource()), }, assignment: { findMany: vi.fn().mockResolvedValue([]), create: vi.fn().mockRejectedValue( new TRPCError({ code: "CONFLICT", message: "Resource is already assigned to this project with overlapping dates", }), ), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, auditLog: { create: vi.fn().mockResolvedValue({}), }, $transaction: vi.fn(async (callback: (tx: unknown) => unknown) => callback(db)), }; const ctx = createToolContext(db, quickAssignPermissions, quickAssignRole); const result = await executeTool( "quick_assign_timeline_resource", JSON.stringify(createQuickAssignInput()), ctx, ); expect(result.action).toBeUndefined(); expect(JSON.parse(result.content)).toEqual({ error: "Resource is already assigned to this project with overlapping dates", }); }); it("returns stable not-found errors when quick-assign timeline targets disappear mid-mutation", async () => { const cases = [ { name: "missing project", tx: { project: { findUnique: vi.fn().mockResolvedValue(null), }, resource: { findUnique: vi.fn().mockResolvedValue(buildBaseResource()), }, assignment: { findMany: vi.fn().mockResolvedValue([]), create: vi.fn(), }, }, expected: "Project not found with the given criteria.", }, { name: "missing resource", tx: { project: { findUnique: vi.fn().mockResolvedValue(buildBaseProject()), }, resource: { findUnique: vi.fn().mockResolvedValue(null), }, assignment: { findMany: vi.fn().mockResolvedValue([]), create: vi.fn(), }, }, expected: "Resource not found with the given criteria.", }, ] as const; for (const testCase of cases) { const db = { project: { findUnique: vi.fn().mockResolvedValue(buildBaseProject()), }, resource: { findUnique: vi.fn().mockResolvedValue(buildBaseResource()), }, assignment: { findMany: vi.fn().mockResolvedValue([]), create: vi.fn(), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, auditLog: { create: vi.fn().mockResolvedValue({}), }, $transaction: vi.fn(async (callback: (tx: typeof testCase.tx) => unknown) => callback(testCase.tx)), }; const ctx = createToolContext(db, quickAssignPermissions, quickAssignRole); const result = await executeTool( "quick_assign_timeline_resource", JSON.stringify(createQuickAssignInput()), ctx, ); expect(JSON.parse(result.content)).toEqual({ error: testCase.expected }); } }); it("returns a stable validation error for quick-assign timeline date ranges", async () => { const ctx = createToolContext( { project: { findUnique: vi.fn().mockResolvedValue(buildBaseProject()), }, resource: { findUnique: vi.fn().mockResolvedValue(buildBaseResource()), }, }, quickAssignPermissions, quickAssignRole, ); const result = await executeTool( "quick_assign_timeline_resource", JSON.stringify(createQuickAssignInput({ startDate: "2026-03-20", endDate: "2026-03-16", })), ctx, ); expect(JSON.parse(result.content)).toEqual({ error: "End date must be after start date", }); }); });