Files
sofa/packages/core/test/image-cache.test.ts
T
jakeandClaude Opus 4.6 554271a888 test: add webhook, credits, image-cache, and metadata tests
- New webhooks.test.ts: 34 tests covering all 3 payload parsers
  (Plex/Jellyfin/Emby), toOptionalInt, and processWebhook end-to-end
  (movie/episode watch logging, deduplication, resolution failures)
- Expand credits.test.ts: 6 new tests for refreshCredits (movie/TV
  cast+crew upsert, person deduplication, cast limit, notable crew filter)
- Expand image-cache.test.ts: 8 new tests for imageCacheEnabled,
  getLocalImagePath, and loadImageBuffer error handling
- Expand metadata.test.ts: 4 new tests for ensureBrowseTitlesExist
  (shell title creation, deduplication, mixed existing/new)

304 → 345 tests passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:23:50 -04:00

114 lines
3.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { getLocalImagePath, imageCacheEnabled, loadImageBuffer } from "../src/image-cache";
beforeEach(() => {
process.env.IMAGE_CACHE_ENABLED = "false";
});
afterEach(() => {
delete process.env.IMAGE_CACHE_ENABLED;
});
function mockFetch(impl: (input: string | URL | Request) => Promise<Response>) {
vi.spyOn(globalThis, "fetch").mockImplementation(impl as unknown as typeof fetch);
}
// ─── imageCacheEnabled ──────────────────────────────────────────────
describe("imageCacheEnabled", () => {
test("returns false when IMAGE_CACHE_ENABLED is 'false'", () => {
process.env.IMAGE_CACHE_ENABLED = "false";
expect(imageCacheEnabled()).toBe(false);
});
test("returns true when IMAGE_CACHE_ENABLED is unset", () => {
delete process.env.IMAGE_CACHE_ENABLED;
expect(imageCacheEnabled()).toBe(true);
});
test("returns true when IMAGE_CACHE_ENABLED is 'true'", () => {
process.env.IMAGE_CACHE_ENABLED = "true";
expect(imageCacheEnabled()).toBe(true);
});
});
// ─── getLocalImagePath ──────────────────────────────────────────────
describe("getLocalImagePath", () => {
test("joins cache dir with category and basename", () => {
const result = getLocalImagePath("posters", "/some/deep/path/poster.jpg");
expect(result).toContain("posters");
expect(result).toContain("poster.jpg");
expect(result).not.toContain("some/deep");
});
});
// ─── loadImageBuffer ────────────────────────────────────────────────
describe("loadImageBuffer", () => {
test("uses category-specific TMDB sizes when cache is disabled", async () => {
const urls: string[] = [];
mockFetch(async (input) => {
urls.push(String(input));
return new Response(new Uint8Array([1, 2, 3]), {
status: 200,
headers: { "content-type": "image/jpeg" },
});
});
await loadImageBuffer("/poster.jpg", "posters");
await loadImageBuffer("/profile.jpg", "profiles");
expect(urls).toEqual([
"https://image.tmdb.org/t/p/w500/poster.jpg",
"https://image.tmdb.org/t/p/w185/profile.jpg",
]);
});
test("returns null on fetch failure", async () => {
mockFetch(async () => new Response(null, { status: 404 }));
const result = await loadImageBuffer("/missing.jpg", "posters");
expect(result).toBeNull();
});
test("returns null on network error", async () => {
mockFetch(async () => {
throw new Error("Network error");
});
const result = await loadImageBuffer("/fail.jpg", "posters");
expect(result).toBeNull();
});
test("returns buffer on success", async () => {
const imageBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
mockFetch(async () => {
return new Response(imageBytes, {
status: 200,
headers: { "content-type": "image/png" },
});
});
const result = await loadImageBuffer("/image.png", "posters");
expect(result).toBeInstanceOf(Buffer);
expect(result).toHaveLength(4);
});
test("rejects images exceeding size limit via content-length", async () => {
mockFetch(async () => {
return new Response(new Uint8Array([1]), {
status: 200,
headers: {
"content-type": "image/jpeg",
"content-length": String(11 * 1024 * 1024),
},
});
});
const result = await loadImageBuffer("/huge.jpg", "posters");
expect(result).toBeNull();
});
});