import { createHmac } from "node:crypto"; import { TRPCError } from "@trpc/server"; import { describe, expect, it, vi } from "vitest"; import { buildWebhookCreateData, buildWebhookTestRequest, buildWebhookUpdateData, loadWebhookOrThrow, sendWebhookTestRequest, } from "../router/webhook-support.js"; describe("webhook support", () => { it("builds create and sparse update payloads", () => { expect(buildWebhookCreateData({ name: "Primary", url: "https://example.com/webhook", secret: "secret", events: ["project.created"], isActive: true, })).toEqual({ name: "Primary", url: "https://example.com/webhook", secret: "secret", events: ["project.created"], isActive: true, }); expect(buildWebhookUpdateData({ isActive: false, secret: null, })).toEqual({ isActive: false, secret: null, }); }); it("loads a webhook or throws", async () => { const db = { webhook: { findUnique: vi.fn().mockResolvedValue(null), }, } as never; await expect(loadWebhookOrThrow(db, "missing")).rejects.toBeInstanceOf(TRPCError); }); it("builds a signed test request when a secret exists", async () => { const timestamp = "2026-03-31T12:00:00.000Z"; const request = await buildWebhookTestRequest({ id: "wh_1", name: "Primary", secret: "topsecret", }, timestamp); const expectedBody = JSON.stringify({ event: "webhook.test", timestamp, payload: { webhookId: "wh_1", webhookName: "Primary", message: "This is a test payload from CapaKraken.", }, }); expect(request.body).toBe(expectedBody); expect(request.headers["X-Webhook-Signature"]).toBe( createHmac("sha256", "topsecret").update(expectedBody).digest("hex"), ); }); it("reports fetch failures as unsuccessful test results", async () => { const fetchImpl = vi.fn().mockRejectedValue(new Error("network down")); await expect(sendWebhookTestRequest({ id: "wh_1", name: "Primary", url: "https://example.com/webhook", secret: null, }, { fetchImpl, timestamp: "2026-03-31T12:00:00.000Z", timeoutMs: 10, })).resolves.toEqual({ success: false, statusCode: 0, statusText: "network down", }); }); });