import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"; import * as core from "@actions/core"; import { checkWritePermissions } from "../src/github/validation/permissions"; import type { ParsedGitHubContext } from "../src/github/context"; describe("checkWritePermissions", () => { let coreInfoSpy: any; let coreWarningSpy: any; let coreErrorSpy: any; beforeEach(() => { // Spy on core methods coreInfoSpy = spyOn(core, "info").mockImplementation(() => {}); coreWarningSpy = spyOn(core, "warning").mockImplementation(() => {}); coreErrorSpy = spyOn(core, "error").mockImplementation(() => {}); }); afterEach(() => { coreInfoSpy.mockRestore(); coreWarningSpy.mockRestore(); coreErrorSpy.mockRestore(); }); const createMockOctokit = (permission: string) => { return { repos: { getCollaboratorPermissionLevel: async () => ({ data: { permission }, }), }, } as any; }; const createContext = (): ParsedGitHubContext => ({ runId: "1234567890", eventName: "issue_comment", eventAction: "created", repository: { full_name: "test-owner/test-repo", owner: "test-owner", repo: "test-repo", }, actor: "test-user", payload: { action: "created", issue: { number: 1, title: "Test Issue", body: "Test body", user: { login: "test-user" }, }, comment: { id: 123, body: "@claude test", user: { login: "test-user" }, html_url: "https://github.com/test-owner/test-repo/issues/1#issuecomment-123", }, } as any, entityNumber: 1, isPR: false, inputs: { triggerPhrase: "@claude", assigneeTrigger: "", allowedTools: "", disallowedTools: "", customInstructions: "", directPrompt: "", }, }); test("should return true for admin permissions", async () => { const mockOctokit = createMockOctokit("admin"); const context = createContext(); const result = await checkWritePermissions(mockOctokit, context); expect(result).toBe(true); expect(coreInfoSpy).toHaveBeenCalledWith( "Checking permissions for actor: test-user", ); expect(coreInfoSpy).toHaveBeenCalledWith( "Permission level retrieved: admin", ); expect(coreInfoSpy).toHaveBeenCalledWith("Actor has write access: admin"); }); test("should return true for write permissions", async () => { const mockOctokit = createMockOctokit("write"); const context = createContext(); const result = await checkWritePermissions(mockOctokit, context); expect(result).toBe(true); expect(coreInfoSpy).toHaveBeenCalledWith("Actor has write access: write"); }); test("should return false for read permissions", async () => { const mockOctokit = createMockOctokit("read"); const context = createContext(); const result = await checkWritePermissions(mockOctokit, context); expect(result).toBe(false); expect(coreWarningSpy).toHaveBeenCalledWith( "Actor has insufficient permissions: read", ); }); test("should return false for none permissions", async () => { const mockOctokit = createMockOctokit("none"); const context = createContext(); const result = await checkWritePermissions(mockOctokit, context); expect(result).toBe(false); expect(coreWarningSpy).toHaveBeenCalledWith( "Actor has insufficient permissions: none", ); }); test("should throw error when permission check fails", async () => { const error = new Error("API error"); const mockOctokit = { repos: { getCollaboratorPermissionLevel: async () => { throw error; }, }, } as any; const context = createContext(); await expect(checkWritePermissions(mockOctokit, context)).rejects.toThrow( "Failed to check permissions for test-user: Error: API error", ); expect(coreErrorSpy).toHaveBeenCalledWith( "Failed to check permissions: Error: API error", ); }); test("should call API with correct parameters", async () => { let capturedParams: any; const mockOctokit = { repos: { getCollaboratorPermissionLevel: async (params: any) => { capturedParams = params; return { data: { permission: "write" } }; }, }, } as any; const context = createContext(); await checkWritePermissions(mockOctokit, context); expect(capturedParams).toEqual({ owner: "test-owner", repo: "test-repo", username: "test-user", }); }); });