test(api): cover assistant project computation views

This commit is contained in:
2026-04-01 00:42:02 +02:00
parent 22ead3ca3d
commit 0039a9997a
3 changed files with 389 additions and 0 deletions
@@ -0,0 +1,175 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_OPENAI_MODEL, SystemRole } from "@capakraken/shared";
vi.mock("@capakraken/application", async (importOriginal) => {
const actual = await importOriginal<typeof import("@capakraken/application")>();
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(),
};
});
vi.mock("../ai-client.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../ai-client.js")>();
return {
...actual,
createAiClient: vi.fn(() => ({
chat: {
completions: {
create: vi.fn().mockResolvedValue({
choices: [
{
message: {
content: "Project is on track overall, but staffing remains partially open.",
},
},
],
}),
},
},
})),
loggedAiCall: vi.fn(async (_provider, _model, _promptLength, fn) => fn()),
};
});
import {
createToolContext,
executeTool,
} from "./assistant-tools-project-media-test-helpers.js";
describe("assistant project media tools", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("routes project narrative generation through the real insights router path", async () => {
const projectFindUnique = vi.fn().mockResolvedValue({
id: "project_1",
name: "Project One",
shortCode: "PROJ-1",
status: "ACTIVE",
startDate: new Date("2026-03-01T00:00:00.000Z"),
endDate: new Date("2026-06-30T00:00:00.000Z"),
budgetCents: 250_000_00,
dynamicFields: { existingFlag: true },
demandRequirements: [
{
headcount: 2,
_count: { assignments: 1 },
},
],
assignments: [
{
status: "ACTIVE",
dailyCostCents: 42_000,
startDate: new Date("2026-03-10T00:00:00.000Z"),
endDate: new Date("2026-03-20T00:00:00.000Z"),
resource: { displayName: "Carol Danvers" },
},
],
});
const projectUpdate = vi.fn().mockResolvedValue({
id: "project_1",
dynamicFields: {
existingFlag: true,
aiNarrative: "Project is on track overall, but staffing remains partially open.",
},
});
const ctx = createToolContext(
{
project: {
findUnique: projectFindUnique,
update: projectUpdate,
},
systemSettings: {
findUnique: vi.fn().mockResolvedValue({
id: "singleton",
aiProvider: "openai",
azureOpenAiApiKey: "sk-test",
azureOpenAiDeployment: DEFAULT_OPENAI_MODEL,
}),
},
},
{
userRole: SystemRole.CONTROLLER,
},
);
const result = await executeTool(
"generate_project_narrative",
JSON.stringify({ projectId: "project_1" }),
ctx,
);
expect(projectFindUnique).toHaveBeenCalledWith({
where: { id: "project_1" },
include: {
demandRequirements: {
select: {
id: true,
role: true,
headcount: true,
hoursPerDay: true,
startDate: true,
endDate: true,
status: true,
_count: { select: { assignments: true } },
},
},
assignments: {
select: {
id: true,
role: true,
hoursPerDay: true,
startDate: true,
endDate: true,
status: true,
dailyCostCents: true,
resource: { select: { displayName: true } },
},
},
},
});
expect(projectUpdate).toHaveBeenCalledWith({
where: { id: "project_1" },
data: {
dynamicFields: expect.objectContaining({
existingFlag: true,
aiNarrative: "Project is on track overall, but staffing remains partially open.",
aiNarrativeGeneratedAt: expect.any(String),
}),
},
});
expect(JSON.parse(result.content)).toEqual(
expect.objectContaining({
narrative: "Project is on track overall, but staffing remains partially open.",
generatedAt: expect.any(String),
}),
);
});
});