Files
Nexus/packages/api/src/__tests__/webhook-router-auth.test.ts
T
Hartmut ed4d4e4640 test(api): fill router auth and security coverage gaps
Four new test files — 27 tests total:
- role-router-auth.test.ts (8): UNAUTHORIZED/FORBIDDEN on all mutations for
  unauthenticated/USER callers; MANAGER and ADMIN happy paths
- webhook-router-auth.test.ts (6): adminProcedure guard verified for all
  six webhook procedures across USER/MANAGER/ADMIN roles
- comment-sanitization-router.test.ts (4): proves stripHtml runs before
  db.comment.create — script tags stripped, plain text and @mentions preserved
- auth-anomaly-check/route.test.ts (+5 unit tests): detectAuthAnomalies()
  unit coverage — empty window, global threshold, per-entity threshold, null
  entityId, and both anomaly types firing simultaneously

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-02 23:31:26 +02:00

164 lines
4.6 KiB
TypeScript

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<string, unknown>,
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);
});
});
});