Formatting was configured but never enforced: `bun run format:check` reported 199 unformatted files and ran in no workflow and in no git hook, so .prettierrc (printWidth 100, trailing commas) described an intention rather than the tree. This is the one-time sweep that makes the check gateable. Whitespace and token-reflow only -- no behavioural change: `bun run check` reports 0 errors and all 1053 frontend tests pass before and after. Kept out of every other commit on purpose. A 199-file diff mixed with real changes is unreviewable, and the next commit turns format:check into a hard CI gate so this cannot silently accumulate again.
210 lines
6.7 KiB
TypeScript
210 lines
6.7 KiB
TypeScript
/**
|
|
* Preload service tests
|
|
*
|
|
* TRACES: UR-004, UR-011 | DR-006, DR-015
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { preloadUpcomingTracks, updateCacheConfig, getCacheConfig } from "./preload";
|
|
import type { CacheConfig } from "$lib/api/bindings";
|
|
|
|
// updateCacheConfig now takes a full CacheConfig (matches the backend command)
|
|
function makeConfig(overrides: Partial<CacheConfig> = {}): CacheConfig {
|
|
return {
|
|
queuePrecacheEnabled: true,
|
|
queuePrecacheCount: 5,
|
|
albumAffinityEnabled: false,
|
|
albumAffinityThreshold: 0.75,
|
|
storageLimit: 2 * 1024 * 1024 * 1024,
|
|
wifiOnly: false,
|
|
temporaryTtlHours: 24 * 7,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
vi.mock("@tauri-apps/api/core", () => ({
|
|
invoke: vi.fn(async (command: string, args?: any) => {
|
|
if (command === "player_preload_upcoming") {
|
|
return {
|
|
queuedCount: 3,
|
|
alreadyDownloaded: 2,
|
|
skipped: 1,
|
|
};
|
|
}
|
|
if (command === "player_set_cache_config") {
|
|
return undefined;
|
|
}
|
|
if (command === "player_get_cache_config") {
|
|
return {
|
|
queuePrecacheEnabled: true,
|
|
queuePrecacheCount: 5,
|
|
albumAffinityEnabled: true,
|
|
albumAffinityThreshold: 0.8,
|
|
storageLimit: 1024 * 1024 * 1024,
|
|
wifiOnly: false,
|
|
};
|
|
}
|
|
return null;
|
|
}),
|
|
}));
|
|
|
|
vi.mock("$lib/stores/auth", () => ({
|
|
auth: {
|
|
getUserId: vi.fn(() => "user-123"),
|
|
},
|
|
}));
|
|
|
|
describe("preload service", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("preloadUpcomingTracks", () => {
|
|
it("should preload tracks without options", async () => {
|
|
await expect(preloadUpcomingTracks()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should invoke correct backend command", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await preloadUpcomingTracks();
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_preload_upcoming");
|
|
expect(call).toBeDefined();
|
|
});
|
|
|
|
it("should include userId in command", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await preloadUpcomingTracks();
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_preload_upcoming");
|
|
expect(call![1]).toHaveProperty("userId", "user-123");
|
|
});
|
|
|
|
it("should use override userId if provided", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await preloadUpcomingTracks({ userId: "user-456" });
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_preload_upcoming");
|
|
expect(call![1]).toHaveProperty("userId", "user-456");
|
|
});
|
|
|
|
it("should skip if no active user", async () => {
|
|
const { auth } = await import("$lib/stores/auth");
|
|
const authModule = vi.mocked(auth);
|
|
authModule.getUserId = vi.fn(() => null);
|
|
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await preloadUpcomingTracks();
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_preload_upcoming");
|
|
expect(call).toBeUndefined();
|
|
});
|
|
|
|
it("should handle preload result", async () => {
|
|
// Should not throw even with result
|
|
await expect(preloadUpcomingTracks()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should handle errors gracefully", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
invokeSpy.mockRejectedValueOnce(new Error("Backend error"));
|
|
|
|
// Should not throw
|
|
await expect(preloadUpcomingTracks()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should support debug option", async () => {
|
|
await expect(preloadUpcomingTracks({ debug: true })).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should support both debug and userId options", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await preloadUpcomingTracks({ debug: true, userId: "user-789" });
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_preload_upcoming");
|
|
expect(call![1]).toHaveProperty("userId", "user-789");
|
|
});
|
|
});
|
|
|
|
describe("updateCacheConfig", () => {
|
|
it("should update cache config", async () => {
|
|
const config = makeConfig({
|
|
queuePrecacheEnabled: false,
|
|
queuePrecacheCount: 10,
|
|
});
|
|
|
|
await expect(updateCacheConfig(config)).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should invoke correct backend command", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
const config = makeConfig({ queuePrecacheEnabled: true });
|
|
await updateCacheConfig(config);
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_set_cache_config");
|
|
expect(call).toBeDefined();
|
|
expect(call![1]).toHaveProperty("config", config);
|
|
});
|
|
|
|
it("should support overriding individual config options", async () => {
|
|
const config = makeConfig({ wifiOnly: true });
|
|
await expect(updateCacheConfig(config)).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("should support all config options", async () => {
|
|
const config = makeConfig({ wifiOnly: true, albumAffinityEnabled: false });
|
|
|
|
await expect(updateCacheConfig(config)).resolves.toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("getCacheConfig", () => {
|
|
it("should get cache config", async () => {
|
|
const config = await getCacheConfig();
|
|
|
|
expect(config).toBeDefined();
|
|
expect(typeof config.queuePrecacheEnabled).toBe("boolean");
|
|
expect(typeof config.queuePrecacheCount).toBe("number");
|
|
expect(typeof config.albumAffinityEnabled).toBe("boolean");
|
|
expect(typeof config.albumAffinityThreshold).toBe("number");
|
|
expect(typeof config.storageLimit).toBe("number");
|
|
expect(typeof config.wifiOnly).toBe("boolean");
|
|
});
|
|
|
|
it("should invoke correct backend command", async () => {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
const invokeSpy = vi.mocked(invoke);
|
|
|
|
await getCacheConfig();
|
|
|
|
const call = invokeSpy.mock.calls.find((c) => c[0] === "player_get_cache_config");
|
|
expect(call).toBeDefined();
|
|
});
|
|
|
|
it("should return valid config structure", async () => {
|
|
const config = await getCacheConfig();
|
|
|
|
expect(config.queuePrecacheEnabled).toBe(true);
|
|
expect(config.queuePrecacheCount).toBe(5);
|
|
expect(config.albumAffinityEnabled).toBe(true);
|
|
expect(config.albumAffinityThreshold).toBe(0.8);
|
|
expect(config.storageLimit).toBe(1024 * 1024 * 1024);
|
|
expect(config.wifiOnly).toBe(false);
|
|
});
|
|
});
|
|
});
|