#!/usr/bin/env bun /** * Tests the Forgejo API divergence in setupBranch(). * * Forgejo uses repos.getBranch() to get source branch SHA because Forgejo's * /git/refs (plural) API differs from GitHub's /git/ref (singular), causing * git.getRef() to 404 on Forgejo. * * Approach: Call setupBranch() with mocked Octokit. The platform-specific * API call records that it was called, then throws a sentinel error to stop * execution before git shell commands run (avoiding real repo side effects). */ import { describe, test, expect, beforeEach, afterEach, spyOn, type Mock, } from "bun:test"; import { setupBranch } from "../src/github/operations/branch"; import { _resetPlatformCache } from "../src/platform/detector"; import { createMockContext } from "./mockContext"; import type { Octokits } from "../src/github/api/client"; import type { FetchDataResult } from "../src/github/data/fetcher"; const mockGithubData: FetchDataResult = { contextData: { title: "Test issue", labels: { nodes: [{ name: "bug" }] }, }, comments: [], changedFiles: [], changedFilesWithSHA: [], reviewData: null, imageUrlMap: new Map(), } as any; function createBranchMockOctokits( overrides: Record = {}, ): Octokits { return { rest: { repos: { get: overrides.reposGet ?? (async () => ({ data: { default_branch: "main" } })), getBranch: overrides.getBranch ?? (async () => ({ data: { commit: { sha: "abc123" } } })), }, git: { getRef: overrides.getRef ?? (async () => ({ data: { object: { sha: "abc123" } } })), }, }, graphql: {} as any, } as unknown as Octokits; } describe("setupBranch — Forgejo API divergence", () => { let savedPlatform: string | undefined; let savedServerUrl: string | undefined; let consoleLogSpy: Mock<(...args: any[]) => void>; let consoleErrorSpy: Mock<(...args: any[]) => void>; beforeEach(() => { _resetPlatformCache(); savedPlatform = process.env.PLATFORM; savedServerUrl = process.env.GITHUB_SERVER_URL; consoleLogSpy = spyOn(console, "log").mockImplementation(() => {}); consoleErrorSpy = spyOn(console, "error").mockImplementation(() => {}); }); 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; consoleLogSpy.mockRestore(); consoleErrorSpy.mockRestore(); _resetPlatformCache(); }); test("on Forgejo: calls repos.getBranch() instead of git.getRef()", async () => { process.env.PLATFORM = "forgejo"; process.env.GITHUB_SERVER_URL = "https://git.example.com"; _resetPlatformCache(); let getBranchCalled = false; let getRefCalled = false; const mockOctokits = createBranchMockOctokits({ getBranch: async () => { getBranchCalled = true; // Throw sentinel error to stop before git commands execute throw new Error("STOP_BEFORE_GIT_COMMANDS"); }, getRef: async () => { getRefCalled = true; throw new Error("STOP_BEFORE_GIT_COMMANDS"); }, }); const context = createMockContext({ isPR: false, entityNumber: 1 }); await expect( setupBranch(mockOctokits, mockGithubData, context), ).rejects.toThrow("STOP_BEFORE_GIT_COMMANDS"); expect(getBranchCalled).toBe(true); expect(getRefCalled).toBe(false); }); test("on GitHub: calls git.getRef() instead of repos.getBranch()", async () => { delete process.env.PLATFORM; process.env.GITHUB_SERVER_URL = "https://github.com"; _resetPlatformCache(); let getBranchCalled = false; let getRefCalled = false; const mockOctokits = createBranchMockOctokits({ getBranch: async () => { getBranchCalled = true; throw new Error("STOP_BEFORE_GIT_COMMANDS"); }, getRef: async () => { getRefCalled = true; throw new Error("STOP_BEFORE_GIT_COMMANDS"); }, }); const context = createMockContext({ isPR: false, entityNumber: 1 }); await expect( setupBranch(mockOctokits, mockGithubData, context), ).rejects.toThrow("STOP_BEFORE_GIT_COMMANDS"); expect(getRefCalled).toBe(true); expect(getBranchCalled).toBe(false); }); });