import { describe, expect, it } from "vitest"; import { DUNGEON_CATALOG } from "./dungeonCatalog"; import { configWithGmReviewNote, configWithGmReviewStatus, describeGmReviewTarget, drawRandomGmReviewTarget, gmReviewQueue, type GmReviewTarget, } from "./gmContentReview"; import { DEFAULT_MANASTORM_ADMIN_CONFIG, resolvedBossAdminCatalog, type GmReadinessStatus, type ManastormAdminConfig, } from "./manastormAdminConfig"; function configWithBossStatuses(statuses: Readonly>): ManastormAdminConfig { return { ...DEFAULT_MANASTORM_ADMIN_CONFIG, bosses: Object.fromEntries(Object.entries(statuses).map(([key, status]) => [key, { status }])), }; } describe("random GM content review", () => { it("draws Needs tested dungeons before Not ready and never draws Ready", () => { const available = DUNGEON_CATALOG.filter((dungeon) => dungeon.available); expect(available.length).toBeGreaterThan(2); const config: ManastormAdminConfig = { ...DEFAULT_MANASTORM_ADMIN_CONFIG, dungeons: Object.fromEntries(available.map((dungeon, index) => [ dungeon.id, { status: index === 0 ? "needs-tested" : index === 1 ? "not-ready" : "ready" }, ])), }; expect(drawRandomGmReviewTarget("dungeon", config, null, () => 0.99)).toEqual({ kind: "dungeon", dungeonId: available[0].id, }); const withoutNeedsTested = configWithGmReviewStatus( config, { kind: "dungeon", dungeonId: available[0].id }, "ready", )!; expect(drawRandomGmReviewTarget("dungeon", withoutNeedsTested, null, () => 0)).toEqual({ kind: "dungeon", dungeonId: available[1].id, }); }); it("uses separate dungeon-boss and imported-Manastorm source queues", () => { const catalog = resolvedBossAdminCatalog(DEFAULT_MANASTORM_ADMIN_CONFIG); const dungeonBosses = catalog.filter((entry) => entry.runnable && entry.source.includes("dungeon")); const manastorms = catalog.filter((entry) => entry.runnable && entry.source.includes("installed-manastorm")); expect(dungeonBosses.length).toBeGreaterThan(0); expect(manastorms.length).toBeGreaterThan(0); const bossQueue = gmReviewQueue("boss", DEFAULT_MANASTORM_ADMIN_CONFIG); const manastormQueue = gmReviewQueue("manastorm", DEFAULT_MANASTORM_ADMIN_CONFIG); expect(bossQueue.every((target) => target.kind === "boss" && dungeonBosses.some((entry) => entry.key === target.bossKey))).toBe(true); expect(manastormQueue.every((target) => target.kind === "manastorm" && manastorms.some((entry) => entry.key === target.bossKey))).toBe(true); const overlap = catalog.find((entry) => entry.runnable && entry.source.includes("dungeon") && entry.source.includes("installed-manastorm")); expect(overlap).toBeTruthy(); expect(bossQueue).toContainEqual({ kind: "boss", bossKey: overlap!.key }); expect(manastormQueue).toContainEqual({ kind: "manastorm", bossKey: overlap!.key }); }); it("avoids an immediate repeat when another prioritized target exists", () => { const targets = gmReviewQueue("manastorm", DEFAULT_MANASTORM_ADMIN_CONFIG); expect(targets.length).toBeGreaterThan(1); const previous = targets[0] as GmReviewTarget; const next = drawRandomGmReviewTarget("manastorm", DEFAULT_MANASTORM_ADMIN_CONFIG, previous, () => 0); expect(next).not.toEqual(previous); }); it("returns no target when every runnable record is Ready", () => { const catalog = resolvedBossAdminCatalog(DEFAULT_MANASTORM_ADMIN_CONFIG); const config = configWithBossStatuses(Object.fromEntries(catalog.map((entry) => [entry.key, "ready"]))); expect(drawRandomGmReviewTarget("boss", config, null, () => 0)).toBeNull(); expect(drawRandomGmReviewTarget("manastorm", config, null, () => 0)).toBeNull(); }); it("updates the shared boss record and resolves exact review details", () => { const target = gmReviewQueue("manastorm", DEFAULT_MANASTORM_ADMIN_CONFIG)[0]; expect(target?.kind).toBe("manastorm"); const updated = configWithGmReviewStatus(DEFAULT_MANASTORM_ADMIN_CONFIG, target!, "not-ready"); expect(updated).toBeTruthy(); const descriptor = describeGmReviewTarget(target!, updated!); expect(descriptor).toMatchObject({ status: "not-ready", runnable: true }); if (target?.kind !== "dungeon") expect(updated!.bosses[target!.bossKey]?.status).toBe("not-ready"); }); it("keeps one shared review note across overlapping Boss and Manastorm targets", () => { const overlap = resolvedBossAdminCatalog(DEFAULT_MANASTORM_ADMIN_CONFIG).find((entry) => ( entry.runnable && entry.source.includes("dungeon") && entry.source.includes("installed-manastorm") )); expect(overlap).toBeTruthy(); const bossTarget = { kind: "boss", bossKey: overlap!.key } as const; const manastormTarget = { kind: "manastorm", bossKey: overlap!.key } as const; const noted = configWithGmReviewNote( DEFAULT_MANASTORM_ADMIN_CONFIG, bossTarget, "Portal clips the west wall.", )!; const changedStatus = configWithGmReviewStatus(noted, manastormTarget, "not-ready")!; expect(describeGmReviewTarget(manastormTarget, changedStatus)).toMatchObject({ status: "not-ready", note: "Portal clips the west wall.", }); expect(changedStatus.bosses[overlap!.key]).toMatchObject({ status: "not-ready", note: "Portal clips the west wall.", }); }); it("adds and clears advisory dungeon notes without changing availability", () => { const dungeon = DUNGEON_CATALOG.find((entry) => entry.available)!; const target = { kind: "dungeon", dungeonId: dungeon.id } as const; const noted = configWithGmReviewNote(DEFAULT_MANASTORM_ADMIN_CONFIG, target, "Verify the final door.")!; const cleared = configWithGmReviewNote(noted, target, "")!; expect(describeGmReviewTarget(target, noted)).toMatchObject({ note: "Verify the final door.", runnable: true }); expect(cleared.dungeons[dungeon.id]?.note).toBeUndefined(); expect(dungeon.available).toBe(true); }); });