import { DispoStagedRecordType, ImportBatchStatus, StagedRecordStatus } from "@capakraken/db"; import { beforeEach, describe, expect, it, vi } from "vitest"; const { assessDispoImportReadiness, stageDispoImportBatch, } = vi.hoisted(() => ({ assessDispoImportReadiness: vi.fn(), stageDispoImportBatch: vi.fn(), })); const { cancelImportBatchMutation, commitImportBatchMutation, resolveStagedRecordMutation, } = vi.hoisted(() => ({ cancelImportBatchMutation: vi.fn(), commitImportBatchMutation: vi.fn(), resolveStagedRecordMutation: vi.fn(), })); const { getImportBatchQuery, listImportBatchesQuery, listStagedAssignmentsQuery, listStagedProjectsQuery, listStagedResourcesQuery, listStagedUnresolvedRecordsQuery, listStagedVacationsQuery, } = vi.hoisted(() => ({ getImportBatchQuery: vi.fn(), listImportBatchesQuery: vi.fn(), listStagedAssignmentsQuery: vi.fn(), listStagedProjectsQuery: vi.fn(), listStagedResourcesQuery: vi.fn(), listStagedUnresolvedRecordsQuery: vi.fn(), listStagedVacationsQuery: vi.fn(), })); vi.mock("@capakraken/application", () => ({ assessDispoImportReadiness, stageDispoImportBatch, })); vi.mock("../router/dispo-management.js", () => ({ cancelImportBatch: cancelImportBatchMutation, commitImportBatch: commitImportBatchMutation, resolveStagedRecord: resolveStagedRecordMutation, })); vi.mock("../router/dispo-read.js", () => ({ getImportBatch: getImportBatchQuery, listImportBatches: listImportBatchesQuery, listStagedAssignments: listStagedAssignmentsQuery, listStagedProjects: listStagedProjectsQuery, listStagedResources: listStagedResourcesQuery, listStagedUnresolvedRecords: listStagedUnresolvedRecordsQuery, listStagedVacations: listStagedVacationsQuery, })); import { cancelImportBatch, commitImportBatch, getImportBatch, listImportBatches, listStagedAssignments, listStagedProjects, listStagedResources, listStagedUnresolvedRecords, listStagedVacations, resolveStagedRecord, stageImportBatch as stageImportBatchProcedure, validateImportBatch, } from "../router/dispo-procedure-support.js"; function createContext(db: Record) { return { db: db as never, dbUser: { id: "user_admin" } as never, }; } describe("dispo procedure support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("forwards stage-import inputs to the application layer", async () => { stageDispoImportBatch.mockResolvedValue({ id: "batch_1" }); const db = { marker: "db" }; const result = await stageImportBatchProcedure(createContext(db), { chargeabilityWorkbookPath: "/tmp/chargeability.xlsx", costWorkbookPath: "/tmp/cost.xlsx", notes: null, planningWorkbookPath: "/tmp/planning.xlsx", referenceWorkbookPath: "/tmp/reference.xlsx", rosterWorkbookPath: "/tmp/roster.xlsx", }); expect(stageDispoImportBatch).toHaveBeenCalledWith(db, { chargeabilityWorkbookPath: "/tmp/chargeability.xlsx", costWorkbookPath: "/tmp/cost.xlsx", notes: null, planningWorkbookPath: "/tmp/planning.xlsx", referenceWorkbookPath: "/tmp/reference.xlsx", rosterWorkbookPath: "/tmp/roster.xlsx", }); expect(result).toEqual({ id: "batch_1" }); }); it("forwards validate-import inputs without requiring router context", async () => { assessDispoImportReadiness.mockResolvedValue({ ready: true }); const result = await validateImportBatch({ chargeabilityWorkbookPath: "/tmp/chargeability.xlsx", importBatchId: "batch_1", notes: "recheck", planningWorkbookPath: "/tmp/planning.xlsx", referenceWorkbookPath: "/tmp/reference.xlsx", }); expect(assessDispoImportReadiness).toHaveBeenCalledWith({ chargeabilityWorkbookPath: "/tmp/chargeability.xlsx", importBatchId: "batch_1", notes: "recheck", planningWorkbookPath: "/tmp/planning.xlsx", referenceWorkbookPath: "/tmp/reference.xlsx", }); expect(result).toEqual({ ready: true }); }); it("delegates staged-record reads to the read helpers", async () => { const db = { marker: "db" }; listImportBatchesQuery.mockResolvedValue({ items: [], nextCursor: undefined }); getImportBatchQuery.mockResolvedValue({ id: "batch_1" }); listStagedResourcesQuery.mockResolvedValue({ items: [{ id: "res_1" }] }); listStagedProjectsQuery.mockResolvedValue({ items: [{ id: "proj_1" }] }); listStagedAssignmentsQuery.mockResolvedValue({ items: [{ id: "assign_1" }] }); listStagedVacationsQuery.mockResolvedValue({ items: [{ id: "vac_1" }] }); listStagedUnresolvedRecordsQuery.mockResolvedValue({ items: [{ id: "unres_1" }] }); await expect( listImportBatches(createContext(db), { limit: 50, status: ImportBatchStatus.STAGED }), ).resolves.toEqual({ items: [], nextCursor: undefined }); await expect( getImportBatch(createContext(db), { id: "batch_1" }), ).resolves.toEqual({ id: "batch_1" }); await expect( listStagedResources(createContext(db), { importBatchId: "batch_1", limit: 25, status: StagedRecordStatus.STAGED, }), ).resolves.toEqual({ items: [{ id: "res_1" }] }); await expect( listStagedProjects(createContext(db), { importBatchId: "batch_1", isTbd: true, limit: 25, }), ).resolves.toEqual({ items: [{ id: "proj_1" }] }); await expect( listStagedAssignments(createContext(db), { importBatchId: "batch_1", limit: 25, resourceExternalId: "R-1", }), ).resolves.toEqual({ items: [{ id: "assign_1" }] }); await expect( listStagedVacations(createContext(db), { importBatchId: "batch_1", limit: 25, }), ).resolves.toEqual({ items: [{ id: "vac_1" }] }); await expect( listStagedUnresolvedRecords(createContext(db), { importBatchId: "batch_1", limit: 25, recordType: DispoStagedRecordType.PROJECT, }), ).resolves.toEqual({ items: [{ id: "unres_1" }] }); expect(listImportBatchesQuery).toHaveBeenCalledWith(db, { limit: 50, status: ImportBatchStatus.STAGED, }); expect(getImportBatchQuery).toHaveBeenCalledWith(db, "batch_1"); expect(listStagedResourcesQuery).toHaveBeenCalledWith(db, { importBatchId: "batch_1", limit: 25, status: StagedRecordStatus.STAGED, }); expect(listStagedProjectsQuery).toHaveBeenCalledWith(db, { importBatchId: "batch_1", isTbd: true, limit: 25, }); expect(listStagedAssignmentsQuery).toHaveBeenCalledWith(db, { importBatchId: "batch_1", limit: 25, resourceExternalId: "R-1", }); expect(listStagedVacationsQuery).toHaveBeenCalledWith(db, { importBatchId: "batch_1", limit: 25, }); expect(listStagedUnresolvedRecordsQuery).toHaveBeenCalledWith(db, { importBatchId: "batch_1", limit: 25, recordType: DispoStagedRecordType.PROJECT, }); }); it("passes user-scoped mutations through the management helpers", async () => { const db = { marker: "db" }; cancelImportBatchMutation.mockResolvedValue({ id: "batch_1", status: ImportBatchStatus.CANCELLED }); resolveStagedRecordMutation.mockResolvedValue({ id: "record_1", status: StagedRecordStatus.APPROVED }); commitImportBatchMutation.mockResolvedValue({ importedAssignments: 4 }); await expect( cancelImportBatch(createContext(db), { id: "batch_1" }), ).resolves.toEqual({ id: "batch_1", status: ImportBatchStatus.CANCELLED }); await expect( resolveStagedRecord(createContext(db), { action: "APPROVE", id: "record_1", recordType: DispoStagedRecordType.ASSIGNMENT, }), ).resolves.toEqual({ id: "record_1", status: StagedRecordStatus.APPROVED }); await expect( commitImportBatch(createContext(db), { allowTbdUnresolved: true, importBatchId: "batch_1", importTbdProjects: false, }), ).resolves.toEqual({ importedAssignments: 4 }); expect(cancelImportBatchMutation).toHaveBeenCalledWith(db, { id: "batch_1", userId: "user_admin", }); expect(resolveStagedRecordMutation).toHaveBeenCalledWith(db, { action: "APPROVE", id: "record_1", recordType: DispoStagedRecordType.ASSIGNMENT, }); expect(commitImportBatchMutation).toHaveBeenCalledWith(db, { allowTbdUnresolved: true, importBatchId: "batch_1", importTbdProjects: false, userId: "user_admin", }); }); });