Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
import {
createServer,
type IncomingMessage,
type Server,
type ServerResponse,
} from "node:http";
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
createWebhookService,
MemoryDeadLetterSink,
MemoryEndpointStore,
resetWebhookInstruments,
WEBHOOK_CODES,
} from "../../src/index.js";
async function startReceiver(
handler: (req: IncomingMessage, res: ServerResponse) => void
) {
const server: Server = createServer((req, res) => {
req.on("data", () => undefined);
req.on("end", () => handler(req, res));
});
await new Promise<void>((resolve) =>
server.listen(0, "127.0.0.1", resolve)
);
const address = server.address();
if (address === null || typeof address === "string") {
throw new Error("failed to bind");
}
return {
url: `http://127.0.0.1:${address.port}/hooks`,
close: () =>
new Promise<void>((resolve, reject) => {
server.close((err) => (err ? reject(err) : resolve()));
}),
};
}
function noopLogger() {
return {
debug: () => undefined,
info: () => undefined,
warn: () => undefined,
error: () => undefined,
};
}
describe("integration: webhook dead letter", () => {
it("5x 500 lands in dead letter", { timeout: 30_000 }, async () => {
let attempts = 0;
const receiver = await startReceiver((_, res) => {
attempts++;
res.writeHead(500);
res.end("nope");
});
try {
resetWebhookInstruments();
const sink = new MemoryDeadLetterSink();
const store = new MemoryEndpointStore({
clock: () => 0,
idFactory: () => "ep-1",
});
const service = createWebhookService({
endpointStore: store,
deadLetterSink: sink,
maxAttempts: 5,
initialRetryDelayMs: 1,
maxRetryDelayMs: 30,
idempotencyGuard: null,
logger: noopLogger(),
clock: () => 0,
fetch: globalThis.fetch.bind(globalThis),
});
await service.register({
tenantId: null,
url: receiver.url,
secret: "shared-secret",
events: ["evt"],
});
await expect(
service.send("ep-1", "evt", { x: 1 })
).rejects.toMatchObject({
webhookCode: WEBHOOK_CODES.DELIVERY_EXHAUSTED,
});
expect(attempts).toBe(5);
const dl = await sink.list({ endpointId: "ep-1" });
expect(dl).toHaveLength(1);
const entry = dl[0];
if (!entry) throw new Error("missing dl entry");
expect(entry.event).toBe("evt");
expect(entry.endpointId).toBe("ep-1");
expect(entry.payload.event).toBe("evt");
} finally {
await receiver.close();
}
});
});