Files
jellytau/src/lib/services/imageCache.test.ts
T

194 lines
5.4 KiB
TypeScript

/**
* Image cache service tests
*
* TRACES: UR-007 | DR-016
*/
import { describe, it, expect, beforeEach, vi } from "vitest";
import {
getCachedImageUrl,
getCacheStats,
setCacheLimit,
clearCache,
deleteItemCache,
formatBytes,
gbToBytes,
bytesToGb,
} from "./imageCache";
vi.mock("@tauri-apps/api/core", () => ({
invoke: vi.fn(async (command: string, args?: any) => {
if (command === "thumbnail_get_cached") {
return null; // No cached image
}
if (command === "thumbnail_get_stats") {
return {
totalSizeBytes: 1024 * 1024,
itemCount: 10,
limitBytes: 1024 * 1024 * 1024,
};
}
if (command === "thumbnail_set_limit") {
return undefined;
}
if (command === "thumbnail_clear_cache") {
return undefined;
}
if (command === "thumbnail_delete_item") {
return undefined;
}
if (command === "thumbnail_save") {
return undefined;
}
return null;
}),
convertFileSrc: vi.fn((path: string) => `asset://path/${path}`),
}));
describe("image cache service", () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe("getCachedImageUrl", () => {
it("should build server URL with default image type", async () => {
const url = await getCachedImageUrl(
"http://server.local:8096",
"item-123"
);
expect(url).toContain("http://server.local:8096/Items/item-123/Images/Primary");
});
it("should build server URL with custom image type", async () => {
const url = await getCachedImageUrl(
"http://server.local:8096",
"item-123",
"Backdrop"
);
expect(url).toContain("Backdrop");
});
it("should include image options in URL", async () => {
const url = await getCachedImageUrl(
"http://server.local:8096",
"item-123",
"Primary",
{
maxWidth: 300,
maxHeight: 400,
quality: 90,
tag: "abc123",
}
);
expect(url).toContain("maxWidth=300");
expect(url).toContain("maxHeight=400");
expect(url).toContain("quality=90");
expect(url).toContain("tag=abc123");
});
it("should trigger background caching", async () => {
const { invoke } = await import("@tauri-apps/api/core");
const invokeSpy = vi.mocked(invoke);
await getCachedImageUrl("http://server.local:8096", "item-123");
const saveCall = invokeSpy.mock.calls.find(
(call) => call[0] === "thumbnail_save"
);
expect(saveCall).toBeDefined();
expect(saveCall![1]).toHaveProperty("itemId", "item-123");
expect(saveCall![1]).toHaveProperty("imageType", "Primary");
});
});
describe("cache statistics", () => {
it("should get cache statistics", async () => {
const stats = await getCacheStats();
expect(stats).toHaveProperty("totalSizeBytes");
expect(stats).toHaveProperty("itemCount");
expect(stats).toHaveProperty("limitBytes");
expect(typeof stats.totalSizeBytes).toBe("number");
expect(typeof stats.itemCount).toBe("number");
});
it("should set cache limit", async () => {
const limit = 1024 * 1024 * 1024 * 5; // 5GB
await setCacheLimit(limit);
const { invoke } = await import("@tauri-apps/api/core");
const invokeSpy = vi.mocked(invoke);
const setLimitCall = invokeSpy.mock.calls.find(
(call) => call[0] === "thumbnail_set_limit"
);
expect(setLimitCall).toBeDefined();
expect(setLimitCall![1]).toHaveProperty("limitBytes", limit);
});
});
describe("cache clearing", () => {
it("should clear all cached thumbnails", async () => {
await clearCache();
const { invoke } = await import("@tauri-apps/api/core");
const invokeSpy = vi.mocked(invoke);
expect(invokeSpy).toHaveBeenCalledWith("thumbnail_clear_cache");
});
it("should delete cache for specific item", async () => {
await deleteItemCache("item-456");
const { invoke } = await import("@tauri-apps/api/core");
const invokeSpy = vi.mocked(invoke);
const deleteCall = invokeSpy.mock.calls.find(
(call) => call[0] === "thumbnail_delete_item"
);
expect(deleteCall).toBeDefined();
expect(deleteCall![1]).toHaveProperty("itemId", "item-456");
});
});
describe("byte formatting", () => {
it("should format bytes", () => {
expect(formatBytes(512)).toContain("B");
expect(formatBytes(512)).not.toContain("KB");
});
it("should format kilobytes", () => {
expect(formatBytes(1024)).toContain("KB");
});
it("should format megabytes", () => {
expect(formatBytes(1024 * 1024)).toContain("MB");
});
it("should format gigabytes", () => {
expect(formatBytes(1024 * 1024 * 1024)).toContain("GB");
});
it("should format with correct precision", () => {
const result = formatBytes(1024 * 1.5);
expect(result).toMatch(/\d+\.\d+ KB/);
});
});
describe("unit conversion", () => {
it("should convert gigabytes to bytes", () => {
const bytes = gbToBytes(1);
expect(bytes).toBe(1024 * 1024 * 1024);
});
it("should convert bytes to gigabytes", () => {
const gb = bytesToGb(1024 * 1024 * 1024);
expect(gb).toBe(1);
});
it("should handle fractional conversions", () => {
const bytes = gbToBytes(0.5);
expect(bytes).toBe(512 * 1024 * 1024);
const gb = bytesToGb(512 * 1024 * 1024);
expect(gb).toBe(0.5);
});
});
});