Files
PaperClipAI/server/src/services/chat-github-events.test.ts
T

78 lines
6.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { githubAutomaticIssueEvent, githubBodyMentionsBot, githubExplicitMentionEvent } from "./chat-github-events.js";
function payload() {
return {
action: "opened", repository: { id: 12, full_name: "Test/Repo" },
sender: { id: 77, login: "sender" },
issue: { number: 2, title: "Question", body: null, user: { id: 42, login: "author", type: "User" }, labels: [{ name: "triage" }] },
};
}
describe("GitHub issue event parsing", () => {
it("preserves author versus sender authority and bounds untrusted content", () => {
const input = payload();
expect(githubAutomaticIssueEvent(input, "delivery")).toMatchObject({
event: "issue_opened", deliveryId: "delivery", repositoryId: "12", repository: "test/repo",
issueNumber: 2, body: "", author: { id: "42", isBot: false }, sender: { id: "77" }, labels: ["triage"],
});
expect(githubAutomaticIssueEvent({ ...input, issue: { ...input.issue, body: "x".repeat(30000) } }, "delivery")!.body).toHaveLength(24000);
});
it("rejects pull requests, other actions, malformed repositories and unsafe numeric IDs", () => {
const input = payload();
expect(githubAutomaticIssueEvent({ ...input, issue: { ...input.issue, pull_request: {} } }, "delivery")).toBeNull();
expect(githubAutomaticIssueEvent({ ...input, action: "edited" }, "delivery")).toBeNull();
expect(githubAutomaticIssueEvent({ ...input, repository: { id: 12, full_name: "test/repo/extra" } }, "delivery")).toBeNull();
expect(githubAutomaticIssueEvent({ ...input, sender: { id: Number.MAX_SAFE_INTEGER + 1, login: "sender" } }, "delivery")).toBeNull();
expect(githubAutomaticIssueEvent({ ...input, issue: { ...input.issue, number: 0 } }, "delivery")).toBeNull();
});
});
describe("explicit GitHub mentions", () => {
it.each(["@maya help", "@MAYA[bot] help", "Hi, @maya."])("recognizes the bot's exact bare and bot identity: %s", (body) => {
expect(githubBodyMentionsBot(body, "maya[bot]")).toBe(true);
});
it.each(["@maya-other", "@mayabot", "x@maya", "@someone"])("does not match a different identity: %s", (body) => {
expect(githubBodyMentionsBot(body, "maya[bot]")).toBe(false);
});
it.each(["issues", "pull_request", "issue_comment", "pull_request_review_comment", "pull_request_review"])("normalizes a %s request into the matching issue or PR thread", (event) => {
const issue = { ...payload().issue, body: "@maya help" };
const comment = { id: 123, body: "@maya[bot] help", in_reply_to_id: 99 };
const input = {
...payload(), action: event === "pull_request_review" ? "submitted" : event.includes("comment") ? "created" : "opened",
issue, pull_request: issue, comment, review: comment,
};
const mention = githubExplicitMentionEvent(event, input, "delivery", "maya[bot]");
expect(mention).toMatchObject({ sender: { id: "77", login: "sender" }, edited: false });
expect(mention!.threadId).toBe(`github:Test/Repo:${event === "issues" || event === "issue_comment" ? "issue:" : ""}2${event === "pull_request_review_comment" ? ":rc:99" : ""}`);
});
it("puts PR conversation comments in the PR thread, and preserves the SDK comment identity", () => {
const input = { ...payload(), action: "created", issue: { ...payload().issue, pull_request: {} }, comment: { id: 123, body: "@maya help" } };
expect(githubExplicitMentionEvent("issue_comment", input, "delivery", "maya")).toMatchObject({ threadId: "github:Test/Repo:2", messageId: "123", url: "https://github.com/test/repo/pull/2#issuecomment-123" });
});
it.each(["issues", "pull_request", "issue_comment", "pull_request_review_comment", "pull_request_review"])("recognizes a newly added mention in an edited %s body", (event) => {
const target = { ...payload().issue, body: "@maya help" };
const content = { id: 123, body: "@maya help" };
const input = { ...payload(), action: "edited", issue: target, pull_request: target, comment: content, review: content, changes: { body: { from: null } } };
expect(githubExplicitMentionEvent(event, input, "edit", "maya")).toMatchObject({ edited: true, messageId: "mention-event:edit", receiptReactionSupported: false });
});
it("admits an edit adding a mention under the editor's identity, but not a persistent mention", () => {
const input = { ...payload(), action: "edited", issue: { ...payload().issue, body: "@maya help" }, changes: { body: { from: "Old description" } } };
expect(githubExplicitMentionEvent("issues", input, "edit-delivery", "maya")).toMatchObject({ edited: true, messageId: "mention-event:edit-delivery", sender: { id: "77" } });
expect(githubExplicitMentionEvent("issues", { ...input, changes: { body: { from: "@maya[bot] old request" } } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", { ...input, changes: { title: { from: "Old title" } } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", { ...input, changes: undefined }, "delivery", "maya")).toBeNull();
});
it.each(["synchronize", "reopened", "ready_for_review", "closed", "deleted"])("does not replay a description mention on %s", (action) => {
const input = { ...payload(), action, pull_request: { ...payload().issue, body: "@maya help" } };
expect(githubExplicitMentionEvent("pull_request", input, "delivery", "maya")).toBeNull();
});
it("ignores bot senders, title-only mentions, malformed payloads and unsafe IDs", () => {
const input = { ...payload(), issue: { ...payload().issue, body: "@maya help" } };
expect(githubExplicitMentionEvent("issues", { ...input, sender: { ...input.sender, type: "Bot" } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", { ...input, issue: { ...input.issue, body: null, title: "@maya" } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", { ...input, repository: { id: 1, full_name: "wrong/repo/extra" } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", { ...input, sender: { id: Number.MAX_SAFE_INTEGER + 1, login: "sender" } }, "delivery", "maya")).toBeNull();
expect(githubExplicitMentionEvent("issues", input, "", "maya")).toBeNull();
});
});