diff --git a/test/comments-common.test.ts b/test/comments-common.test.ts new file mode 100644 index 000000000..b37dec97f --- /dev/null +++ b/test/comments-common.test.ts @@ -0,0 +1,74 @@ +import { describe, test, expect } from "bun:test"; +import { + SPINNER_HTML, + createJobRunLink, + createBranchLink, + createCommentBody, +} from "../src/github/operations/comments/common"; +import { GITHUB_SERVER_URL } from "../src/github/api/config"; + +describe("comments/common", () => { + describe("createJobRunLink", () => { + test("builds a markdown link to the workflow run", () => { + const result = createJobRunLink("anthropics", "claude-code-action", "42"); + expect(result).toBe( + `[View job run](${GITHUB_SERVER_URL}/anthropics/claude-code-action/actions/runs/42)`, + ); + }); + + test("honors GITHUB_SERVER_URL (GHES) rather than hardcoding github.com", () => { + // The link is built from the configured server URL, so it must point at + // whatever GITHUB_SERVER_URL resolves to (github.com by default, a GHES + // host in enterprise setups). + expect(createJobRunLink("o", "r", "1")).toContain(GITHUB_SERVER_URL); + }); + }); + + describe("createBranchLink", () => { + test("builds a leading-newline markdown link to the branch tree", () => { + const result = createBranchLink( + "anthropics", + "claude-code-action", + "feature/x", + ); + expect(result).toBe( + `\n[View branch](${GITHUB_SERVER_URL}/anthropics/claude-code-action/tree/feature/x)`, + ); + }); + + test("prefixes the link with a newline so it renders on its own line", () => { + expect(createBranchLink("o", "r", "main").startsWith("\n")).toBe(true); + }); + }); + + describe("createCommentBody", () => { + test("includes the spinner, the working message, and the job run link", () => { + const jobRunLink = createJobRunLink("o", "r", "7"); + const body = createCommentBody(jobRunLink); + + expect(body).toContain(SPINNER_HTML); + expect(body).toContain("Claude Code is working…"); + expect(body).toContain(jobRunLink); + }); + + test("omits the branch link when none is provided (defaults to empty)", () => { + const body = createCommentBody(createJobRunLink("o", "r", "7")); + expect(body).not.toContain("View branch"); + // No trailing branch content: body ends with the job run link. + expect(body.endsWith(")")).toBe(true); + }); + + test("appends the branch link when provided", () => { + const jobRunLink = createJobRunLink("o", "r", "7"); + const branchLink = createBranchLink("o", "r", "feature/x"); + const body = createCommentBody(jobRunLink, branchLink); + + expect(body).toContain(jobRunLink); + expect(body).toContain(branchLink); + // The branch link (with its leading newline) comes after the job run link. + expect(body.indexOf(branchLink)).toBeGreaterThan( + body.indexOf(jobRunLink), + ); + }); + }); +}); diff --git a/test/create-prompt.test.ts b/test/create-prompt.test.ts index cf5a44ae5..8ff510676 100644 --- a/test/create-prompt.test.ts +++ b/test/create-prompt.test.ts @@ -6,8 +6,10 @@ import { getEventTypeAndContext, buildAllowedToolsString, buildDisallowedToolsString, + prepareContext, } from "../src/create-prompt"; import type { PreparedContext } from "../src/create-prompt"; +import { createMockContext } from "./mockContext"; beforeAll(() => { process.env.GITHUB_ACTION_PATH = "/test/action/path"; @@ -1270,3 +1272,83 @@ describe("buildDisallowedToolsString", () => { expect(result).toBe("BadTool1,BadTool2"); }); }); + +describe("prepareContext validation errors", () => { + const commentId = "12345"; + + test("throws on an unsupported event type", () => { + const context = createMockContext({ + eventName: "deployment_status" as any, + }); + + expect(() => prepareContext(context, commentId)).toThrow( + "Unsupported event type: deployment_status", + ); + }); + + test("pull_request event requires a PR number (isPR must be true)", () => { + const context = createMockContext({ + eventName: "pull_request", + eventAction: "opened", + isPR: false, + }); + + expect(() => prepareContext(context, commentId)).toThrow( + "PR_NUMBER is required for pull_request event", + ); + }); + + test("pull_request_review event requires a PR number", () => { + const context = createMockContext({ + eventName: "pull_request_review", + isPR: false, + payload: { + review: { body: "please fix", user: { login: "user1" } }, + } as any, + }); + + expect(() => prepareContext(context, commentId)).toThrow( + "PR_NUMBER is required for pull_request_review event", + ); + }); + + test("issues event requires an event action", () => { + const context = createMockContext({ + eventName: "issues", + eventAction: "", + isPR: false, + payload: { issue: { user: { login: "user1" } } } as any, + }); + + expect(() => prepareContext(context, commentId)).toThrow( + "GITHUB_EVENT_ACTION is required for issues event", + ); + }); + + test("issues event rejects an unsupported action", () => { + const context = createMockContext({ + eventName: "issues", + eventAction: "deleted", + isPR: false, + payload: { issue: { user: { login: "user1" } } } as any, + }); + + expect(() => + prepareContext(context, commentId, "main", "claude/issue-1"), + ).toThrow("Unsupported issue action: deleted"); + }); + + test("issue_comment on an issue requires a claude branch", () => { + const context = createMockContext({ + eventName: "issue_comment", + isPR: false, + payload: { + comment: { id: 999, body: "@claude help", user: { login: "user1" } }, + } as any, + }); + + expect(() => prepareContext(context, commentId)).toThrow( + "CLAUDE_BRANCH is required for issue_comment event", + ); + }); +});