/** * M7 Integration Tests: Entry point + mode handler platform routing. * * Verifies that: * - Tag mode routes to fetchForgejoData on Forgejo and fetchGitHubData on GitHub * - createInitialComment uses Forgejo spinner/URL format on Forgejo * - update-comment-link uses updateForgejoCommentBody on Forgejo * - update-comment-link uses Forgejo PR compare URL format on Forgejo */ import { describe, it, expect, beforeEach, afterEach } from "bun:test"; import { detectPlatform } from "../src/platform/detector"; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- let savedEnv: Record; function saveEnv(...keys: string[]) { savedEnv = {}; for (const key of keys) { savedEnv[key] = process.env[key]; } } function restoreEnv() { for (const [key, value] of Object.entries(savedEnv)) { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } } // --------------------------------------------------------------------------- // Platform detection routing (verifies the branch logic used in M7) // --------------------------------------------------------------------------- describe("M7: Platform routing for mode handlers", () => { beforeEach(() => { saveEnv("PLATFORM", "FORGEJO", "GITHUB_SERVER_URL"); }); afterEach(() => { restoreEnv(); }); describe("detectPlatform() — used by tag mode and update-comment-link", () => { it('returns "forgejo" when PLATFORM=forgejo', () => { process.env.PLATFORM = "forgejo"; expect(detectPlatform()).toBe("forgejo"); }); it('returns "github" when PLATFORM=github', () => { process.env.PLATFORM = "github"; expect(detectPlatform()).toBe("github"); }); it('returns "forgejo" when FORGEJO=true', () => { delete process.env.PLATFORM; process.env.FORGEJO = "true"; expect(detectPlatform()).toBe("forgejo"); }); it('returns "forgejo" when GITHUB_SERVER_URL is not github.com', () => { delete process.env.PLATFORM; delete process.env.FORGEJO; process.env.GITHUB_SERVER_URL = "https://git.wxs.ro"; expect(detectPlatform()).toBe("forgejo"); }); it('returns "github" by default', () => { delete process.env.PLATFORM; delete process.env.FORGEJO; process.env.GITHUB_SERVER_URL = "https://github.com"; expect(detectPlatform()).toBe("github"); }); }); }); // --------------------------------------------------------------------------- // Initial comment platform-aware body // --------------------------------------------------------------------------- describe("M7: createInitialComment platform-aware body", () => { beforeEach(() => { saveEnv("PLATFORM", "GITHUB_SERVER_URL"); }); afterEach(() => { restoreEnv(); }); it("Forgejo comment body helpers produce Unicode spinner, not GitHub CDN", async () => { const { createForgejoJobRunLink, createForgejoCommentBody, FORGEJO_SPINNER_HTML, } = await import("../src/forgejo/comment-logic"); const link = createForgejoJobRunLink("owner", "repo", "123"); const body = createForgejoCommentBody(link); // Uses Unicode spinner, not an tag expect(body).toContain(FORGEJO_SPINNER_HTML); expect(body).not.toContain(" { const { createJobRunLink, createCommentBody, SPINNER_HTML } = await import( "../src/github/operations/comments/common" ); const link = createJobRunLink("owner", "repo", "123"); const body = createCommentBody(link); expect(body).toContain(SPINNER_HTML); expect(body).toContain(" { it("uses expand=1 instead of quick_pull=1", async () => { const { createForgejoPrCompareUrl } = await import( "../src/forgejo/comment-logic" ); const url = createForgejoPrCompareUrl( "https://git.wxs.ro", "owner", "repo", "main", "claude/fix-bug", "Issue #42: Changes from Claude", "This PR addresses issue #42", ); expect(url).toContain("expand=1"); expect(url).not.toContain("quick_pull=1"); expect(url).toContain("git.wxs.ro"); expect(url).toContain("compare/main...claude/fix-bug"); }); }); // --------------------------------------------------------------------------- // update-comment-link: platform-aware final comment body // --------------------------------------------------------------------------- describe("M7: updateForgejoCommentBody vs updateCommentBody", () => { it("Forgejo version strips Unicode spinner", async () => { const { updateForgejoCommentBody } = await import( "../src/forgejo/comment-logic" ); const result = updateForgejoCommentBody({ currentBody: "Claude Code is working… ⏳\n\nI'll analyze this and get back to you.\n\n[View job run](https://git.wxs.ro/owner/repo/actions/runs/123)", actionFailed: false, executionDetails: { duration_ms: 30000 }, jobUrl: "https://git.wxs.ro/owner/repo/actions/runs/123", triggerUsername: "testuser", }); expect(result).not.toContain("⏳"); expect(result).not.toContain("Claude Code is working"); expect(result).toContain("@testuser"); expect(result).toContain("30s"); }); it("GitHub version strips CDN spinner image", async () => { const { updateCommentBody } = await import( "../src/github/operations/comment-logic" ); const result = updateCommentBody({ currentBody: 'Claude Code is working… \n\nI\'ll analyze this and get back to you.\n\n[View job run](https://github.com/owner/repo/actions/runs/123)', actionFailed: false, executionDetails: { duration_ms: 30000 }, jobUrl: "https://github.com/owner/repo/actions/runs/123", triggerUsername: "testuser", }); expect(result).not.toContain(" { it("fetchForgejoData and fetchGitHubData both produce FetchDataResult type", async () => { // Verify both functions are importable and are functions // (actual execution requires mock Octokit — covered by their respective test files) const { fetchForgejoData } = await import("../src/forgejo/data/fetcher"); const { fetchGitHubData } = await import("../src/github/data/fetcher"); expect(typeof fetchForgejoData).toBe("function"); expect(typeof fetchGitHubData).toBe("function"); }); it("tag mode imports both fetchers", async () => { // Verify the tag mode module imports work without errors const tagMode = await import("../src/modes/tag"); expect(typeof tagMode.prepareTagMode).toBe("function"); }); }); // --------------------------------------------------------------------------- // Agent mode: no changes needed (verify no regression) // --------------------------------------------------------------------------- describe("M7: Agent mode — no platform changes needed", () => { it("prepareAgentMode is still exported and functional", async () => { const agentMode = await import("../src/modes/agent"); expect(typeof agentMode.prepareAgentMode).toBe("function"); }); });