import { SystemRole } from "@capakraken/shared"; import { describe, expect, it, vi } from "vitest"; import { webhookRouter } from "../router/webhook.js"; import { createCallerFactory } from "../trpc.js"; const createCaller = createCallerFactory(webhookRouter); function createContext( db: Record, options: { role?: SystemRole; session?: boolean; } = {}, ) { const { role = SystemRole.USER, session = true } = options; return { session: session ? { user: { email: "user@example.com", name: "User", image: null }, expires: "2099-01-01T00:00:00.000Z", } : null, db: db as never, dbUser: session ? { id: role === SystemRole.ADMIN ? "user_admin" : "user_1", systemRole: role, permissionOverrides: null, } : null, }; } describe("webhook router authorization", () => { describe("unauthenticated access", () => { it("rejects unauthenticated list call with UNAUTHORIZED", async () => { const webhookFindMany = vi.fn(); const caller = createCaller( createContext( { webhook: { findMany: webhookFindMany } }, { session: false }, ), ); await expect(caller.list()).rejects.toMatchObject({ code: "UNAUTHORIZED", message: "Authentication required", }); expect(webhookFindMany).not.toHaveBeenCalled(); }); }); describe("USER role — insufficient for admin-only procedures", () => { it("forbids USER from calling list", async () => { const webhookFindMany = vi.fn(); const caller = createCaller( createContext({ webhook: { findMany: webhookFindMany } }), ); await expect(caller.list()).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); expect(webhookFindMany).not.toHaveBeenCalled(); }); }); describe("MANAGER role — insufficient for admin-only procedures", () => { it("forbids MANAGER from calling list", async () => { const webhookFindMany = vi.fn(); const caller = createCaller( createContext( { webhook: { findMany: webhookFindMany } }, { role: SystemRole.MANAGER }, ), ); await expect(caller.list()).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); expect(webhookFindMany).not.toHaveBeenCalled(); }); it("forbids MANAGER from calling create", async () => { const webhookCreate = vi.fn(); const caller = createCaller( createContext( { webhook: { create: webhookCreate } }, { role: SystemRole.MANAGER }, ), ); await expect( caller.create({ name: "My Webhook", url: "https://example.com/hook", events: ["allocation.created"], }), ).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); expect(webhookCreate).not.toHaveBeenCalled(); }); }); describe("ADMIN role — full access to all procedures", () => { it("allows ADMIN to call list without auth error", async () => { const webhookFindMany = vi.fn().mockResolvedValue([]); const caller = createCaller( createContext( { webhook: { findMany: webhookFindMany } }, { role: SystemRole.ADMIN }, ), ); const result = await caller.list(); expect(result).toEqual([]); expect(webhookFindMany).toHaveBeenCalledTimes(1); }); it("allows ADMIN to call create without auth error", async () => { const createdWebhook = { id: "webhook_1", name: "Slack Notifications", url: "https://hooks.slack.com/services/test", secret: null, events: ["allocation.created"], isActive: true, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), }; const webhookCreate = vi.fn().mockResolvedValue(createdWebhook); const auditLogCreate = vi.fn().mockResolvedValue({}); const caller = createCaller( createContext( { webhook: { create: webhookCreate }, auditLog: { create: auditLogCreate }, }, { role: SystemRole.ADMIN }, ), ); const result = await caller.create({ name: "Slack Notifications", url: "https://hooks.slack.com/services/test", events: ["allocation.created"], }); expect(result).toMatchObject({ id: "webhook_1", name: "Slack Notifications" }); expect(webhookCreate).toHaveBeenCalledTimes(1); }); }); });