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); }); });