import type { Creature } from "@initiative/domain"; import { creatureId } from "@initiative/domain"; import { beforeEach, describe, expect, it, vi } from "vitest"; // Mock idb to reject — simulates IndexedDB unavailable. // This must be a separate file from bestiary-cache.test.ts because the // module caches the db connection in a singleton; once openDB succeeds // in one test, the fallback path is unreachable. vi.mock("idb", () => ({ openDB: vi.fn().mockRejectedValue(new Error("IndexedDB unavailable")), })); const { cacheSource, isSourceCached, getCachedSources, clearSource, clearAll, loadAllCachedCreatures, } = await import("../bestiary-cache.js"); function makeCreature(id: string, name: string): Creature { return { id: creatureId(id), name, source: "MM", sourceDisplayName: "Monster Manual", size: "Small", type: "humanoid", alignment: "neutral evil", ac: 15, hp: { average: 7, formula: "2d6" }, speed: "30 ft.", abilities: { str: 8, dex: 14, con: 10, int: 10, wis: 8, cha: 8 }, cr: "1/4", initiativeProficiency: 0, proficiencyBonus: 2, passive: 9, }; } describe("bestiary-cache fallback (IndexedDB unavailable)", () => { beforeEach(async () => { await clearAll(); }); it("cacheSource falls back to in-memory store", async () => { const creatures = [makeCreature("mm:goblin", "Goblin")]; await cacheSource("dnd", "MM", "Monster Manual", creatures); expect(await isSourceCached("dnd", "MM")).toBe(true); }); it("isSourceCached returns false for uncached source", async () => { expect(await isSourceCached("dnd", "XGE")).toBe(false); }); it("getCachedSources returns sources from in-memory store", async () => { await cacheSource("dnd", "MM", "Monster Manual", [ makeCreature("mm:goblin", "Goblin"), ]); const sources = await getCachedSources(); expect(sources).toHaveLength(1); expect(sources[0].sourceCode).toBe("MM"); expect(sources[0].creatureCount).toBe(1); }); it("loadAllCachedCreatures assembles creatures from in-memory store", async () => { const goblin = makeCreature("mm:goblin", "Goblin"); await cacheSource("dnd", "MM", "Monster Manual", [goblin]); const map = await loadAllCachedCreatures(); expect(map.size).toBe(1); expect(map.get(creatureId("mm:goblin"))?.name).toBe("Goblin"); }); it("clearSource removes a single source from in-memory store", async () => { await cacheSource("dnd", "MM", "Monster Manual", []); await cacheSource("dnd", "VGM", "Volo's Guide", []); await clearSource("dnd", "MM"); expect(await isSourceCached("dnd", "MM")).toBe(false); expect(await isSourceCached("dnd", "VGM")).toBe(true); }); it("clearAll removes all data from in-memory store", async () => { await cacheSource("dnd", "MM", "Monster Manual", []); await clearAll(); const sources = await getCachedSources(); expect(sources).toEqual([]); }); });