Files
Nexus/packages/api/src/__tests__/assistant-tools-project-admin-create-blueprint-errors.test.ts
T

178 lines
5.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { PermissionKey, SystemRole } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import {
createToolContext,
executeTool,
} from "./assistant-tools-project-admin-create-test-helpers.js";
describe("assistant project admin create tools - blueprint errors", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns a stable assistant error when the blueprint cannot be resolved", async () => {
const projectCreate = vi.fn();
const ctx = createToolContext(
{
resource: {
findFirst: vi.fn().mockResolvedValue({
displayName: "Peter Parker",
}),
findMany: vi.fn().mockResolvedValue([]),
},
project: {
findUnique: vi.fn().mockResolvedValue(null),
create: projectCreate,
},
blueprint: {
findUnique: vi.fn().mockResolvedValue(null),
findFirst: vi.fn().mockResolvedValue(null),
},
client: {
findUnique: vi.fn().mockResolvedValue(null),
findFirst: vi.fn().mockResolvedValue(null),
},
auditLog: {
create: vi.fn(),
},
},
{
userRole: SystemRole.ADMIN,
permissions: [PermissionKey.MANAGE_PROJECTS],
},
);
const result = await executeTool(
"create_project",
JSON.stringify({
shortCode: "PROJ-404",
name: "Missing Blueprint Project",
orderType: "CHARGEABLE",
budgetCents: 150000,
startDate: "2026-05-01",
endDate: "2026-06-30",
responsiblePerson: "Peter Parker",
blueprintName: "Missing Blueprint",
}),
ctx,
);
expect(JSON.parse(result.content)).toEqual({
error: 'Blueprint not found: "Missing Blueprint"',
});
expect(projectCreate).not.toHaveBeenCalled();
});
it("returns a generic assistant error when blueprint resolution fails internally during project creation", async () => {
const projectCreate = vi.fn();
const ctx = createToolContext(
{
resource: {
findFirst: vi.fn().mockResolvedValue({
displayName: "Peter Parker",
}),
findMany: vi.fn().mockResolvedValue([]),
},
project: {
findUnique: vi.fn().mockResolvedValue(null),
create: projectCreate,
},
blueprint: {
findUnique: vi.fn().mockRejectedValue(
new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "blueprint resolver connection exhausted",
}),
),
},
client: {
findUnique: vi.fn().mockResolvedValue(null),
findFirst: vi.fn().mockResolvedValue(null),
},
auditLog: {
create: vi.fn(),
},
},
{
userRole: SystemRole.ADMIN,
permissions: [PermissionKey.MANAGE_PROJECTS],
},
);
const result = await executeTool(
"create_project",
JSON.stringify({
shortCode: "PROJ-500",
name: "Blueprint Failure Project",
orderType: "CHARGEABLE",
budgetCents: 150000,
startDate: "2026-05-01",
endDate: "2026-06-30",
responsiblePerson: "Peter Parker",
blueprintName: "Consulting Blueprint",
}),
ctx,
);
expect(JSON.parse(result.content)).toEqual({
error: "The tool could not complete due to an internal error.",
});
expect(projectCreate).not.toHaveBeenCalled();
});
it("returns a stable assistant error when the project blueprint disappears before creation", async () => {
const projectCreate = vi.fn();
const blueprintFindUnique = vi.fn()
.mockResolvedValueOnce({
id: "bp_1",
name: "Consulting Blueprint",
target: "PROJECT",
isActive: true,
})
.mockResolvedValueOnce(null);
const ctx = createToolContext(
{
resource: {
findFirst: vi.fn().mockResolvedValue({
displayName: "Peter Parker",
}),
findMany: vi.fn().mockResolvedValue([]),
},
project: {
findUnique: vi.fn().mockResolvedValue(null),
create: projectCreate,
},
blueprint: {
findUnique: blueprintFindUnique,
findFirst: vi.fn().mockResolvedValue(null),
},
},
{
userRole: SystemRole.ADMIN,
permissions: [PermissionKey.MANAGE_PROJECTS],
},
);
const result = await executeTool(
"create_project",
JSON.stringify({
shortCode: "PROJ-BP",
name: "Blueprint Race Project",
orderType: "CHARGEABLE",
budgetCents: 150000,
startDate: "2026-05-01",
endDate: "2026-06-30",
responsiblePerson: "Peter Parker",
blueprintName: "Consulting Blueprint",
}),
ctx,
);
expect(JSON.parse(result.content)).toEqual({
error: "Blueprint not found with the given criteria.",
});
expect(projectCreate).not.toHaveBeenCalled();
});
});