fix: replace mock.module with spyOn to stop cross-file test pollution

mock.module("image-downloader") in forgejo-data-fetcher.test.ts replaced
the module in Bun's registry globally for the entire process. When Bun's
non-deterministic file ordering ran this file BEFORE image-downloader.test.ts,
the image-downloader tests imported a stubbed module instead of the real one,
breaking all 18 tests. This was the root cause of the CI failures that
persisted through 5 previous fix attempts.

spyOn patches the live module export per-test and is auto-restored by Bun
after each test — no cross-file pollution regardless of execution order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-26 10:45:09 +02:00
1 parent a544d710e5
commit ac9e9cddff
1 file changed
+16 -6
+16 -6
View File
@@ -1,4 +1,4 @@
import { describe, expect, it, jest, mock } from "bun:test"; import { beforeEach, describe, expect, it, jest, spyOn } from "bun:test";
import { import {
fetchForgejoData, fetchForgejoData,
mapFileStatus, mapFileStatus,
@@ -8,11 +8,12 @@ import {
} from "../src/forgejo/data/fetcher"; } from "../src/forgejo/data/fetcher";
import type { Octokits } from "../src/github/api/client"; import type { Octokits } from "../src/github/api/client";
import type { GitHubReviewComment } from "../src/github/types"; import type { GitHubReviewComment } from "../src/github/types";
// Import the module namespace so spyOn can patch the export directly.
// Mock image downloader — returns empty map (no-op on Forgejo) // This avoids mock.module() which replaces the module in Bun's registry
mock.module("../src/github/utils/image-downloader", () => ({ // globally for the entire process — poisoning other test files
downloadCommentImages: jest.fn().mockResolvedValue(new Map()), // (like image-downloader.test.ts) that import the same module.
})); // spyOn is auto-restored after each test, so no cross-file pollution.
import * as imageDownloaderModule from "../src/github/utils/image-downloader";
// Helper to build a mock Octokits with REST methods // Helper to build a mock Octokits with REST methods
function createMockOctokits({ function createMockOctokits({
@@ -125,6 +126,15 @@ const mockIssueRestResponse = {
}; };
describe("fetchForgejoData", () => { describe("fetchForgejoData", () => {
// Stub downloadCommentImages per-test — returns empty Map (no-op on Forgejo).
// spyOn patches the live module export and is auto-restored after each test,
// unlike mock.module which persists for the entire Bun process.
beforeEach(() => {
spyOn(imageDownloaderModule, "downloadCommentImages").mockResolvedValue(
new Map(),
);
});
describe("PR data fetching", () => { describe("PR data fetching", () => {
it("should fetch PR details, files, commits, comments, and reviews in parallel then per-review comments", async () => { it("should fetch PR details, files, commits, comments, and reviews in parallel then per-review comments", async () => {
const mockOctokits = createMockOctokits({ const mockOctokits = createMockOctokits({