mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
feat: add gitea/forgejo support
This commit is contained in:
parent
ace036c209
commit
aec0d1a257
43 changed files with 683 additions and 130 deletions
142
src/__tests__/providers.test.ts
Normal file
142
src/__tests__/providers.test.ts
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import { describe, it, expect } from "bun:test";
|
||||
import { createHmac } from "crypto";
|
||||
import { detectProvider } from "../providers";
|
||||
import { verifyGiteaSignature } from "../providers/gitea/verify";
|
||||
import { parseGiteaEvent } from "../providers/gitea/parse";
|
||||
import { formatEvent } from "../formatters";
|
||||
import type { Route } from "../types";
|
||||
|
||||
function giteaSign(body: string, secret: string): string {
|
||||
return createHmac("sha256", secret).update(body).digest("hex");
|
||||
}
|
||||
|
||||
describe("provider detection", () => {
|
||||
it("detects github by X-GitHub-Event header", () => {
|
||||
const p = detectProvider({ "x-github-event": "push" });
|
||||
expect(p?.id).toBe("github");
|
||||
});
|
||||
|
||||
it("detects gitea by X-Gitea-Event header", () => {
|
||||
const p = detectProvider({ "x-gitea-event": "push" });
|
||||
expect(p?.id).toBe("gitea");
|
||||
});
|
||||
|
||||
it("prefers gitea when both gitea and github headers are present", () => {
|
||||
// Gitea webhooks also send GitHub-compatible headers (X-GitHub-Event,
|
||||
// X-Hub-Signature-256, X-Gogs-*), so detection must not misclassify them.
|
||||
const p = detectProvider({
|
||||
"x-gitea-event": "push",
|
||||
"x-github-event": "push",
|
||||
"x-gitea-signature": "abc",
|
||||
"x-hub-signature-256": "sha256=abc",
|
||||
});
|
||||
expect(p?.id).toBe("gitea");
|
||||
});
|
||||
|
||||
it("returns null for unknown providers", () => {
|
||||
expect(detectProvider({})).toBeNull();
|
||||
expect(detectProvider({ "x-gitlab-event": "Push Hook" })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("gitea signature", () => {
|
||||
const secret = "gitea-secret";
|
||||
|
||||
it("accepts a valid hex HMAC-SHA256 signature (no sha256= prefix)", async () => {
|
||||
const body = '{"ref":"refs/heads/main"}';
|
||||
const sig = giteaSign(body, secret);
|
||||
expect(await verifyGiteaSignature(body, sig, secret)).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects an invalid signature", async () => {
|
||||
expect(await verifyGiteaSignature("body", "deadbeef", secret)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects when signature or secret is missing", async () => {
|
||||
expect(await verifyGiteaSignature("body", undefined, secret)).toBe(false);
|
||||
expect(await verifyGiteaSignature("body", "abc", undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("gitea event parsing", () => {
|
||||
it("parses a push event and sets the provider", () => {
|
||||
const event = parseGiteaEvent(
|
||||
{ "x-gitea-event": "push", "x-gitea-delivery": "d-1" },
|
||||
JSON.stringify({
|
||||
ref: "refs/heads/main",
|
||||
compare_url: "https://git.example.com/org/repo/compare/abc...def",
|
||||
pusher: { login: "octo", html_url: "https://git.example.com/octo" },
|
||||
repository: { full_name: "org/repo", html_url: "https://git.example.com/org/repo" },
|
||||
}),
|
||||
);
|
||||
expect(event).not.toBeNull();
|
||||
expect(event!.provider).toBe("gitea");
|
||||
expect(event!.event).toBe("push");
|
||||
expect(event!.deliveryId).toBe("d-1");
|
||||
expect(event!.payload.compare).toBe("https://git.example.com/org/repo/compare/abc...def");
|
||||
expect((event!.payload.sender as { login?: string }).login).toBe("octo");
|
||||
});
|
||||
|
||||
it("maps pull_request_comment to pull_request_review_comment and pulls the PR", () => {
|
||||
const event = parseGiteaEvent(
|
||||
{ "x-gitea-event": "pull_request_comment" },
|
||||
JSON.stringify({
|
||||
action: "created",
|
||||
issue: { number: 7, title: "Add feature", html_url: "https://git.example.com/org/repo/pulls/7" },
|
||||
comment: { body: "looks good", line: 12, html_url: "https://git.example.com/org/repo/pulls/7#issuecomment-1" },
|
||||
repository: { full_name: "org/repo" },
|
||||
sender: { login: "octo" },
|
||||
}),
|
||||
);
|
||||
expect(event!.event).toBe("pull_request_review_comment");
|
||||
const pr = event!.payload.pull_request as { number?: number; title?: string };
|
||||
expect(pr.number).toBe(7);
|
||||
const comment = event!.payload.comment as { position?: number };
|
||||
expect(comment.position).toBe(12);
|
||||
});
|
||||
|
||||
it("copies top-level commit_id onto the comment for commit_comment events", () => {
|
||||
const event = parseGiteaEvent(
|
||||
{ "x-gitea-event": "commit_comment" },
|
||||
JSON.stringify({
|
||||
action: "created",
|
||||
commit_id: "abcd1234ef",
|
||||
comment: { body: "why?", html_url: "https://git.example.com/org/repo/commit/abcd1234ef#commitcomment-1" },
|
||||
repository: { full_name: "org/repo" },
|
||||
sender: { login: "octo" },
|
||||
}),
|
||||
);
|
||||
expect(event!.event).toBe("commit_comment");
|
||||
expect((event!.payload.comment as { commit_id?: string }).commit_id).toBe("abcd1234ef");
|
||||
});
|
||||
|
||||
it("returns null for missing event header or invalid JSON", () => {
|
||||
expect(parseGiteaEvent({}, "{}")).toBeNull();
|
||||
expect(parseGiteaEvent({ "x-gitea-event": "push" }, "not json")).toBeNull();
|
||||
});
|
||||
|
||||
it("formats a normalized gitea push with gitea commit links", () => {
|
||||
const event = parseGiteaEvent(
|
||||
{ "x-gitea-event": "push" },
|
||||
JSON.stringify({
|
||||
ref: "refs/heads/main",
|
||||
compare_url: "https://git.example.com/org/repo/compare/abc...def",
|
||||
pusher: { login: "octo", html_url: "https://git.example.com/octo" },
|
||||
commits: [{ id: "abcd1234ef", message: "fix stuff", added: [], removed: [], modified: [] }],
|
||||
repository: { full_name: "org/repo", html_url: "https://git.example.com/org/repo" },
|
||||
}),
|
||||
);
|
||||
const route: Route = {
|
||||
id: "test",
|
||||
name: "Test",
|
||||
enabled: true,
|
||||
filters: [],
|
||||
targets: [{ channelId: "111" }],
|
||||
};
|
||||
const msg = formatEvent(route, event!);
|
||||
expect(msg.title).toContain("org/repo");
|
||||
expect(msg.fields![0].value).toBe(
|
||||
"[`abcd123`](https://git.example.com/org/repo/commit/abcd1234ef) fix stuff",
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue