Files
claude-code-action/test/m7-integration.test.ts
T
Vlad MocanuandClaude Opus 4.6 ac58fb6c14 fix(prd-7): M1 architecture review — consolidate duplication, fix bugs, memoize detection
- F1: Consolidated updateForgejoCommentBody into unified updateCommentBody (~150 lines removed)
- F2: Fixed wrong run identifier in update-with-branch.ts (context.runId → getForgejoRunIndex())
- F3: Memoized detectPlatform() with _resetPlatformCache() for tests
- F4: Added getServerUrl() runtime helper to resolve constant-vs-function inconsistency
- F5: Fixed sticky comment bot matching on Forgejo via context.inputs.botName
- F6: Documented GHES heuristic misidentification in docs/forgejo.md
- Updated PRD M1 checkbox to complete

Progress: 17% complete — M1 done, M2 (error handling review) next

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:09:02 +02:00

235 lines
8.2 KiB
TypeScript

/**
* 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, _resetPlatformCache } from "../src/platform/detector";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
let savedEnv: Record<string, string | undefined>;
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(() => {
_resetPlatformCache();
saveEnv("PLATFORM", "FORGEJO", "GITHUB_SERVER_URL");
});
afterEach(() => {
restoreEnv();
_resetPlatformCache();
});
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 <img> tag
expect(body).toContain(FORGEJO_SPINNER_HTML);
expect(body).not.toContain("<img");
expect(body).not.toContain("github.com/user-attachments");
// Contains the job run link
expect(body).toContain("[View job run]");
});
it("GitHub comment body helpers produce CDN spinner image", async () => {
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("<img");
expect(body).toContain("[View job run]");
});
});
// ---------------------------------------------------------------------------
// update-comment-link: Forgejo PR compare URL format
// ---------------------------------------------------------------------------
describe("M7: Forgejo PR compare URL format", () => {
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: unified updateCommentBody handles both platforms", () => {
it("strips Unicode spinner (Forgejo format)", 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://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… <img src="https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f" width="14px" height="14px" />\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("<img");
expect(result).not.toContain("Claude Code is working");
expect(result).toContain("@testuser");
expect(result).toContain("30s");
});
});
// ---------------------------------------------------------------------------
// Tag mode: data fetcher selection
// ---------------------------------------------------------------------------
describe("M7: Tag mode data fetcher selection", () => {
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");
});
});