194 lines
5.4 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|