/** * Diagnostic test file to debug mock tracking failures on Forgejo CI. * Tests pass locally (macOS + Docker grunt-runner) but image-downloader * tests fail on the actual Forgejo runner. This file isolates mock/spy * tracking behavior to find the exact failure point. * * DELETE THIS FILE once the root cause is found and fixed. */ import { describe, test, expect, spyOn, mock, beforeEach, afterEach, } from "bun:test"; describe("DEBUG: mock tracking diagnostics", () => { test("basic mock() tracks calls", () => { const fn = mock(() => 42); fn("hello"); console.log("[DIAG] mock.calls:", JSON.stringify(fn.mock.calls)); expect(fn).toHaveBeenCalledWith("hello"); expect(fn).toHaveBeenCalledTimes(1); }); test("mock() tracks calls through object property", () => { const obj = { method: mock(() => "ok") }; obj.method("arg1", "arg2"); console.log( "[DIAG] obj.method.mock.calls:", JSON.stringify(obj.method.mock.calls), ); expect(obj.method).toHaveBeenCalledWith("arg1", "arg2"); }); test("mock() tracks calls after destructuring", () => { const fn = mock(() => Promise.resolve("done")); const obj = { fn }; const { fn: extracted } = obj; extracted("test"); console.log( "[DIAG] destructured mock.calls:", JSON.stringify(fn.mock.calls), ); expect(fn).toHaveBeenCalledWith("test"); }); test("mock() tracks calls when passed as parameter", async () => { const mockFn = mock(() => Promise.resolve(undefined)); const deps = { action: mockFn as (...args: unknown[]) => Promise }; async function doWork(d: { action: (...args: unknown[]) => Promise; }) { const { action } = d; await action("path", { recursive: true }); } await doWork(deps); console.log( "[DIAG] param-passed mock.calls:", JSON.stringify(mockFn.mock.calls), ); expect(mockFn).toHaveBeenCalledWith("path", { recursive: true }); }); test("spyOn(console) tracks calls", () => { const spy = spyOn(console, "warn").mockImplementation(() => {}); console.warn("test warning"); // Check raw calls before expect console.log( "[DIAG] console.warn spy.mock.calls:", JSON.stringify(spy.mock.calls), ); expect(spy).toHaveBeenCalledWith("test warning"); spy.mockRestore(); }); test("spyOn(global, 'fetch') tracks calls", async () => { const fetchSpy = spyOn(global, "fetch").mockResolvedValue({ ok: true, arrayBuffer: async () => new ArrayBuffer(0), } as Response); await fetch("https://example.com"); console.log( "[DIAG] fetch spy.mock.calls:", JSON.stringify(fetchSpy.mock.calls), ); expect(fetchSpy).toHaveBeenCalledWith("https://example.com"); fetchSpy.mockRestore(); }); test("mock() with mockClear preserves tracking", () => { const fn = mock(() => "first"); fn("before-clear"); fn.mockClear(); fn("after-clear"); console.log( "[DIAG] after mockClear, calls:", JSON.stringify(fn.mock.calls), ); expect(fn).toHaveBeenCalledTimes(1); expect(fn).toHaveBeenCalledWith("after-clear"); }); describe("mock created at describe scope", () => { const describeMock = mock(() => Promise.resolve(undefined as any)); beforeEach(() => { describeMock.mockClear(); }); test("tracks calls from describe-scoped mock", async () => { await describeMock("test-arg"); console.log( "[DIAG] describe-scope mock.calls:", JSON.stringify(describeMock.mock.calls), ); expect(describeMock).toHaveBeenCalledWith("test-arg"); }); test("tracks calls via DI pattern (like image-downloader)", async () => { const deps = { mkdir: describeMock as any, }; // Simulate the exact pattern from image-downloader.ts const { mkdir } = deps; await mkdir("/tmp/github-images", { recursive: true }); console.log( "[DIAG] DI-pattern mock.calls:", JSON.stringify(describeMock.mock.calls), ); expect(describeMock).toHaveBeenCalledWith("/tmp/github-images", { recursive: true, }); }); }); describe("env var diagnostics", () => { test("GITHUB_SERVER_URL value", () => { console.log("[DIAG] GITHUB_SERVER_URL:", process.env.GITHUB_SERVER_URL); console.log("[DIAG] FORGEJO:", process.env.FORGEJO); console.log("[DIAG] RUNNER_OS:", process.env.RUNNER_OS); console.log("[DIAG] Bun.version:", Bun.version); console.log("[DIAG] process.arch:", process.arch); console.log("[DIAG] process.platform:", process.platform); expect(true).toBe(true); }); }); });