import { beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ reads: [] as unknown[], rows: new Map() })); vi.mock("server-only", () => ({})); vi.mock("@/lib/media/storage", () => ({ publicMediaUrl: (key: string) => `/media/${key}` })); vi.mock("@/db", () => ({ getDb: () => ({ select: () => { let table: unknown; const query = { from(value: unknown) { table = value; return query; }, innerJoin() { return query; }, leftJoin() { return query; }, where() { return query; }, orderBy() { return query; }, limit() { return query; }, then(resolve: (rows: unknown[]) => unknown) { mocks.reads.push(table); return Promise.resolve(mocks.rows.get(table) ?? []).then(resolve); }, }; return query; }, }), })); import { artifactConditions, artifactProfiles, constellationConditions, extraSectionRows, guides, guideSections, teamCompositions, teamConditions, teamMembers, weaponConditions, catalogCharacters, catalogMaterials, guidePosterBleeding, media, weaponRecommendations, artifactRecommendations, } from "@/db/schema"; import { getActiveCatalog, getAdminGuideContent, getPublicGuide, listAdminTeamImportsForCharacter } from "./queries"; beforeEach(() => { mocks.reads.length = 0; mocks.rows.clear(); mocks.rows.set(guides, [{ guide: { id: "guide", slug: "test", posterArtMediaId: null }, cover: { objectKey: "cover", status: "ready" }, character: { key: "test", imageKey: "character", constellations: [], talents: [], ascension: [], leveling: [] }, }]); mocks.rows.set(guideSections, [ { id: "extra-a", slug: "extra-a", kind: "extra" }, { id: "extra-b", slug: "extra-b", kind: "extra" }, ]); }); describe("admin section reads", () => { it.each([ ["weapon", weaponConditions], ["artifact", artifactConditions], ["constellations", constellationConditions], ["team", teamConditions], ])("loads only the %s section's conditions", async (section, expectedTable) => { const result = await getAdminGuideContent("test", section); expect(result).not.toBeNull(); expect(mocks.reads).toContain(expectedTable); for (const table of [weaponConditions, artifactConditions, constellationConditions, teamConditions]) { if (table !== expectedTable) expect(mocks.reads).not.toContain(table); } expect(mocks.reads).not.toContain(extraSectionRows); expect(mocks.reads).not.toContain(catalogMaterials); expect(mocks.reads).not.toContain(guidePosterBleeding); }); it("loads just the selected extra section", async () => { const result = await getAdminGuideContent("test", "extra-b"); expect(result?.extras.map((extra) => extra.id)).toEqual(["extra-b"]); expect(mocks.reads).toContain(extraSectionRows); expect(mocks.reads).not.toContain(weaponConditions); expect(mocks.reads).not.toContain(teamConditions); }); it("preserves full-guide reads for existing preview and validation callers", async () => { const result = await getAdminGuideContent("test"); for (const table of [weaponConditions, artifactConditions, constellationConditions, teamConditions, extraSectionRows]) { expect(mocks.reads).toContain(table); } expect(result?.extras).toHaveLength(2); }); it("resolves condition icons from one batched media read", async () => { const iconMediaId = "550e8400-e29b-41d4-a716-446655440000"; mocks.rows.set(weaponConditions, [{ id: "weapon-condition", iconMediaId }]); mocks.rows.set(artifactConditions, [{ id: "artifact-condition", iconMediaId }]); mocks.rows.set(constellationConditions, [{ id: "constellation-condition", iconMediaId }]); mocks.rows.set(teamConditions, [{ id: "team-condition", iconMediaId }]); mocks.rows.set(artifactProfiles, [{ sands: [], goblet: [], circlet: [], substats: [], targets: [], recommendedConditions: [{ name: "Recommended", note: null, iconMediaId, sands: [], goblet: [], circlet: [], substats: [] }], targetConditions: [{ name: "Target", note: null, iconMediaId, rows: [] }], }]); mocks.rows.set(media, [{ id: iconMediaId, objectKey: "condition-icon.png" }]); const result = await getAdminGuideContent("test"); expect(result?.weaponGroups[0].iconUrl).toBe("/media/condition-icon.png"); expect(result?.artifactGroups[0].iconUrl).toBe("/media/condition-icon.png"); expect(result?.constellationGroups[0].iconUrl).toBe("/media/condition-icon.png"); expect(result?.teamGroups[0].iconUrl).toBe("/media/condition-icon.png"); expect(result?.artifactProfile?.recommendedConditions[0].iconUrl).toBe("/media/condition-icon.png"); expect(result?.artifactProfile?.targetConditions[0].iconUrl).toBe("/media/condition-icon.png"); expect(mocks.reads.filter((table) => table === media)).toHaveLength(1); }); }); describe("character material metadata", () => { it.each(["catalog", "overview"] as const)( "preserves ascension and leveling metadata in the %s read", async (read) => { const character = { key: "test", imageKey: "character", constellations: [], talents: [], ascension: [{ type: "ascension", items: [{ key: "known", amount: 2 }, { key: "missing/key", amount: 3 }] }], leveling: [{ type: "leveling", items: [{ key: "missing/key", amount: 5 }, { key: "known", amount: 7 }] }], }; const original = structuredClone(character); mocks.rows.set(catalogCharacters, [character]); mocks.rows.set(guides, [{ guide: { id: "guide", slug: "test", posterArtMediaId: null }, cover: { objectKey: "cover", status: "ready" }, character, }]); mocks.rows.set(catalogMaterials, [{ key: "known", name: "Known material", description: "Description", rarity: 4, type: "upgrade", imageKey: "material.webp", }]); const result = read === "catalog" ? (await getActiveCatalog())?.characters[0] : (await getAdminGuideContent("test", "overview"))?.character; const known = { key: "known", name: "Known material", description: "Description", rarity: 4, type: "upgrade", imageUrl: "/media/material.webp", lunarisUrl: "https://lunaris.moe/material/known", available: true, }; const missing = { key: "missing/key", name: "missing/key", description: null, rarity: null, type: null, imageUrl: null, lunarisUrl: "https://lunaris.moe/material/missing%2Fkey", available: false, }; expect(result?.ascension).toEqual([{ type: "ascension", items: [{ ...known, amount: 2 }, { ...missing, amount: 3 }] }]); expect(result?.leveling).toEqual([{ type: "leveling", items: [{ ...missing, amount: 5 }, { ...known, amount: 7 }] }]); expect(mocks.reads.filter((table) => table === catalogMaterials)).toHaveLength(1); expect(character).toEqual(original); }, ); }); describe("team import lookup", () => { it("hydrates matching teams and source labels with one member read", async () => { mocks.rows.set(teamCompositions, [{ id: "team-1", title: "Example", rotationSeconds: "20", combo: "E > Q", guideName: "Other Guide", conditionName: "Boss", }]); mocks.rows.set(teamMembers, [{ member: { teamId: "team-1", characterKey: "character-1", weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: null, constellation: "2", refinement: "1", damage: "100", }, characterName: "Character One", }]); const result = await listAdminTeamImportsForCharacter("current-guide", "character-1"); expect(result).toEqual([{ id: "team-1", title: "Example", rotationSeconds: "20", combo: "E > Q", guideName: "Other Guide", conditionName: "Boss", members: [{ characterKey: "character-1", characterName: "Character One", weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: null, constellation: "2", refinement: "1", damage: "100", }], }]); expect(mocks.reads).toEqual([teamCompositions, teamMembers]); }); }); describe("public overview build summary", () => { it("loads small recommendation reads only for published sections", async () => { mocks.rows.set(guideSections, [{ section: { id: "weapon", slug: "weapon", kind: "weapon" }, populated: true }]); mocks.rows.set(weaponRecommendations, [ { name: "First", rarity: 5, refinement: "1", conditionId: "first" }, { name: "Four-star", rarity: 4, refinement: "5", conditionId: "first" }, { name: "Different condition", rarity: 4, refinement: "1", conditionId: "second" }, ]); const result = await getPublicGuide("test"); expect(result?.buildSummary?.weapon?.name).toBe("First"); expect(result?.buildSummary?.fourStarWeapon?.name).toBe("Four-star"); expect(mocks.reads).not.toContain(artifactProfiles); expect(mocks.reads).not.toContain(artifactRecommendations); expect(mocks.reads).not.toContain(teamMembers); }); it("does not add overview recommendation reads to admin editing", async () => { await getAdminGuideContent("test", "overview"); expect(mocks.reads).not.toContain(weaponRecommendations); expect(mocks.reads).not.toContain(artifactProfiles); }); it("does not disclose recommendations from hidden sections", async () => { mocks.rows.set(guideSections, []); mocks.rows.set(weaponRecommendations, [{ name: "Hidden", conditionId: "hidden", rarity: 5, refinement: "1" }]); const result = await getPublicGuide("test"); expect(result?.buildSummary?.weapon).toBeNull(); expect(mocks.reads).not.toContain(weaponRecommendations); }); });