mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
BREAKING CHANGES: - Remove review mode entirely - now handled via slash commands in agent mode - Remove all deprecated backward compatibility fields (mode, anthropic_model, override_prompt, direct_prompt) - Simplify mode detection: prompt overrides everything, then @claude mentions trigger tag mode, default is agent mode - Remove slash command resolution from GitHub Action - Claude Code handles natively - Remove variable substitution - prompts passed through as-is Architecture changes: - Only two modes now: tag (for @claude mentions) and agent (everything else) - Agent mode is the default for all events including PRs - Users configure behavior via prompts/slash commands (e.g. /review) - GitHub Action is now a thin wrapper that passes prompts to Claude Code - Mode names changed: 'experimental-review' → removed entirely This aligns with the philosophy that the GitHub Action should do minimal work and delegate to Claude Code for all intelligent behavior.
303 lines
9.8 KiB
TypeScript
303 lines
9.8 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,
|
|
} 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_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("optional fields", () => {
|
|
test("should include custom instructions when provided", () => {
|
|
process.env = BASE_ENV;
|
|
const contextWithCustomInstructions = createMockContext({
|
|
...mockPullRequestCommentContext,
|
|
inputs: {
|
|
...mockPullRequestCommentContext.inputs,
|
|
customInstructions: "Be concise",
|
|
},
|
|
});
|
|
const result = prepareContext(contextWithCustomInstructions, "12345");
|
|
|
|
expect(result.customInstructions).toBe("Be concise");
|
|
});
|
|
|
|
test("should include allowed tools when provided", () => {
|
|
process.env = BASE_ENV;
|
|
const contextWithAllowedTools = createMockContext({
|
|
...mockPullRequestCommentContext,
|
|
inputs: {
|
|
...mockPullRequestCommentContext.inputs,
|
|
allowedTools: ["Tool1", "Tool2"],
|
|
},
|
|
});
|
|
const result = prepareContext(contextWithAllowedTools, "12345");
|
|
|
|
expect(result.allowedTools).toBe("Tool1,Tool2");
|
|
});
|
|
});
|
|
});
|