From c201006ca2be8d9d106741f837abeb02a1fdab71 Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Sun, 15 Mar 2026 11:38:35 -0400 Subject: [PATCH] test(core): replace image-cache module mocks with globalThis.fetch spies Switch `mock.module("../src/image-cache", ...)` stubs in thumbhash, person-detail, colors, and credits tests to `spyOn(globalThis, "fetch")` instead. This avoids the cross-file persistence problem with `mock.module` and tests the real code paths more faithfully. Update `image-cache.ts` to call `globalThis.fetch` so the spy intercepts it, add `mock.restore()` to the global `afterEach` in `preload.ts`, and set `IMAGE_CACHE_ENABLED=false` in affected tests to avoid disk writes during test runs. --- packages/core/src/image-cache.ts | 2 +- packages/core/test/colors.test.ts | 9 +------ packages/core/test/credits.test.ts | 7 +---- packages/core/test/person-detail.test.ts | 33 ++++++++++++++++++++---- packages/core/test/preload.ts | 8 +++++- packages/core/test/thumbhash.test.ts | 33 ++++++++++++++++++++---- 6 files changed, 66 insertions(+), 26 deletions(-) diff --git a/packages/core/src/image-cache.ts b/packages/core/src/image-cache.ts index 508fea3..5024802 100644 --- a/packages/core/src/image-cache.ts +++ b/packages/core/src/image-cache.ts @@ -63,7 +63,7 @@ async function fetchRemoteImage( const url = tmdbCdnImageUrl(tmdbPath, category) ?? `${TMDB_IMAGE_BASE_URL}${tmdbPath}`; - const res = await fetch(url); + const res = await globalThis.fetch(url); if (!res.ok) { log.warn(`Fetch failed: ${url} -> ${res.status}`); return null; diff --git a/packages/core/test/colors.test.ts b/packages/core/test/colors.test.ts index 41762dc..ddde553 100644 --- a/packages/core/test/colors.test.ts +++ b/packages/core/test/colors.test.ts @@ -1,11 +1,4 @@ -import { describe, expect, mock, test } from "bun:test"; - -mock.module("../src/image-cache", () => ({ - downloadAndCacheImage: async () => {}, - getLocalImagePath: () => "", - imageCacheEnabled: () => false, - isImageCached: async () => false, -})); +import { describe, expect, test } from "bun:test"; import { parseColorPalette } from "../src/colors"; diff --git a/packages/core/test/credits.test.ts b/packages/core/test/credits.test.ts index 6a1d7b9..078eec5 100644 --- a/packages/core/test/credits.test.ts +++ b/packages/core/test/credits.test.ts @@ -1,12 +1,7 @@ -import { beforeEach, describe, expect, mock, test } from "bun:test"; +import { beforeEach, describe, expect, test } from "bun:test"; import { persons, titleCast } from "@sofa/db/schema"; import { clearAllTables, insertTitle, testDb } from "@sofa/db/test-utils"; -mock.module("../src/image-cache", () => ({ - imageCacheEnabled: () => false, - cacheProfilePhotos: async () => {}, -})); - import { getCastForTitle } from "../src/credits"; beforeEach(() => { diff --git a/packages/core/test/person-detail.test.ts b/packages/core/test/person-detail.test.ts index 6233196..412e99b 100644 --- a/packages/core/test/person-detail.test.ts +++ b/packages/core/test/person-detail.test.ts @@ -1,4 +1,12 @@ -import { beforeEach, describe, expect, mock, test } from "bun:test"; +import { + afterEach, + beforeEach, + describe, + expect, + mock, + spyOn, + test, +} from "bun:test"; import { eq } from "@sofa/db/helpers"; import { persons } from "@sofa/db/schema"; import { clearAllTables, testDb } from "@sofa/db/test-utils"; @@ -22,10 +30,6 @@ let nextPersonDetails = { imdb_id: null, }; -mock.module("../src/image-cache", () => ({ - loadImageBuffer: async () => nextBuffer, -})); - mock.module("@sofa/tmdb/client", () => ({ getPersonDetails: async () => nextPersonDetails, getPersonCombinedCredits: async () => ({ cast: [] }), @@ -35,6 +39,7 @@ import { getOrFetchPerson } from "../src/person"; beforeEach(() => { clearAllTables(); + process.env.IMAGE_CACHE_ENABLED = "false"; nextBuffer = TINY_PNG; nextPersonDetails = { id: 100, @@ -48,6 +53,24 @@ beforeEach(() => { popularity: 10, imdb_id: null, }; + spyOn(globalThis, "fetch").mockImplementation((async ( + _input: string | URL | Request, + _init?: RequestInit, + ) => { + if (!nextBuffer) { + return new Response(null, { status: 404 }); + } + + return new Response(nextBuffer, { + status: 200, + headers: { "content-type": "image/png" }, + }); + }) as typeof fetch); +}); + +afterEach(() => { + delete process.env.IMAGE_CACHE_ENABLED; + mock.restore(); }); describe("getOrFetchPerson", () => { diff --git a/packages/core/test/preload.ts b/packages/core/test/preload.ts index b91a0c9..29c4efb 100644 --- a/packages/core/test/preload.ts +++ b/packages/core/test/preload.ts @@ -1,9 +1,15 @@ -import { mock } from "bun:test"; +import { afterEach, mock } from "bun:test"; import { applyMigrations, testDb } from "@sofa/db/test-utils"; +process.env.LOG_LEVEL ??= "error"; + mock.module("@sofa/db/client", () => ({ db: testDb, closeDatabase: () => {}, })); applyMigrations(); + +afterEach(() => { + mock.restore(); +}); diff --git a/packages/core/test/thumbhash.test.ts b/packages/core/test/thumbhash.test.ts index 1d3e6e1..b6bd652 100644 --- a/packages/core/test/thumbhash.test.ts +++ b/packages/core/test/thumbhash.test.ts @@ -1,4 +1,12 @@ -import { beforeEach, describe, expect, mock, test } from "bun:test"; +import { + afterEach, + beforeEach, + describe, + expect, + mock, + spyOn, + test, +} from "bun:test"; import { eq } from "@sofa/db/helpers"; import { episodes, seasons, titles } from "@sofa/db/schema"; import { clearAllTables, testDb } from "@sofa/db/test-utils"; @@ -10,15 +18,30 @@ const TINY_PNG = Buffer.from( let nextBuffer: Buffer | null = TINY_PNG; -mock.module("../src/image-cache", () => ({ - loadImageBuffer: async () => nextBuffer, -})); - import { generateEpisodeThumbHash, generateThumbHash } from "../src/thumbhash"; beforeEach(() => { clearAllTables(); + process.env.IMAGE_CACHE_ENABLED = "false"; nextBuffer = TINY_PNG; + spyOn(globalThis, "fetch").mockImplementation((async ( + _input: string | URL | Request, + _init?: RequestInit, + ) => { + if (!nextBuffer) { + return new Response(null, { status: 404 }); + } + + return new Response(nextBuffer, { + status: 200, + headers: { "content-type": "image/png" }, + }); + }) as typeof fetch); +}); + +afterEach(() => { + delete process.env.IMAGE_CACHE_ENABLED; + mock.restore(); }); describe("thumbhash generation", () => {