mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
* enable track_progress for comments * refactor: pass mode explicitly to prepareMcpConfig Update prepareMcpConfig to receive the mode parameter from its callers instead of detecting agent mode by checking context.inputs.prompt. This makes mode determination explicit and controlled by the caller. Also update all test cases to include the required mode parameter and fix agent mode test expectations to match new behavior where MCP config is only included when tools are explicitly allowed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix test --------- Co-authored-by: Claude <noreply@anthropic.com>
229 lines
7.1 KiB
TypeScript
229 lines
7.1 KiB
TypeScript
import {
|
|
describe,
|
|
test,
|
|
expect,
|
|
beforeEach,
|
|
afterEach,
|
|
spyOn,
|
|
mock,
|
|
} from "bun:test";
|
|
import { agentMode } from "../../src/modes/agent";
|
|
import type { GitHubContext } from "../../src/github/context";
|
|
import { createMockContext, createMockAutomationContext } from "../mockContext";
|
|
import * as core from "@actions/core";
|
|
import * as gitConfig from "../../src/github/operations/git-config";
|
|
|
|
describe("Agent Mode", () => {
|
|
let mockContext: GitHubContext;
|
|
let exportVariableSpy: any;
|
|
let setOutputSpy: any;
|
|
let configureGitAuthSpy: any;
|
|
|
|
beforeEach(() => {
|
|
mockContext = createMockAutomationContext({
|
|
eventName: "workflow_dispatch",
|
|
});
|
|
exportVariableSpy = spyOn(core, "exportVariable").mockImplementation(
|
|
() => {},
|
|
);
|
|
setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {});
|
|
// Mock configureGitAuth to prevent actual git commands from running
|
|
configureGitAuthSpy = spyOn(
|
|
gitConfig,
|
|
"configureGitAuth",
|
|
).mockImplementation(async () => {
|
|
// Do nothing - prevent actual git config modifications
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
exportVariableSpy?.mockClear();
|
|
setOutputSpy?.mockClear();
|
|
configureGitAuthSpy?.mockClear();
|
|
exportVariableSpy?.mockRestore();
|
|
setOutputSpy?.mockRestore();
|
|
configureGitAuthSpy?.mockRestore();
|
|
});
|
|
|
|
test("agent mode has correct properties", () => {
|
|
expect(agentMode.name).toBe("agent");
|
|
expect(agentMode.description).toBe(
|
|
"Direct automation mode for explicit prompts",
|
|
);
|
|
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
|
expect(agentMode.getAllowedTools()).toEqual([]);
|
|
expect(agentMode.getDisallowedTools()).toEqual([]);
|
|
});
|
|
|
|
test("prepareContext returns minimal data", () => {
|
|
const context = agentMode.prepareContext(mockContext);
|
|
|
|
expect(context.mode).toBe("agent");
|
|
expect(context.githubContext).toBe(mockContext);
|
|
// Agent mode doesn't use comment tracking or branch management
|
|
expect(Object.keys(context)).toEqual(["mode", "githubContext"]);
|
|
});
|
|
|
|
test("agent mode only triggers when prompt is provided", () => {
|
|
// Should NOT trigger for automation events without prompt
|
|
const workflowDispatchContext = createMockAutomationContext({
|
|
eventName: "workflow_dispatch",
|
|
});
|
|
expect(agentMode.shouldTrigger(workflowDispatchContext)).toBe(false);
|
|
|
|
const scheduleContext = createMockAutomationContext({
|
|
eventName: "schedule",
|
|
});
|
|
expect(agentMode.shouldTrigger(scheduleContext)).toBe(false);
|
|
|
|
const repositoryDispatchContext = createMockAutomationContext({
|
|
eventName: "repository_dispatch",
|
|
});
|
|
expect(agentMode.shouldTrigger(repositoryDispatchContext)).toBe(false);
|
|
|
|
// Should NOT trigger for entity events without prompt
|
|
const entityEvents = [
|
|
"issue_comment",
|
|
"pull_request",
|
|
"pull_request_review",
|
|
"issues",
|
|
] as const;
|
|
|
|
entityEvents.forEach((eventName) => {
|
|
const contextNoPrompt = createMockContext({ eventName });
|
|
expect(agentMode.shouldTrigger(contextNoPrompt)).toBe(false);
|
|
});
|
|
|
|
// Should trigger for ANY event when prompt is provided
|
|
const allEvents = [
|
|
"workflow_dispatch",
|
|
"repository_dispatch",
|
|
"schedule",
|
|
"issue_comment",
|
|
"pull_request",
|
|
"pull_request_review",
|
|
"issues",
|
|
] as const;
|
|
|
|
allEvents.forEach((eventName) => {
|
|
const contextWithPrompt =
|
|
eventName === "workflow_dispatch" ||
|
|
eventName === "repository_dispatch" ||
|
|
eventName === "schedule"
|
|
? createMockAutomationContext({
|
|
eventName,
|
|
inputs: { prompt: "Do something" },
|
|
})
|
|
: createMockContext({
|
|
eventName,
|
|
inputs: { prompt: "Do something" },
|
|
});
|
|
expect(agentMode.shouldTrigger(contextWithPrompt)).toBe(true);
|
|
});
|
|
});
|
|
|
|
test("prepare method passes through claude_args", async () => {
|
|
// Clear any previous calls before this test
|
|
exportVariableSpy.mockClear();
|
|
setOutputSpy.mockClear();
|
|
|
|
const contextWithCustomArgs = createMockAutomationContext({
|
|
eventName: "workflow_dispatch",
|
|
});
|
|
|
|
// Save original env vars and set test values
|
|
const originalHeadRef = process.env.GITHUB_HEAD_REF;
|
|
const originalRefName = process.env.GITHUB_REF_NAME;
|
|
delete process.env.GITHUB_HEAD_REF;
|
|
delete process.env.GITHUB_REF_NAME;
|
|
|
|
// Set CLAUDE_ARGS environment variable
|
|
process.env.CLAUDE_ARGS = "--model claude-sonnet-4 --max-turns 10";
|
|
|
|
const mockOctokit = {
|
|
rest: {
|
|
users: {
|
|
getAuthenticated: mock(() =>
|
|
Promise.resolve({
|
|
data: { login: "test-user", id: 12345 },
|
|
}),
|
|
),
|
|
getByUsername: mock(() =>
|
|
Promise.resolve({
|
|
data: { login: "test-user", id: 12345 },
|
|
}),
|
|
),
|
|
},
|
|
},
|
|
} as any;
|
|
const result = await agentMode.prepare({
|
|
context: contextWithCustomArgs,
|
|
octokit: mockOctokit,
|
|
githubToken: "test-token",
|
|
});
|
|
|
|
// Verify claude_args includes user args (no MCP config in agent mode without allowed tools)
|
|
const callArgs = setOutputSpy.mock.calls[0];
|
|
expect(callArgs[0]).toBe("claude_args");
|
|
expect(callArgs[1]).toBe("--model claude-sonnet-4 --max-turns 10");
|
|
expect(callArgs[1]).not.toContain("--mcp-config");
|
|
|
|
// Verify return structure - should use "main" as fallback when no env vars set
|
|
expect(result).toEqual({
|
|
commentId: undefined,
|
|
branchInfo: {
|
|
baseBranch: "main",
|
|
currentBranch: "main",
|
|
claudeBranch: undefined,
|
|
},
|
|
mcpConfig: expect.any(String),
|
|
});
|
|
|
|
// Clean up
|
|
delete process.env.CLAUDE_ARGS;
|
|
if (originalHeadRef !== undefined)
|
|
process.env.GITHUB_HEAD_REF = originalHeadRef;
|
|
if (originalRefName !== undefined)
|
|
process.env.GITHUB_REF_NAME = originalRefName;
|
|
});
|
|
|
|
test("prepare method creates prompt file with correct content", async () => {
|
|
const contextWithPrompts = createMockAutomationContext({
|
|
eventName: "workflow_dispatch",
|
|
});
|
|
// In v1-dev, we only have the unified prompt field
|
|
contextWithPrompts.inputs.prompt = "Custom prompt content";
|
|
|
|
const mockOctokit = {
|
|
rest: {
|
|
users: {
|
|
getAuthenticated: mock(() =>
|
|
Promise.resolve({
|
|
data: { login: "test-user", id: 12345 },
|
|
}),
|
|
),
|
|
getByUsername: mock(() =>
|
|
Promise.resolve({
|
|
data: { login: "test-user", id: 12345 },
|
|
}),
|
|
),
|
|
},
|
|
},
|
|
} as any;
|
|
await agentMode.prepare({
|
|
context: contextWithPrompts,
|
|
octokit: mockOctokit,
|
|
githubToken: "test-token",
|
|
});
|
|
|
|
// Note: We can't easily test file creation in this unit test,
|
|
// but we can verify the method completes without errors
|
|
// With our conditional MCP logic, agent mode with no allowed tools
|
|
// should not include any MCP config
|
|
const callArgs = setOutputSpy.mock.calls[0];
|
|
expect(callArgs[0]).toBe("claude_args");
|
|
// Should be empty or just whitespace when no MCP servers are included
|
|
expect(callArgs[1]).not.toContain("--mcp-config");
|
|
});
|
|
});
|