import { beforeEach, describe, expect, it, vi } from "vitest"; import { PermissionKey, SystemRole } from "@capakraken/shared"; vi.mock("@capakraken/application", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, approveEstimateVersion: vi.fn(), cloneEstimate: vi.fn(), commitDispoImportBatch: vi.fn(), countPlanningEntries: vi.fn().mockResolvedValue({ countsByRoleId: new Map() }), createEstimateExport: vi.fn(), createEstimatePlanningHandoff: vi.fn(), createEstimateRevision: vi.fn(), assessDispoImportReadiness: vi.fn(), loadResourceDailyAvailabilityContexts: vi.fn().mockResolvedValue(new Map()), getDashboardDemand: vi.fn().mockResolvedValue([]), getDashboardBudgetForecast: vi.fn().mockResolvedValue([]), getDashboardOverview: vi.fn(), getDashboardSkillGapSummary: vi.fn().mockResolvedValue({ roleGaps: [], totalOpenPositions: 0, skillSupplyTop10: [], resourcesByRole: [], }), getDashboardProjectHealth: vi.fn().mockResolvedValue([]), getDashboardPeakTimes: vi.fn().mockResolvedValue([]), getDashboardTopValueResources: vi.fn().mockResolvedValue([]), getEstimateById: vi.fn(), listAssignmentBookings: vi.fn().mockResolvedValue([]), stageDispoImportBatch: vi.fn(), submitEstimateVersion: vi.fn(), updateEstimateDraft: vi.fn(), }; }); import { executeTool } from "../router/assistant-tools.js"; import { createToolContext } from "./assistant-tools-allocation-planning-test-helpers.js"; describe("assistant demand fill tool", () => { beforeEach(() => { vi.clearAllMocks(); }); it("routes demand filling through the real allocation router path", async () => { const auditCreate = vi.fn().mockResolvedValue({ id: "audit_1" }); const assignmentCreate = vi.fn().mockResolvedValue({ id: "assignment_1", demandRequirementId: "demand_1", resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-05-01T00:00:00.000Z"), endDate: new Date("2026-05-15T00:00:00.000Z"), hoursPerDay: 6, percentage: 75, role: "Designer", roleId: "role_1", dailyCostCents: 42000, status: "PROPOSED", metadata: {}, createdAt: new Date("2026-03-29T00:00:00.000Z"), updatedAt: new Date("2026-03-29T00:00:00.000Z"), resource: { id: "resource_1", displayName: "Carol Danvers", eid: "EMP-001", lcrCents: 7000 }, project: { id: "project_1", name: "Project One", shortCode: "PROJ-1" }, roleEntity: { id: "role_1", name: "Designer", color: "#00AAFF" }, demandRequirement: { id: "demand_1", projectId: "project_1", startDate: new Date("2026-05-01T00:00:00.000Z"), endDate: new Date("2026-05-15T00:00:00.000Z"), hoursPerDay: 6, percentage: 75, role: "Designer", roleId: "role_1", headcount: 1, status: "PROPOSED", }, }); const txDemandUpdate = vi.fn().mockResolvedValue({ id: "demand_1", projectId: "project_1", headcount: 1, status: "COMPLETED", }); const tx = { project: { findUnique: vi.fn().mockResolvedValue({ id: "project_1" }), }, resource: { findUnique: vi.fn().mockResolvedValue({ id: "resource_1", lcrCents: 7000, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0, }, }), }, demandRequirement: { findUnique: vi.fn().mockResolvedValue({ id: "demand_1", projectId: "project_1", }), update: txDemandUpdate, }, assignment: { findMany: vi.fn().mockResolvedValue([]), create: assignmentCreate, }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, auditLog: { create: auditCreate, }, }; const transaction = vi.fn().mockImplementation(async (callback: (inner: typeof tx) => Promise) => callback(tx)); const demandRecord = { id: "demand_1", projectId: "project_1", startDate: new Date("2026-05-01T00:00:00.000Z"), endDate: new Date("2026-05-15T00:00:00.000Z"), hoursPerDay: 6, percentage: 75, role: "Designer", roleId: "role_1", headcount: 1, status: "PROPOSED", metadata: {}, roleEntity: { name: "Designer" }, project: { id: "project_1", name: "Project One", shortCode: "PROJ-1" }, }; const demandFindUnique = vi.fn() .mockResolvedValueOnce(demandRecord) .mockResolvedValueOnce(demandRecord); const resourceFindUnique = vi.fn().mockResolvedValue({ id: "resource_1", displayName: "Carol Danvers", lcrCents: 7000, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0, }, }); const ctx = createToolContext( { demandRequirement: { findUnique: demandFindUnique, }, project: { findUnique: vi.fn().mockResolvedValue({ id: "project_1", name: "Project One", shortCode: "PROJ-1", budgetCents: 100000, }), }, user: { findMany: vi.fn().mockResolvedValue([]), }, notification: { findFirst: vi.fn().mockResolvedValue(null), create: vi.fn(), }, resource: { findUnique: resourceFindUnique, findFirst: vi.fn().mockResolvedValue(null), }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, $transaction: transaction, }, { userRole: SystemRole.ADMIN, permissions: [PermissionKey.MANAGE_ALLOCATIONS], }, ); const result = await executeTool( "fill_demand", JSON.stringify({ demandId: "demand_1", resourceId: "resource_1" }), ctx, ); expect(JSON.parse(result.content)).toEqual(expect.objectContaining({ success: true, message: "Assigned Carol Danvers to Designer on Project One (PROJ-1)", assignmentId: "assignment_1", })); expect(assignmentCreate).toHaveBeenCalledTimes(1); expect(txDemandUpdate).toHaveBeenCalledWith(expect.objectContaining({ where: { id: "demand_1" }, data: { status: "COMPLETED" }, })); expect(auditCreate).toHaveBeenCalledTimes(1); }); });