diff --git a/packages/api/src/__tests__/assistant-tools-estimate-generate-weekly-phasing-errors.test.ts b/packages/api/src/__tests__/assistant-tools-estimate-generate-weekly-phasing-errors.test.ts new file mode 100644 index 0000000..e2fd533 --- /dev/null +++ b/packages/api/src/__tests__/assistant-tools-estimate-generate-weekly-phasing-errors.test.ts @@ -0,0 +1,174 @@ +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(), + createEstimateExport: vi.fn(), + createEstimatePlanningHandoff: vi.fn(), + createEstimateRevision: vi.fn(), + getDashboardBudgetForecast: vi.fn().mockResolvedValue([]), + getDashboardPeakTimes: vi.fn().mockResolvedValue([]), + getEstimateById: vi.fn(), + listAssignmentBookings: vi.fn().mockResolvedValue([]), + submitEstimateVersion: vi.fn(), + updateEstimateDraft: vi.fn(), + }; +}); + +import { getEstimateById } from "@capakraken/application"; + +import { executeTool } from "../router/assistant-tools.js"; +import { + createToolContext, + resetEstimateToolMocks, +} from "./assistant-tools-estimate-test-helpers.js"; + +const managerOptions = { + userRole: SystemRole.MANAGER, + permissions: [PermissionKey.MANAGE_PROJECTS], +} as const; + +describe("assistant estimate weekly phasing generation errors", () => { + beforeEach(() => { + resetEstimateToolMocks(); + }); + + it("returns a stable error when generate_estimate_weekly_phasing cannot find the estimate", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce(null as never); + + const ctx = createToolContext({}, managerOptions); + const result = await executeTool( + "generate_estimate_weekly_phasing", + JSON.stringify({ + estimateId: "est_missing", + startDate: "2026-04-01", + endDate: "2026-04-30", + }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate not found with the given criteria.", + }); + }); + + it("returns a stable error when generate_estimate_weekly_phasing has no working version", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce({ + id: "est_1", + name: "Estimate One", + versions: [], + } as Awaited>); + + const ctx = createToolContext({}, managerOptions); + const result = await executeTool( + "generate_estimate_weekly_phasing", + JSON.stringify({ + estimateId: "est_1", + startDate: "2026-04-01", + endDate: "2026-04-30", + }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate has no working version.", + }); + }); + + it("returns a stable error when generate_estimate_weekly_phasing loses a demand line with model metadata", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce({ + id: "est_2", + name: "Estimate Two", + versions: [ + { + id: "ver_1", + status: "WORKING", + demandLines: [ + { + id: "line_missing", + hours: 40, + metadata: {}, + }, + ], + }, + ], + } as Awaited>); + + const ctx = createToolContext( + { + estimateDemandLine: { + update: vi.fn().mockRejectedValue({ + code: "P2025", + message: "Record not found", + meta: { modelName: "EstimateDemandLine" }, + }), + }, + }, + managerOptions, + ); + + const result = await executeTool( + "generate_estimate_weekly_phasing", + JSON.stringify({ + estimateId: "est_2", + startDate: "2026-04-01", + endDate: "2026-04-30", + }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate demand line not found with the given criteria.", + }); + }); + + it("returns a stable error when generate_estimate_weekly_phasing loses a demand line with generic P2025 metadata", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce({ + id: "est_3", + name: "Estimate Three", + versions: [ + { + id: "ver_2", + status: "WORKING", + demandLines: [ + { + id: "line_missing_generic", + hours: 40, + metadata: {}, + }, + ], + }, + ], + } as Awaited>); + + const ctx = createToolContext( + { + estimateDemandLine: { + update: vi.fn().mockRejectedValue({ + code: "P2025", + message: "Record to update not found", + }), + }, + }, + managerOptions, + ); + + const result = await executeTool( + "generate_estimate_weekly_phasing", + JSON.stringify({ + estimateId: "est_3", + startDate: "2026-04-01", + endDate: "2026-04-30", + }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate demand line not found with the given criteria.", + }); + }); +}); diff --git a/packages/api/src/__tests__/assistant-tools-estimate-get-weekly-phasing-errors.test.ts b/packages/api/src/__tests__/assistant-tools-estimate-get-weekly-phasing-errors.test.ts new file mode 100644 index 0000000..80e73d7 --- /dev/null +++ b/packages/api/src/__tests__/assistant-tools-estimate-get-weekly-phasing-errors.test.ts @@ -0,0 +1,72 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { SystemRole } from "@capakraken/shared"; + +vi.mock("@capakraken/application", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + approveEstimateVersion: vi.fn(), + cloneEstimate: vi.fn(), + createEstimateExport: vi.fn(), + createEstimatePlanningHandoff: vi.fn(), + createEstimateRevision: vi.fn(), + getDashboardBudgetForecast: vi.fn().mockResolvedValue([]), + getDashboardPeakTimes: vi.fn().mockResolvedValue([]), + getEstimateById: vi.fn(), + listAssignmentBookings: vi.fn().mockResolvedValue([]), + submitEstimateVersion: vi.fn(), + updateEstimateDraft: vi.fn(), + }; +}); + +import { getEstimateById } from "@capakraken/application"; + +import { executeTool } from "../router/assistant-tools.js"; +import { + createToolContext, + resetEstimateToolMocks, +} from "./assistant-tools-estimate-test-helpers.js"; + +describe("assistant estimate weekly phasing read errors", () => { + beforeEach(() => { + resetEstimateToolMocks(); + }); + + it("returns a stable error when get_estimate_weekly_phasing cannot find the estimate", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce(null as never); + + const ctx = createToolContext({}, { + userRole: SystemRole.CONTROLLER, + }); + const result = await executeTool( + "get_estimate_weekly_phasing", + JSON.stringify({ estimateId: "est_missing" }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate not found with the given criteria.", + }); + }); + + it("returns a stable error when get_estimate_weekly_phasing cannot find a working version", async () => { + vi.mocked(getEstimateById).mockResolvedValueOnce({ + id: "est_empty", + name: "Estimate Empty", + versions: [], + } as Awaited>); + + const ctx = createToolContext({}, { + userRole: SystemRole.CONTROLLER, + }); + const result = await executeTool( + "get_estimate_weekly_phasing", + JSON.stringify({ estimateId: "est_empty" }), + ctx, + ); + + expect(JSON.parse(result.content)).toEqual({ + error: "Estimate version not found with the given criteria.", + }); + }); +});