test(api): cover assistant timeline batch quick assign

This commit is contained in:
2026-04-01 00:34:25 +02:00
parent 705b570684
commit adf25f328f
3 changed files with 223 additions and 0 deletions
@@ -0,0 +1,91 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
batchQuickAssignPermissions,
batchQuickAssignRole,
buildBaseProject,
buildBaseResource,
createBatchQuickAssignInput,
createToolContext,
executeTool,
} from "./assistant-tools-timeline-batch-quick-assign-test-helpers.js";
describe("assistant timeline batch quick-assign error tools", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns a structured batch assignment resolver error for an unknown resource", async () => {
const db = {
project: {
findUnique: vi.fn().mockResolvedValue({
...buildBaseProject(),
startDate: new Date("2026-03-16"),
endDate: new Date("2026-03-20"),
}),
findFirst: vi.fn().mockResolvedValue(null),
},
resource: {
findUnique: vi.fn().mockResolvedValue(null),
findFirst: vi.fn().mockResolvedValue(null),
findMany: vi.fn().mockResolvedValue([]),
},
assignment: {
create: vi.fn(),
},
};
const ctx = createToolContext(db, batchQuickAssignPermissions, batchQuickAssignRole);
const result = await executeTool(
"batch_quick_assign_timeline_resources",
JSON.stringify(
createBatchQuickAssignInput([
{
resourceIdentifier: "missing_resource",
},
]),
),
ctx,
);
expect(result.action).toBeUndefined();
expect(JSON.parse(result.content)).toEqual({
error: "assignments[0].resourceIdentifier: Resource not found: missing_resource",
field: "assignments[0].resourceIdentifier",
index: 0,
});
expect(db.assignment.create).not.toHaveBeenCalled();
});
it("returns a stable validation error for batch timeline mutation date ranges", async () => {
const ctx = createToolContext(
{
project: {
findUnique: vi.fn().mockResolvedValue(buildBaseProject()),
},
resource: {
findUnique: vi.fn().mockResolvedValue(buildBaseResource()),
},
},
batchQuickAssignPermissions,
batchQuickAssignRole,
);
const result = await executeTool(
"batch_quick_assign_timeline_resources",
JSON.stringify(
createBatchQuickAssignInput([
{
startDate: "2026-03-20",
endDate: "2026-03-16",
},
]),
),
ctx,
);
expect(JSON.parse(result.content)).toEqual({
error: "End date must be after start date",
});
});
});
@@ -0,0 +1,60 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
batchQuickAssignPermissions,
batchQuickAssignRole,
buildBaseProject,
buildBaseResource,
createBatchQuickAssignInput,
createToolContext,
executeTool,
} from "./assistant-tools-timeline-batch-quick-assign-test-helpers.js";
describe("assistant timeline batch quick-assign tools", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("batch quick-assigns timeline resources through the real timeline router mutation", async () => {
const db = {
project: {
findUnique: vi.fn().mockResolvedValue(buildBaseProject()),
},
resource: {
findUnique: vi
.fn()
.mockImplementation(async ({ where }: { where: { id: string } }) => buildBaseResource(where.id)),
},
assignment: {
findMany: vi.fn().mockResolvedValue([]),
create: vi
.fn()
.mockResolvedValueOnce({ id: "assignment_batch_1" })
.mockResolvedValueOnce({ id: "assignment_batch_2" }),
},
auditLog: {
create: vi.fn().mockResolvedValue({}),
},
vacation: {
findMany: vi.fn().mockResolvedValue([]),
},
$transaction: vi.fn(async (callback: (tx: unknown) => unknown) => callback(db)),
};
const ctx = createToolContext(db, batchQuickAssignPermissions, batchQuickAssignRole);
const result = await executeTool(
"batch_quick_assign_timeline_resources",
JSON.stringify(createBatchQuickAssignInput()),
ctx,
);
expect(result.action).toEqual({ type: "invalidate", scope: ["allocation", "timeline", "project"] });
expect(JSON.parse(result.content)).toEqual(
expect.objectContaining({
success: true,
count: 2,
}),
);
expect(db.assignment.create).toHaveBeenCalledTimes(2);
});
});
@@ -0,0 +1,72 @@
import { PermissionKey, SystemRole } from "@capakraken/shared";
import { vi } from "vitest";
vi.mock("@capakraken/application", async (importOriginal) => {
const actual = await importOriginal<typeof import("@capakraken/application")>();
return {
...actual,
getDashboardBudgetForecast: vi.fn().mockResolvedValue([]),
getDashboardPeakTimes: vi.fn().mockResolvedValue([]),
listAssignmentBookings: vi.fn().mockResolvedValue([]),
};
});
vi.mock("../sse/event-bus.js", () => ({
emitAllocationCreated: vi.fn(),
emitAllocationDeleted: vi.fn(),
emitAllocationUpdated: vi.fn(),
emitProjectShifted: vi.fn(),
}));
vi.mock("../lib/budget-alerts.js", () => ({
checkBudgetThresholds: vi.fn(),
}));
vi.mock("../lib/cache.js", () => ({
invalidateDashboardCache: vi.fn(),
}));
import { executeTool as executeAssistantTool } from "../router/assistant-tools.js";
export { createToolContext } from "./assistant-tools-advanced-timeline-test-helpers.js";
export {
buildBaseProject,
buildBaseResource,
} from "./assistant-tools-timeline-allocation-mutation-test-helpers.js";
export const batchQuickAssignPermissions = [
PermissionKey.MANAGE_ALLOCATIONS,
PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS,
];
export const batchQuickAssignRole = SystemRole.MANAGER;
export function createBatchQuickAssignInput(
overrides: Array<Record<string, unknown>> = [],
) {
const baseAssignments = [
{
resourceIdentifier: "resource_1",
projectIdentifier: "project_1",
startDate: "2026-03-16",
endDate: "2026-03-20",
hoursPerDay: 8,
},
{
resourceIdentifier: "resource_2",
projectIdentifier: "project_1",
startDate: "2026-03-23",
endDate: "2026-03-27",
hoursPerDay: 6,
},
];
return {
assignments: baseAssignments.map((assignment, index) => ({
...assignment,
...(overrides[index] ?? {}),
})),
};
}
export const executeTool = executeAssistantTool;