import { describe, it, expect, vi, beforeEach } from "vitest"; // UT-068: `pushCatalogVisibility` resolves `serverReachable || showCatalog` and // pushes the result to the backend whenever either input changes. // See docs/architecture/06-downloads-and-offline.md, "Offline Catalog // Visibility" (DR-078/DR-079). // // `isConnected` now tracks server reachability alone (DR-079), so from this // service's perspective its input is "is the server reachable". We drive it and // the `showServerCatalog` toggle and assert what gets pushed via // `commands.setShowServerCatalog`. const h = vi.hoisted(() => { function shim(initial: T) { let value = initial; const subs = new Set<(v: T) => void>(); return { set(v: T) { value = v; subs.forEach((fn) => fn(value)); }, subscribe(fn: (v: T) => void) { subs.add(fn); fn(value); return () => subs.delete(fn); }, // Drop subscribers left behind by module instances discarded via // `vi.resetModules()`. Without this, every previously-imported copy of the // service still reacts to `set()` and pushes its own visibility value. reset(v: T) { subs.clear(); value = v; }, }; } return { isConnectedStore: shim(true), setShowServerCatalog: vi.fn(async () => {}), }; }); // Prime the module graph once, at collection time, instead of inside a test. // // Every test re-imports the service after `vi.resetModules()` so it gets a fresh // set of module-level subscriptions. The *first* of those imports also pays to // transform the service and its dependency graph — around a second of real // wall-clock work with a cold Vite cache. Charged to a test body that cost sat // close enough to vitest's 5s default that suite-wide contention (many workers // transforming at once) tipped this file into a timeout, while running the file // alone always passed. Warming here moves the compile out of the timed region; // the per-test re-imports that follow are cached and cost ~30ms. // // The timeout is deliberately left at the default: the point is to stop timing // the compiler, not to give it a bigger budget. await import("./offlineCatalog"); vi.mock("$lib/stores/connectivity", () => ({ isConnected: { subscribe: h.isConnectedStore.subscribe }, })); vi.mock("$lib/api/bindings", () => ({ commands: { setShowServerCatalog: h.setShowServerCatalog, syncFullCatalog: vi.fn(), resumeQueuedDownloads: vi.fn(), catalogSyncStatus: vi.fn(), }, })); vi.mock("$lib/stores/auth", () => ({ auth: { getRepository: () => ({ getHandle: () => "handle-1" }) }, })); describe("pushCatalogVisibility resolves reachable || showCatalog (UT-068)", () => { beforeEach(() => { h.isConnectedStore.reset(true); h.setShowServerCatalog.mockClear(); vi.resetModules(); }); it("pushes include=true while the server is reachable (initial subscribe)", async () => { const { showServerCatalog } = await import("./offlineCatalog"); // Initial subscription with reachable=true, showCatalog=false ⇒ include=true. expect(h.setShowServerCatalog).toHaveBeenLastCalledWith(true); // Keep the import binding referenced so tree-shaking never elides it. expect(showServerCatalog).toBeDefined(); }); it("pushes include=false when unreachable and the toggle is off", async () => { h.isConnectedStore.set(false); await import("./offlineCatalog"); // Fresh module subscribes with reachable=false, showCatalog=false ⇒ false. expect(h.setShowServerCatalog).toHaveBeenLastCalledWith(false); }); it("re-pushes include=true when the toggle flips on while unreachable", async () => { h.isConnectedStore.set(false); const { showServerCatalog } = await import("./offlineCatalog"); h.setShowServerCatalog.mockClear(); showServerCatalog.set(true); // showCatalog input changes ⇒ include flips to true expect(h.setShowServerCatalog).toHaveBeenLastCalledWith(true); h.setShowServerCatalog.mockClear(); showServerCatalog.set(false); // back off ⇒ include flips to false expect(h.setShowServerCatalog).toHaveBeenLastCalledWith(false); }); it("does not re-push when the resolved value is unchanged", async () => { // reachable=true ⇒ include already true. Turning the toggle on keeps it true. const { showServerCatalog } = await import("./offlineCatalog"); h.setShowServerCatalog.mockClear(); showServerCatalog.set(true); // include stays true (true || true) ⇒ no push expect(h.setShowServerCatalog).not.toHaveBeenCalled(); }); });