Files
healer-man/src/game/dungeonCatalog.test.ts
T
2026-09-27 10:16:42 -04:00

157 lines
7.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
DUNGEON_CATALOG,
DUNGEON_CATEGORIES,
dungeonById,
dungeonsByCategory,
randomDungeonByCategory,
randomRegularDungeon,
randomizedBiteSizedDungeons,
randomRandomizedBiteSizedDungeon,
} from "./dungeonCatalog";
import { DUNGEON_MANIFEST } from "./dungeonManifest";
import { RUNEWAKER_DUNGEON_DEFINITIONS } from "./generated/runewakerDungeonDefinitions.generated";
import { compareDungeonReleaseOrder } from "./dungeonReleaseStage";
import availability from "./generated/dungeonAvailability.json";
import queuePolicyCatalog from "./generated/dungeonLeaderboardCatalog.generated.json";
describe("dungeon catalog", () => {
it("lists the complete campaign and bite-sized variants", () => {
expect(DUNGEON_CATALOG).toHaveLength(98);
expect(DUNGEON_CATALOG.find((dungeon) => dungeon.id === "stormwind-stockade")).toMatchObject({
id: "stormwind-stockade",
releaseStage: "beta",
available: true,
});
expect(dungeonById(DUNGEON_MANIFEST.id)).toMatchObject({
title: DUNGEON_MANIFEST.title,
mapId: DUNGEON_MANIFEST.mapId,
entranceName: DUNGEON_MANIFEST.entrance.name,
releaseStage: "alpha",
});
expect(DUNGEON_CATALOG.find((dungeon) => dungeon.id === "deadmines")).toMatchObject({
id: "deadmines",
mapId: 36,
available: true,
});
expect(DUNGEON_CATALOG.find((dungeon) => dungeon.id === "forsaken-abbey")).toMatchObject({
id: "forsaken-abbey",
category: "rom",
mapId: 102,
available: true,
});
});
it("separates WoW, RoM, and bite-sized dungeons", () => {
expect(DUNGEON_CATEGORIES.map((category) => category.id)).toEqual(["wow", "rom", "bite-sized"]);
expect(dungeonsByCategory("rom").map((dungeon) => dungeon.id).sort()).toEqual([
"forsaken-abbey",
...RUNEWAKER_DUNGEON_DEFINITIONS.map((definition) => definition.id),
].sort());
expect(dungeonsByCategory("wow")).not.toContainEqual(
expect.objectContaining({ id: "forsaken-abbey" }),
);
expect(dungeonsByCategory("rom")).toHaveLength(30);
expect(dungeonsByCategory("bite-sized").map((dungeon) => dungeon.id).sort())
.toEqual(["bite-sized-barren-caves-anglo", "bite-sized-deadmines-ship", "bite-sized-forsaken-abbey-ancalon",
"bite-sized-grafu-castle-barkud", "bite-sized-hall-of-survivors-pursuer", "bite-sized-hoto-teeth",
"bite-sized-kawaks-tomb-linobia", "bite-sized-mystic-altar-golem", "bite-sized-paspers-shrine-hafiz",
"bite-sized-stormwind-stockade", "bite-sized-utgarde-pinnacle-skadi",
"bite-sized-wailing-caverns"]);
});
it("selects another available category dungeon", () => {
expect(randomDungeonByCategory("bite-sized", "bite-sized-stormwind-stockade", () => 0)?.id)
.not.toBe("bite-sized-stormwind-stockade");
expect(randomDungeonByCategory("wow", "stormwind-stockade", () => 0)?.id)
.not.toBe("stormwind-stockade");
});
it("selects regular full dungeons without entering either Bite-Sized pool", () => {
const first = randomRegularDungeon(undefined, () => 0);
const last = randomRegularDungeon(undefined, () => 0.999999);
for (const dungeon of [first, last]) {
expect(dungeon?.available).toBe(true);
expect(["wow", "rom"]).toContain(dungeon?.category);
}
expect(randomRegularDungeon(first?.id, () => 0)?.id).not.toBe(first?.id);
expect(randomRegularDungeon(undefined, () => 0, () => false)).toBeNull();
});
it("keeps randomized replay in the separate randomized Bite-Sized location pool", () => {
expect(randomizedBiteSizedDungeons().map((dungeon) => dungeon.id))
.toEqual(["randomized-bite-sized-stormwind-stockade", "randomized-bite-sized-stormwind-stockade-available"]);
expect(randomRandomizedBiteSizedDungeon("randomized-bite-sized-stormwind-stockade", () => 0)?.id)
.toBe("randomized-bite-sized-stormwind-stockade-available");
});
it("keeps stable unique ids and resolves only catalogued dungeons", () => {
const ids = DUNGEON_CATALOG.map((dungeon) => dungeon.id);
expect(new Set(ids).size).toBe(ids.length);
expect(dungeonById(DUNGEON_MANIFEST.id)).toBe(DUNGEON_CATALOG.find((dungeon) => dungeon.id === DUNGEON_MANIFEST.id));
expect(dungeonById("missing-dungeon")).toBeNull();
});
it("keeps every queue candidate in the server policy validation catalog", () => {
const policyIds = new Set(queuePolicyCatalog.dungeons.map((dungeon) => dungeon.id));
expect(DUNGEON_CATALOG.every((dungeon) => policyIds.has(dungeon.id))).toBe(true);
});
it("provides centralized display copy for every entry", () => {
for (const dungeon of DUNGEON_CATALOG) {
expect(dungeon.location.trim()).not.toBe("");
expect(dungeon.theme.trim()).not.toBe("");
expect(dungeon.summary.trim()).not.toBe("");
}
});
it("keeps all Stockades experiences in Beta and alphabetizes each release group", () => {
expect(DUNGEON_CATALOG.filter((dungeon) => dungeon.releaseStage === "beta").map((dungeon) => dungeon.id))
.toEqual([
"bite-sized-barren-caves-anglo",
"bite-sized-forsaken-abbey-ancalon",
"bite-sized-grafu-castle-barkud",
"bite-sized-hall-of-survivors-pursuer",
"bite-sized-hoto-teeth",
"bite-sized-kawaks-tomb-linobia",
"bite-sized-mystic-altar-golem",
"bite-sized-paspers-shrine-hafiz",
"randomized-bite-sized-stormwind-stockade",
"randomized-bite-sized-stormwind-stockade-available",
"stormwind-stockade",
"bite-sized-stormwind-stockade",
"bite-sized-deadmines-ship",
"bite-sized-utgarde-pinnacle-skadi",
"bite-sized-wailing-caverns",
]);
expect(DUNGEON_CATALOG.filter((dungeon) => !dungeon.id.includes("stormwind-stockade")
&& !dungeon.id.startsWith("bite-sized-"))
.every((dungeon) => dungeon.releaseStage === "alpha"))
.toBe(true);
for (const category of DUNGEON_CATEGORIES) {
const dungeons = dungeonsByCategory(category.id);
expect(dungeons).toEqual([...dungeons].sort(compareDungeonReleaseOrder));
}
const stages = [
{ title: "Zeta", releaseStage: "alpha" as const },
{ title: "Beta Z", releaseStage: "beta" as const },
{ title: "Released Z", releaseStage: "full-release" as const },
{ title: "Alpha", releaseStage: "alpha" as const },
{ title: "Beta A", releaseStage: "beta" as const },
{ title: "Released A", releaseStage: "full-release" as const },
];
expect(stages.sort(compareDungeonReleaseOrder).map((entry) => entry.title))
.toEqual(["Released A", "Released Z", "Beta A", "Beta Z", "Alpha", "Zeta"]);
});
it("shares the complete dungeon identity list used to validate reports on the server", () => {
expect(DUNGEON_CATALOG.map((dungeon) => dungeon.id).sort())
.toEqual(["bite-sized-barren-caves-anglo", "bite-sized-deadmines-ship", "bite-sized-forsaken-abbey-ancalon",
"bite-sized-grafu-castle-barkud", "bite-sized-hall-of-survivors-pursuer", "bite-sized-hoto-teeth",
"bite-sized-kawaks-tomb-linobia", "bite-sized-mystic-altar-golem", "bite-sized-paspers-shrine-hafiz", "bite-sized-stormwind-stockade",
"bite-sized-utgarde-pinnacle-skadi", "bite-sized-wailing-caverns",
"randomized-bite-sized-stormwind-stockade", "randomized-bite-sized-stormwind-stockade-available", "forsaken-abbey",
...Object.keys(availability.enabled)].sort());
});
});