Files
claude-code-action/test/actor.test.ts
T

172 lines
5.4 KiB
TypeScript

#!/usr/bin/env bun
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
import { checkHumanActor } from "../src/github/validation/actor";
import type { Octokit } from "@octokit/rest";
import { createMockContext } from "./mockContext";
function createMockOctokit(userType: string): Octokit {
return {
users: {
getByUsername: async () => ({
data: {
type: userType,
},
}),
},
} as unknown as Octokit;
}
describe("checkHumanActor", () => {
test("should pass for human actor", async () => {
const mockOctokit = createMockOctokit("User");
const context = createMockContext();
context.actor = "human-user";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
});
test("should throw error for bot actor when not allowed", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "test-bot[bot]";
context.inputs.allowedBots = "";
await expect(checkHumanActor(mockOctokit, context)).rejects.toThrow(
"Workflow initiated by non-human actor: test-bot (type: Bot). Add bot to allowed_bots list or use '*' to allow all bots.",
);
});
test("should pass for bot actor when all bots allowed", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "test-bot[bot]";
context.inputs.allowedBots = "*";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
});
test("should pass for specific bot when in allowed list", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "dependabot[bot]";
context.inputs.allowedBots = "dependabot[bot],renovate[bot]";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
});
test("should pass for specific bot when in allowed list (without [bot])", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "dependabot[bot]";
context.inputs.allowedBots = "dependabot,renovate";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
});
test("should throw error for bot not in allowed list", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "other-bot[bot]";
context.inputs.allowedBots = "dependabot[bot],renovate[bot]";
await expect(checkHumanActor(mockOctokit, context)).rejects.toThrow(
"Workflow initiated by non-human actor: other-bot (type: Bot). Add bot to allowed_bots list or use '*' to allow all bots.",
);
});
test("should throw error for bot not in allowed list (without [bot])", async () => {
const mockOctokit = createMockOctokit("Bot");
const context = createMockContext();
context.actor = "other-bot[bot]";
context.inputs.allowedBots = "dependabot,renovate";
await expect(checkHumanActor(mockOctokit, context)).rejects.toThrow(
"Workflow initiated by non-human actor: other-bot (type: Bot). Add bot to allowed_bots list or use '*' to allow all bots.",
);
});
test("should treat undefined actor type as human (GitHub compatibility)", async () => {
// Edge case: GitHub API returns no `type` field
const mockOctokit = {
users: {
getByUsername: async () => ({
data: {},
}),
},
} as unknown as Octokit;
const context = createMockContext();
context.actor = "unknown-user";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
});
describe("Forgejo platform", () => {
let savedPlatform: string | undefined;
let savedServerUrl: string | undefined;
beforeEach(() => {
savedPlatform = process.env.PLATFORM;
savedServerUrl = process.env.GITHUB_SERVER_URL;
process.env.PLATFORM = "forgejo";
});
afterEach(() => {
if (savedPlatform !== undefined) process.env.PLATFORM = savedPlatform;
else delete process.env.PLATFORM;
if (savedServerUrl !== undefined)
process.env.GITHUB_SERVER_URL = savedServerUrl;
else delete process.env.GITHUB_SERVER_URL;
});
test("should skip actor check entirely on Forgejo", async () => {
let apiCalled = false;
const mockOctokit = {
users: {
getByUsername: async () => {
apiCalled = true;
return { data: {} };
},
},
} as unknown as Octokit;
const context = createMockContext();
context.actor = "forgejo-user";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
expect(apiCalled).toBe(false);
});
test("should skip actor check for bot-like names on Forgejo", async () => {
let apiCalled = false;
const mockOctokit = {
users: {
getByUsername: async () => {
apiCalled = true;
return { data: { type: "Bot" } };
},
},
} as unknown as Octokit;
const context = createMockContext();
context.actor = "some-bot[bot]";
context.inputs.allowedBots = "";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
expect(apiCalled).toBe(false);
});
});
});