import { beforeEach, describe, expect, it, vi } from "vitest"; import { PermissionKey, SystemRole } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import { createProject, createToolContext, executeTool, } from "./assistant-tools-project-admin-test-helpers.js"; describe("assistant project admin delete tools", () => { beforeEach(() => { vi.clearAllMocks(); }); it("enforces admin-only project deletion through the real project router path", async () => { const ctx = createToolContext( { project: { findUnique: vi.fn().mockResolvedValue(createProject({ status: "ACTIVE" })), }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, }, { userRole: SystemRole.MANAGER, permissions: [PermissionKey.MANAGE_PROJECTS], }, ); const result = await executeTool( "delete_project", JSON.stringify({ projectId: "project_1" }), ctx, ); expect(JSON.parse(result.content)).toEqual(expect.objectContaining({ error: "You do not have permission to perform this action.", })); }); it("routes project deletion through the real project router path for admins", async () => { const auditCreate = vi.fn().mockResolvedValue({ id: "audit_1" }); const tx = { assignment: { deleteMany: vi.fn().mockResolvedValue({ count: 0 }) }, demandRequirement: { deleteMany: vi.fn().mockResolvedValue({ count: 0 }) }, calculationRule: { updateMany: vi.fn().mockResolvedValue({ count: 0 }) }, project: { delete: vi.fn().mockResolvedValue({ id: "project_1" }) }, auditLog: { create: auditCreate }, }; const transaction = vi .fn() .mockImplementation(async (callback: (inner: typeof tx) => Promise) => callback(tx)); const projectFindUnique = vi.fn() .mockResolvedValueOnce(createProject({ status: "ACTIVE" })) .mockResolvedValueOnce(createProject({ name: "Project One" })); const ctx = createToolContext( { project: { findUnique: projectFindUnique, }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, $transaction: transaction, }, { userRole: SystemRole.ADMIN, permissions: [PermissionKey.MANAGE_PROJECTS], }, ); const result = await executeTool( "delete_project", JSON.stringify({ projectId: "project_1" }), ctx, ); expect(JSON.parse(result.content)).toEqual(expect.objectContaining({ success: true, message: "Deleted project: Project One (PROJ-1)", })); expect(transaction).toHaveBeenCalledTimes(1); expect(auditCreate).toHaveBeenCalledTimes(1); }); it("returns a stable error when a project disappears before deletion completes", async () => { const ctx = createToolContext( { project: { findUnique: vi.fn() .mockResolvedValueOnce(createProject({ status: "ACTIVE" })) .mockResolvedValueOnce(createProject({ name: "Project One" })), }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, $transaction: vi.fn().mockRejectedValue( new TRPCError({ code: "NOT_FOUND", message: "Project not found" }), ), }, { userRole: SystemRole.ADMIN, permissions: [PermissionKey.MANAGE_PROJECTS], }, ); const result = await executeTool( "delete_project", JSON.stringify({ projectId: "project_1" }), ctx, ); expect(JSON.parse(result.content)).toEqual(expect.objectContaining({ error: "Project not found: project_1", })); }); it("returns a stable assistant error when the project cannot be resolved before deletion", async () => { const transaction = vi.fn(); const ctx = createToolContext( { project: { findUnique: vi.fn().mockResolvedValue(null), findFirst: vi.fn().mockResolvedValue(null), }, $transaction: transaction, }, { userRole: SystemRole.ADMIN, permissions: [PermissionKey.MANAGE_PROJECTS], }, ); const result = await executeTool( "delete_project", JSON.stringify({ projectId: "missing-project" }), ctx, ); expect(JSON.parse(result.content)).toEqual({ error: "Project not found: missing-project", }); expect(transaction).not.toHaveBeenCalled(); }); });