import { resolvePermissions, SystemRole } from "@capakraken/shared"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { countActiveUsers, getEffectiveUserPermissions, linkUserResource, listAssignableUsers, } from "../router/user-procedure-support.js"; import { getCurrentMfaStatus, getCurrentUserProfile, getDashboardLayout, setColumnPreferences, toggleFavoriteProject, } from "../router/user-self-service-procedure-support.js"; vi.mock("../lib/audit.js", () => ({ createAuditEntry: vi.fn(), })); function createContext(db: Record, overrides: Record = {}) { return { db: db as never, dbUser: { id: "user_admin", systemRole: SystemRole.ADMIN, permissionOverrides: null, }, session: { user: { email: "admin@example.com", name: "Admin", image: null }, expires: "2099-01-01T00:00:00.000Z", }, ...overrides, }; } describe("user-procedure-support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("lists assignable users with the expected lightweight selection", async () => { const findMany = vi.fn().mockResolvedValue([ { id: "user_1", name: "Alice", email: "alice@example.com" }, ]); const result = await listAssignableUsers(createContext({ user: { findMany }, })); expect(result).toEqual([{ id: "user_1", name: "Alice", email: "alice@example.com" }]); expect(findMany).toHaveBeenCalledWith({ select: { id: true, name: true, email: true }, orderBy: { name: "asc" }, }); }); it("counts only users active within the trailing five minute window", async () => { const nowSpy = vi.spyOn(Date, "now").mockReturnValue(new Date("2026-03-30T20:00:00.000Z").valueOf()); const count = vi.fn().mockResolvedValue(4); const result = await countActiveUsers(createContext({ user: { count }, })); expect(result).toEqual({ count: 4 }); expect(count).toHaveBeenCalledWith({ where: { lastActiveAt: { gte: new Date("2026-03-30T19:55:00.000Z") } }, }); nowSpy.mockRestore(); }); it("reads the current user profile by db user id", async () => { const findUnique = vi.fn().mockResolvedValue({ id: "user_admin", name: "Admin", email: "admin@example.com", systemRole: SystemRole.ADMIN, permissionOverrides: null, createdAt: new Date("2026-03-30T08:00:00.000Z"), }); const result = await getCurrentUserProfile(createContext({ user: { findUnique }, })); expect(result).toEqual({ id: "user_admin", name: "Admin", email: "admin@example.com", systemRole: SystemRole.ADMIN, permissionOverrides: null, createdAt: new Date("2026-03-30T08:00:00.000Z"), }); expect(findUnique).toHaveBeenCalledWith({ where: { id: "user_admin" }, select: { id: true, name: true, email: true, systemRole: true, permissionOverrides: true, createdAt: true, }, }); }); it("unlinks an existing resource before linking the requested one", async () => { const userFindUnique = vi.fn().mockResolvedValue({ id: "user_1" }); const resourceFindUnique = vi.fn().mockResolvedValue({ id: "resource_1", userId: null }); const updateMany = vi.fn() .mockResolvedValueOnce({ count: 1 }) .mockResolvedValueOnce({ count: 1 }); const result = await linkUserResource(createContext({ user: { findUnique: userFindUnique }, resource: { findUnique: resourceFindUnique, updateMany }, }), { userId: "user_1", resourceId: "resource_1", }); expect(result).toEqual({ success: true }); expect(updateMany).toHaveBeenNthCalledWith(1, { where: { userId: "user_1", NOT: { id: "resource_1" } }, data: { userId: null }, }); expect(updateMany).toHaveBeenNthCalledWith(2, { where: { id: "resource_1", OR: [{ userId: null }, { userId: "user_1" }], }, data: { userId: "user_1" }, }); }); it("normalizes dashboard layouts before returning them", async () => { const findUnique = vi.fn().mockResolvedValue({ dashboardLayout: { widgets: [{ id: "peakTimes", position: { x: 0, y: 0, w: 4, h: 3 } }], }, updatedAt: new Date("2026-03-30T18:00:00.000Z"), }); const result = await getDashboardLayout(createContext({ user: { findUnique }, })); expect(result).toEqual({ layout: { version: 2, gridCols: 12, widgets: [] }, updatedAt: new Date("2026-03-30T18:00:00.000Z"), }); }); it("toggles favorite projects without losing the existing list", async () => { const findUnique = vi.fn().mockResolvedValue({ favoriteProjectIds: ["project_1"], }); const update = vi.fn().mockResolvedValue({}); const result = await toggleFavoriteProject(createContext({ user: { findUnique, update }, }), { projectId: "project_2", }); expect(result).toEqual({ favoriteProjectIds: ["project_1", "project_2"], added: true, }); expect(update).toHaveBeenCalledWith({ where: { id: "user_admin" }, data: { favoriteProjectIds: ["project_1", "project_2"] }, }); }); it("merges column preferences while preserving untouched sort and row order", async () => { const findUnique = vi.fn().mockResolvedValue({ columnPreferences: { resources: { visible: ["name", "role"], sort: { field: "name", dir: "asc" }, rowOrder: ["name", "role", "email"], }, }, }); const update = vi.fn().mockResolvedValue({ id: "user_admin" }); const result = await setColumnPreferences(createContext({ user: { findUnique, update }, }), { view: "resources", visible: ["name", "email"], }); expect(result).toEqual({ ok: true }); expect(update).toHaveBeenCalledWith({ where: { id: "user_admin" }, data: { columnPreferences: { resources: { visible: ["name", "email"], sort: { field: "name", dir: "asc" }, rowOrder: ["name", "role", "email"], }, }, }, }); }); it("returns effective permissions alongside stored overrides", async () => { const overrides = { granted: ["manageProjects"], denied: ["viewCosts"], chapterIds: ["chapter_design"], }; const findUnique = vi.fn().mockResolvedValue({ systemRole: SystemRole.MANAGER, permissionOverrides: overrides, }); const result = await getEffectiveUserPermissions(createContext({ user: { findUnique }, }), { userId: "user_2", }); expect(result).toEqual({ systemRole: SystemRole.MANAGER, effectivePermissions: Array.from(resolvePermissions(SystemRole.MANAGER, overrides)), overrides, }); }); it("reports MFA status for the current user and throws when the user no longer exists", async () => { const findUnique = vi.fn() .mockResolvedValueOnce({ totpEnabled: true }) .mockResolvedValueOnce(null); const ctx = createContext({ user: { findUnique }, }); await expect(getCurrentMfaStatus(ctx)).resolves.toEqual({ totpEnabled: true }); await expect(getCurrentMfaStatus(ctx)).rejects.toMatchObject({ code: "NOT_FOUND", message: "User not found", }); }); });