Files
claude-code-action/test/prepare-context.test.ts
Philippe Laflamme 2acd1f7011 fix: commentBody may be null (#706)
* fix: `commentBody` may be `null`

This handles the cases where `pull_request_review` events have no
comments (`commentBody` field is `null`). In those cases, the `null`
value is converted to the empty string.

The issue was testing `!commentBody` which was triggerring on empty
strings as well. This guard was removed (which is the fix), but for
clarity, the `commentBody` field was also made optional to make it clear
that the comment may be missing.

* fix: bun run format
2025-12-03 17:34:31 -08:00

312 lines
10 KiB
TypeScript

#!/usr/bin/env bun
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
import { prepareContext } from "../src/create-prompt";
import {
createMockContext,
mockIssueOpenedContext,
mockIssueAssignedContext,
mockIssueCommentContext,
mockPullRequestCommentContext,
mockPullRequestReviewContext,
mockPullRequestReviewCommentContext,
mockPullRequestReviewWithoutCommentContext,
} from "./mockContext";
const BASE_ENV = {
CLAUDE_COMMENT_ID: "12345",
GITHUB_TOKEN: "test-token",
};
describe("parseEnvVarsWithContext", () => {
let originalEnv: typeof process.env;
beforeEach(() => {
originalEnv = { ...process.env };
process.env = {};
});
afterEach(() => {
process.env = originalEnv;
});
describe("issue_comment event", () => {
describe("on issue", () => {
beforeEach(() => {
process.env = {
...BASE_ENV,
BASE_BRANCH: "main",
CLAUDE_BRANCH: "claude/issue-67890-20240101-1200",
};
});
test("should parse issue_comment event correctly", () => {
const result = prepareContext(
mockIssueCommentContext,
"12345",
"main",
"claude/issue-67890-20240101-1200",
);
expect(result.repository).toBe("test-owner/test-repo");
expect(result.claudeCommentId).toBe("12345");
expect(result.triggerPhrase).toBe("@claude");
expect(result.triggerUsername).toBe("contributor-user");
expect(result.eventData.eventName).toBe("issue_comment");
expect(result.eventData.isPR).toBe(false);
if (
result.eventData.eventName === "issue_comment" &&
!result.eventData.isPR
) {
expect(result.eventData.issueNumber).toBe("55");
expect(result.eventData.commentId).toBe("12345678");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-67890-20240101-1200",
);
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.commentBody).toBe(
"@claude can you help explain how to configure the logging system?",
);
}
});
test("should throw error when CLAUDE_BRANCH is missing", () => {
expect(() =>
prepareContext(mockIssueCommentContext, "12345", "main"),
).toThrow("CLAUDE_BRANCH is required for issue_comment event");
});
test("should throw error when BASE_BRANCH is missing", () => {
expect(() =>
prepareContext(
mockIssueCommentContext,
"12345",
undefined,
"claude/issue-67890-20240101-1200",
),
).toThrow("BASE_BRANCH is required for issue_comment event");
});
});
describe("on PR", () => {
test("should parse PR issue_comment event correctly", () => {
process.env = BASE_ENV;
const result = prepareContext(mockPullRequestCommentContext, "12345");
expect(result.eventData.eventName).toBe("issue_comment");
expect(result.eventData.isPR).toBe(true);
expect(result.triggerUsername).toBe("reviewer-user");
if (
result.eventData.eventName === "issue_comment" &&
result.eventData.isPR
) {
expect(result.eventData.prNumber).toBe("789");
expect(result.eventData.commentId).toBe("87654321");
expect(result.eventData.commentBody).toBe(
"/claude please review the changes and ensure we're not introducing any new memory issues",
);
}
});
});
});
describe("pull_request_review event", () => {
test("should parse pull_request_review event correctly", () => {
process.env = BASE_ENV;
const result = prepareContext(mockPullRequestReviewContext, "12345");
expect(result.eventData.eventName).toBe("pull_request_review");
expect(result.eventData.isPR).toBe(true);
expect(result.triggerUsername).toBe("senior-developer");
if (result.eventData.eventName === "pull_request_review") {
expect(result.eventData.prNumber).toBe("321");
expect(result.eventData.commentBody).toBe(
"@claude can you check if the error handling is comprehensive enough in this PR?",
);
}
});
});
describe("pull_request_review event without comment", () => {
test("should parse pull_request_review event correctly", () => {
process.env = BASE_ENV;
const result = prepareContext(
mockPullRequestReviewWithoutCommentContext,
"12345",
);
expect(result.eventData.eventName).toBe("pull_request_review");
expect(result.eventData.isPR).toBe(true);
expect(result.triggerUsername).toBe("senior-developer");
if (result.eventData.eventName === "pull_request_review") {
expect(result.eventData.prNumber).toBe("321");
expect(result.eventData.commentBody).toBe("");
}
});
});
describe("pull_request_review_comment event", () => {
test("should parse pull_request_review_comment event correctly", () => {
process.env = BASE_ENV;
const result = prepareContext(
mockPullRequestReviewCommentContext,
"12345",
);
expect(result.eventData.eventName).toBe("pull_request_review_comment");
expect(result.eventData.isPR).toBe(true);
expect(result.triggerUsername).toBe("code-reviewer");
if (result.eventData.eventName === "pull_request_review_comment") {
expect(result.eventData.prNumber).toBe("999");
expect(result.eventData.commentId).toBe("99988877");
expect(result.eventData.commentBody).toBe(
"/claude is this the most efficient way to implement this algorithm?",
);
}
});
});
describe("issues event", () => {
beforeEach(() => {
process.env = {
...BASE_ENV,
BASE_BRANCH: "main",
CLAUDE_BRANCH: "claude/issue-42-20240101-1200",
};
});
test("should parse issue opened event correctly", () => {
const result = prepareContext(
mockIssueOpenedContext,
"12345",
"main",
"claude/issue-42-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
expect(result.eventData.isPR).toBe(false);
expect(result.triggerUsername).toBe("john-doe");
if (
result.eventData.eventName === "issues" &&
result.eventData.eventAction === "opened"
) {
expect(result.eventData.issueNumber).toBe("42");
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-42-20240101-1200",
);
}
});
test("should parse issue assigned event correctly", () => {
const result = prepareContext(
mockIssueAssignedContext,
"12345",
"main",
"claude/issue-123-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
expect(result.eventData.isPR).toBe(false);
expect(result.triggerUsername).toBe("jane-smith");
if (
result.eventData.eventName === "issues" &&
result.eventData.eventAction === "assigned"
) {
expect(result.eventData.issueNumber).toBe("123");
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-123-20240101-1200",
);
expect(result.eventData.assigneeTrigger).toBe("@claude-bot");
}
});
test("should throw error when CLAUDE_BRANCH is missing for issues", () => {
expect(() =>
prepareContext(mockIssueOpenedContext, "12345", "main"),
).toThrow("CLAUDE_BRANCH is required for issues event");
});
test("should throw error when BASE_BRANCH is missing for issues", () => {
expect(() =>
prepareContext(
mockIssueOpenedContext,
"12345",
undefined,
"claude/issue-42-20240101-1200",
),
).toThrow("BASE_BRANCH is required for issues event");
});
test("should allow issue assigned event with prompt and no assigneeTrigger", () => {
const contextWithDirectPrompt = createMockContext({
...mockIssueAssignedContext,
inputs: {
...mockIssueAssignedContext.inputs,
assigneeTrigger: "", // No assignee trigger
prompt: "Please assess this issue", // But prompt is provided
},
});
const result = prepareContext(
contextWithDirectPrompt,
"12345",
"main",
"claude/issue-123-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
expect(result.eventData.isPR).toBe(false);
expect(result.prompt).toBe("Please assess this issue");
if (
result.eventData.eventName === "issues" &&
result.eventData.eventAction === "assigned"
) {
expect(result.eventData.issueNumber).toBe("123");
expect(result.eventData.assigneeTrigger).toBeUndefined();
}
});
test("should throw error when neither assigneeTrigger nor prompt provided for issue assigned event", () => {
const contextWithoutTriggers = createMockContext({
...mockIssueAssignedContext,
inputs: {
...mockIssueAssignedContext.inputs,
assigneeTrigger: "", // No assignee trigger
prompt: "", // No prompt
},
});
expect(() =>
prepareContext(
contextWithoutTriggers,
"12345",
"main",
"claude/issue-123-20240101-1200",
),
).toThrow("ASSIGNEE_TRIGGER is required for issue assigned event");
});
});
describe("context generation", () => {
test("should generate context without legacy fields", () => {
process.env = BASE_ENV;
const context = createMockContext({
...mockPullRequestCommentContext,
inputs: {
...mockPullRequestCommentContext.inputs,
},
});
const result = prepareContext(context, "12345");
// Verify context is created without legacy fields
expect(result.repository).toBe("test-owner/test-repo");
expect(result.claudeCommentId).toBe("12345");
expect(result.triggerPhrase).toBe("/claude");
expect((result as any).customInstructions).toBeUndefined();
expect((result as any).allowedTools).toBeUndefined();
});
});
});