Files
CapaKraken/packages/api/src/__tests__/webhook-support.test.ts
T

90 lines
2.3 KiB
TypeScript

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