Files
healer-man/src/game/dungeonSimulationStore.test.ts
T
2026-09-16 14:30:58 -04:00

71 lines
2.8 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import {
DUNGEON_SIMULATION_FIXED_STEP_MS,
fixedSimulationTickCount,
useDungeonSimulationStore,
} from "./dungeonSimulationStore";
import { createSpectatorDungeonParty } from "./partyStore";
import { routeHasExactApproval } from "./dungeonRoutes";
import type { DungeonRouteDefinitionV1, DungeonSimulationSpeed } from "./dungeonSimulationTypes";
const route: DungeonRouteDefinitionV1 = {
schemaVersion: 1,
id: "primary",
dungeonId: "test-dungeon",
mapId: 1,
hash: "a".repeat(64),
generationStatus: "Candidate",
generationIssues: [],
legs: [{
id: "leg-1",
objectiveId: "boss-1",
objectiveName: "Boss",
checkpoints: [[0, 0, 0], [4, 0, 0]],
expectedDistance: 4,
corridorPlanarDistance: 10,
corridorVerticalDistance: 4,
actions: [],
}],
};
describe("dungeon simulation fixed clock", () => {
beforeEach(() => useDungeonSimulationStore.getState().leave());
it("produces the same ordered ticks at every playback speed", () => {
const speeds: readonly DungeonSimulationSpeed[] = [0.5, 1, 2, 4];
for (const speed of speeds) {
const wallDuration = 500 / speed;
const result = fixedSimulationTickCount(0, wallDuration, speed);
expect(result.ticks).toBe(500 / DUNGEON_SIMULATION_FIXED_STEP_MS);
expect(result.accumulatorMs).toBe(0);
}
});
it("freezes while paused and advances exactly one tick per step", () => {
const store = useDungeonSimulationStore.getState();
store.startLive({ dungeonId: route.dungeonId, route, mode: "navigation-only", seed: "clock", startedAt: 10_000 });
store.setRunning(false);
expect(useDungeonSimulationStore.getState().consumeTicks(5_000)).toEqual([]);
expect(useDungeonSimulationStore.getState().elapsedMs).toBe(0);
useDungeonSimulationStore.getState().step();
expect(useDungeonSimulationStore.getState().consumeTicks(0)).toEqual([10_050]);
expect(useDungeonSimulationStore.getState().elapsedMs).toBe(50);
});
});
describe("spectator roster and exact-hash approvals", () => {
it("creates a stable tank, healer, and three damage dealers", () => {
const first = createSpectatorDungeonParty("same-seed", 80, "wow");
const second = createSpectatorDungeonParty("same-seed", 80, "wow");
expect(second).toEqual(first);
expect(first.map((member) => member.role)).toEqual(["tank", "healer", "damage", "damage", "damage"]);
});
it("rejects stale hashes and generator-rejected routes", () => {
const approval = { dungeonId: route.dungeonId, routeId: route.id, routeHash: route.hash, reviewedAt: 1 };
expect(routeHasExactApproval(route, [approval])).toBe(true);
expect(routeHasExactApproval({ ...route, hash: "b".repeat(64) }, [approval])).toBe(false);
expect(routeHasExactApproval({ ...route, generationStatus: "Rejected" }, [approval])).toBe(false);
});
});