This commit is contained in:
commit
0ba9b9f95c
17 changed files with 705 additions and 0 deletions
54
test/providers.test.ts
Normal file
54
test/providers.test.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import { normalizeGithubEvent } from "../src/providers/github";
|
||||
import { normalizeJojoEvent } from "../src/providers/jojo";
|
||||
|
||||
const repository = {
|
||||
name: "git-webhooks",
|
||||
full_name: "peezy-tech/git-webhooks",
|
||||
clone_url: "https://example.test/peezy-tech/git-webhooks.git",
|
||||
ssh_url: "git@example.test:peezy-tech/git-webhooks.git",
|
||||
default_branch: "main",
|
||||
owner: { login: "peezy-tech" },
|
||||
};
|
||||
|
||||
describe("provider normalization", () => {
|
||||
test("normalizes GitHub push events", () => {
|
||||
const event = normalizeGithubEvent({
|
||||
providerEvent: "push",
|
||||
deliveryId: "delivery-1",
|
||||
receivedAt: "2026-05-12T00:00:00.000Z",
|
||||
payload: {
|
||||
ref: "refs/heads/main",
|
||||
before: "before",
|
||||
after: "after",
|
||||
repository,
|
||||
sender: { login: "peezy", html_url: "https://github.com/peezy" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(event.provider).toBe("github");
|
||||
expect(event.event).toBe("push");
|
||||
expect(event.repo?.fullName).toBe("peezy-tech/git-webhooks");
|
||||
expect(event.sender?.username).toBe("peezy");
|
||||
});
|
||||
|
||||
test("normalizes jojo push events", () => {
|
||||
const event = normalizeJojoEvent({
|
||||
providerEvent: "push",
|
||||
deliveryId: "delivery-2",
|
||||
receivedAt: "2026-05-12T00:00:00.000Z",
|
||||
payload: {
|
||||
ref: "refs/heads/main",
|
||||
before: "before",
|
||||
after: "after",
|
||||
repository,
|
||||
sender: { username: "peezy", html_url: "https://jojo.build/peezy" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(event.provider).toBe("jojo");
|
||||
expect(event.event).toBe("push");
|
||||
expect(event.repo?.owner).toBe("peezy-tech");
|
||||
expect(event.sender?.username).toBe("peezy");
|
||||
});
|
||||
});
|
||||
60
test/server.test.ts
Normal file
60
test/server.test.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { mkdtemp, readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createHandler } from "../src/server";
|
||||
import { hmacSha256Hex } from "../src/signatures";
|
||||
|
||||
async function signedRequest(path: string, provider: "github" | "jojo", secret: string, body: unknown): Promise<Request> {
|
||||
const raw = JSON.stringify(body);
|
||||
const digest = await hmacSha256Hex(secret, raw);
|
||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||
if (provider === "github") {
|
||||
headers["x-hub-signature-256"] = `sha256=${digest}`;
|
||||
headers["x-github-event"] = "push";
|
||||
headers["x-github-delivery"] = "github-delivery";
|
||||
} else {
|
||||
headers["x-forgejo-signature-256"] = `sha256=${digest}`;
|
||||
headers["x-forgejo-event"] = "push";
|
||||
headers["x-forgejo-delivery"] = "jojo-delivery";
|
||||
}
|
||||
return new Request(`http://localhost${path}`, { method: "POST", headers, body: raw });
|
||||
}
|
||||
|
||||
describe("server", () => {
|
||||
test("healthz returns ok", async () => {
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "git-webhooks-")) });
|
||||
const response = await handler(new Request("http://localhost/healthz"));
|
||||
expect(response.status).toBe(200);
|
||||
expect(await response.text()).toBe("ok\n");
|
||||
});
|
||||
|
||||
test("rejects invalid signatures", async () => {
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir: await mkdtemp(join(tmpdir(), "git-webhooks-")) });
|
||||
const response = await handler(new Request("http://localhost/git-webhooks/github", {
|
||||
method: "POST",
|
||||
headers: { "x-hub-signature-256": "sha256=bad" },
|
||||
body: "{}",
|
||||
}));
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("accepts jojo main pushes and queues a job", async () => {
|
||||
const dataDir = await mkdtemp(join(tmpdir(), "git-webhooks-"));
|
||||
const handler = createHandler({ githubSecret: "gh", jojoSecret: "jojo", dataDir });
|
||||
const request = await signedRequest("/git-webhooks/jojo", "jojo", "jojo", {
|
||||
ref: "refs/heads/main",
|
||||
after: "abc123",
|
||||
repository: {
|
||||
name: "git-webhooks",
|
||||
full_name: "peezy-tech/git-webhooks",
|
||||
owner: { username: "peezy-tech" },
|
||||
},
|
||||
});
|
||||
|
||||
const response = await handler(request);
|
||||
expect(response.status).toBe(202);
|
||||
expect(await readFile(join(dataDir, "events.jsonl"), "utf8")).toContain("\"provider\":\"jojo\"");
|
||||
expect(await readFile(join(dataDir, "jobs.jsonl"), "utf8")).toContain("\"kind\":\"main_push\"");
|
||||
});
|
||||
});
|
||||
21
test/signatures.test.ts
Normal file
21
test/signatures.test.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import { hmacSha256Hex, verifyGithubSignature, verifyJojoSignature } from "../src/signatures";
|
||||
|
||||
describe("webhook signatures", () => {
|
||||
test("verifies GitHub sha256 signatures", async () => {
|
||||
const body = JSON.stringify({ ok: true });
|
||||
const digest = await hmacSha256Hex("secret", body);
|
||||
|
||||
expect(await verifyGithubSignature("secret", body, `sha256=${digest}`)).toBe(true);
|
||||
expect(await verifyGithubSignature("wrong", body, `sha256=${digest}`)).toBe(false);
|
||||
});
|
||||
|
||||
test("verifies jojo Forgejo/Gitea signature headers", async () => {
|
||||
const body = JSON.stringify({ ok: true });
|
||||
const digest = await hmacSha256Hex("secret", body);
|
||||
const headers = new Headers({ "x-forgejo-signature-256": `sha256=${digest}` });
|
||||
|
||||
expect(await verifyJojoSignature("secret", body, headers)).toBe(true);
|
||||
expect(await verifyJojoSignature("wrong", body, headers)).toBe(false);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue