import { describe, expect, it } from "vitest"; import { getAllSourceCodes, getDefaultFetchUrl, getSourceDisplayName, loadBestiaryIndex, } from "../bestiary-index-adapter.js"; describe("loadBestiaryIndex", () => { it("returns an object with sources and creatures", () => { const index = loadBestiaryIndex(); expect(index.sources).toBeDefined(); expect(index.creatures).toBeDefined(); expect(Array.isArray(index.creatures)).toBe(true); }); it("creatures have the expected shape", () => { const index = loadBestiaryIndex(); expect(index.creatures.length).toBeGreaterThan(0); const first = index.creatures[0]; expect(first).toHaveProperty("name"); expect(first).toHaveProperty("source"); expect(first).toHaveProperty("ac"); expect(first).toHaveProperty("hp"); expect(first).toHaveProperty("dex"); expect(first).toHaveProperty("cr"); expect(first).toHaveProperty("initiativeProficiency"); expect(first).toHaveProperty("size"); expect(first).toHaveProperty("type"); }); it("returns the same cached instance on subsequent calls", () => { const a = loadBestiaryIndex(); const b = loadBestiaryIndex(); expect(a).toBe(b); }); it("sources is a record of source code to display name", () => { const index = loadBestiaryIndex(); const entries = Object.entries(index.sources); expect(entries.length).toBeGreaterThan(0); for (const [code, name] of entries) { expect(typeof code).toBe("string"); expect(typeof name).toBe("string"); expect(code.length).toBeGreaterThan(0); expect(name.length).toBeGreaterThan(0); } }); }); describe("getAllSourceCodes", () => { it("returns all keys from the index sources", () => { const codes = getAllSourceCodes(); const index = loadBestiaryIndex(); expect(codes).toEqual(Object.keys(index.sources)); }); it("returns only strings", () => { for (const code of getAllSourceCodes()) { expect(typeof code).toBe("string"); } }); }); describe("getDefaultFetchUrl", () => { it("returns default GitHub URL when no baseUrl provided", () => { const url = getDefaultFetchUrl("MM"); expect(url).toBe( "https://raw.githubusercontent.com/5etools-mirror-3/5etools-src/main/data/bestiary/bestiary-mm.json", ); }); it("constructs URL from baseUrl with trailing slash", () => { const url = getDefaultFetchUrl("PHB", "https://example.com/data/"); expect(url).toBe("https://example.com/data/bestiary-phb.json"); }); it("normalizes baseUrl without trailing slash", () => { const url = getDefaultFetchUrl("PHB", "https://example.com/data"); expect(url).toBe("https://example.com/data/bestiary-phb.json"); }); it("lowercases the source code in the filename", () => { const url = getDefaultFetchUrl("XMM"); expect(url).toContain("bestiary-xmm.json"); }); it("applies filename override for Plane Shift sources", () => { expect(getDefaultFetchUrl("PSA")).toContain("bestiary-ps-a.json"); expect(getDefaultFetchUrl("PSD")).toContain("bestiary-ps-d.json"); expect(getDefaultFetchUrl("PSK")).toContain("bestiary-ps-k.json"); }); }); describe("getSourceDisplayName", () => { it("returns display name for a known source", () => { const index = loadBestiaryIndex(); const [code, expectedName] = Object.entries(index.sources)[0]; expect(getSourceDisplayName(code)).toBe(expectedName); }); it("falls back to source code for unknown source", () => { expect(getSourceDisplayName("UNKNOWN_SOURCE_XYZ")).toBe( "UNKNOWN_SOURCE_XYZ", ); }); });