import { AllocationStatus, SystemRole } from "@planarchy/shared"; import { TRPCError } from "@trpc/server"; import { describe, expect, it, vi } from "vitest"; import { roleRouter } from "../router/role.js"; import { createCallerFactory } from "../trpc.js"; vi.mock("../sse/event-bus.js", () => ({ emitRoleCreated: vi.fn(), emitRoleDeleted: vi.fn(), emitRoleUpdated: vi.fn(), })); const createCaller = createCallerFactory(roleRouter); function createManagerCaller(db: Record) { return createCaller({ session: { user: { email: "manager@example.com", name: "Manager", image: null }, expires: "2026-03-13T00:00:00.000Z", }, db: db as never, dbUser: { id: "user_1", systemRole: SystemRole.MANAGER, permissionOverrides: null, }, }); } describe("role router planning counts", () => { it("reports planning entry counts for roles", async () => { const db = { role: { findMany: vi.fn().mockResolvedValue([ { id: "role_fx", name: "FX", description: null, color: "#111111", isActive: true, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), _count: { resourceRoles: 2 }, }, ]), }, allocation: { findMany: vi.fn().mockResolvedValue([ { id: "legacy_demand", resourceId: null, projectId: "project_1", startDate: new Date("2026-03-17"), endDate: new Date("2026-03-18"), hoursPerDay: 8, percentage: 100, role: "FX", roleId: "role_fx", isPlaceholder: true, headcount: 2, dailyCostCents: 0, status: AllocationStatus.PROPOSED, metadata: {}, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), }, ]), }, demandRequirement: { findMany: vi.fn().mockResolvedValue([ { id: "demand_1", projectId: "project_1", startDate: new Date("2026-03-17"), endDate: new Date("2026-03-18"), hoursPerDay: 8, percentage: 100, role: "FX", roleId: "role_fx", headcount: 2, status: AllocationStatus.PROPOSED, metadata: {}, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), }, ]), }, assignment: { findMany: vi.fn().mockResolvedValue([ { id: "assignment_1", demandRequirementId: null, resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-03-19"), endDate: new Date("2026-03-20"), hoursPerDay: 8, percentage: 100, role: "FX Lead", roleId: "role_fx", dailyCostCents: 32000, status: AllocationStatus.ACTIVE, metadata: {}, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), }, ]), }, }; const caller = createManagerCaller(db); const result = await caller.list({}); expect(result).toHaveLength(1); expect(result[0]?._count.resourceRoles).toBe(2); expect(result[0]?._count.allocations).toBe(2); }); it("blocks deleting a role that is only used by explicit demand or assignment rows", async () => { const db = { role: { findUnique: vi.fn().mockResolvedValue({ id: "role_fx", name: "FX", description: null, color: "#111111", isActive: true, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), _count: { resourceRoles: 0 }, }), delete: vi.fn(), }, allocation: { findMany: vi.fn().mockResolvedValue([]), }, demandRequirement: { findMany: vi.fn().mockResolvedValue([ { id: "demand_1", projectId: "project_1", startDate: new Date("2026-03-17"), endDate: new Date("2026-03-18"), hoursPerDay: 8, percentage: 100, role: "FX", roleId: "role_fx", headcount: 1, status: AllocationStatus.PROPOSED, metadata: {}, createdAt: new Date("2026-03-13"), updatedAt: new Date("2026-03-13"), }, ]), }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, }; const caller = createManagerCaller(db); await expect(caller.delete({ id: "role_fx" })).rejects.toMatchObject({ code: "PRECONDITION_FAILED", } satisfies Partial); expect(db.role.delete).not.toHaveBeenCalled(); }); });