import { describe, expect, it, vi } from "vitest"; import type { OnlineGroupActivity } from "./onlineGroupTypes"; import { enterOnlineGroupActivity, onlineGroupActivityNeedsEntry } from "./OnlineGroupCoordinator"; import { SHELVED_MODES_ENABLED } from "./releaseFeatures"; const normalReleaseIt = SHELVED_MODES_ENABLED ? it.skip : it; const shelvedModeIt = SHELVED_MODES_ENABLED ? it : it.skip; const DUNGEON_ACTIVITY: OnlineGroupActivity = { id: "activity-dungeon-1", type: "dungeon", contentId: "deadmines", fillWithAi: true, partySize: 5, startingLevel: null, startedAt: 1, }; const MANASTORM_ACTIVITY: OnlineGroupActivity = { ...DUNGEON_ACTIVITY, id: "activity-manastorm-1", type: "manastorm", partySize: 3, startingLevel: 6, }; function entryDependencies(enterDungeon: () => boolean) { return { prepareDungeonContent: vi.fn(async () => undefined), prepareRandomizedDungeonContent: vi.fn(async () => undefined), installDungeonRuntime: vi.fn(async () => undefined), enterDungeon: vi.fn(enterDungeon), installManastormRuntime: vi.fn(async () => undefined), enterManastorm: vi.fn(() => true), }; } describe("online group activity entry", () => { it("recognizes a newly published activity while the previous activity is still active", () => { const next = { ...DUNGEON_ACTIVITY, id: "activity-dungeon-2", startedAt: 10_000 }; expect(onlineGroupActivityNeedsEntry(next, DUNGEON_ACTIVITY, [DUNGEON_ACTIVITY.id], 10_100)).toBe(true); expect(onlineGroupActivityNeedsEntry(DUNGEON_ACTIVITY, DUNGEON_ACTIVITY, [], 10_100)).toBe(false); }); it("enters with the leader's source variant and boss difficulty as a fresh shared run", async () => { const dependencies = entryDependencies(() => true); await enterOnlineGroupActivity({ ...DUNGEON_ACTIVITY, difficultyId: "heroic", bossDifficulty: 9 }, "damage", vi.fn(), dependencies); expect(dependencies.prepareDungeonContent).toHaveBeenCalledWith("deadmines"); expect(dependencies.prepareDungeonContent.mock.invocationCallOrder[0]) .toBeLessThan(dependencies.enterDungeon.mock.invocationCallOrder[0]); expect(dependencies.enterDungeon).toHaveBeenCalledWith("deadmines", "damage", { difficultyId: "heroic", bossDifficulty: 9, freshRun: true, runId: DUNGEON_ACTIVITY.id, }); }); it("downloads the seeded randomized creature packs before shared entry", async () => { const dependencies = entryDependencies(() => true); const activity = { ...DUNGEON_ACTIVITY, id: "shared-randomized-seed", contentId: "randomized-bite-sized-stormwind-stockade", }; await enterOnlineGroupActivity(activity, "healer", vi.fn(), dependencies); expect(dependencies.prepareDungeonContent).toHaveBeenCalledWith(activity.contentId); expect(dependencies.prepareRandomizedDungeonContent).toHaveBeenCalledWith(activity.id, activity.contentId); expect(dependencies.prepareRandomizedDungeonContent.mock.invocationCallOrder[0]) .toBeLessThan(dependencies.enterDungeon.mock.invocationCallOrder[0]); }); it("does not enter a shared dungeon when its content download fails", async () => { const dependencies = entryDependencies(() => true); dependencies.prepareDungeonContent.mockRejectedValueOnce(new Error("Download failed")); const consumeActivity = vi.fn(); await expect(enterOnlineGroupActivity(DUNGEON_ACTIVITY, "healer", consumeActivity, dependencies)) .rejects.toThrow("Download failed"); expect(dependencies.enterDungeon).not.toHaveBeenCalled(); expect(consumeActivity).not.toHaveBeenCalled(); }); it("leaves a dungeon activity unconsumed when dungeon entry fails", async () => { const consumeActivity = vi.fn(); const dependencies = entryDependencies(() => false); await expect(enterOnlineGroupActivity( DUNGEON_ACTIVITY, "healer", consumeActivity, dependencies, )).resolves.toBe(false); expect(dependencies.installDungeonRuntime).toHaveBeenCalledOnce(); expect(dependencies.enterDungeon).toHaveBeenCalledWith("deadmines", "healer", { difficultyId: undefined, bossDifficulty: undefined, freshRun: true, runId: DUNGEON_ACTIVITY.id, }); expect(consumeActivity).not.toHaveBeenCalled(); }); it("can retry the same dungeon activity and consumes it only after entry succeeds", async () => { const consumeActivity = vi.fn(); const enterDungeon = vi.fn() .mockReturnValueOnce(false) .mockReturnValueOnce(true); const dependencies = entryDependencies(enterDungeon); await enterOnlineGroupActivity(DUNGEON_ACTIVITY, "tank", consumeActivity, dependencies); expect(consumeActivity).not.toHaveBeenCalled(); await expect(enterOnlineGroupActivity( DUNGEON_ACTIVITY, "tank", consumeActivity, dependencies, )).resolves.toBe(true); expect(dependencies.enterDungeon).toHaveBeenCalledTimes(2); expect(consumeActivity).toHaveBeenCalledOnce(); expect(consumeActivity).toHaveBeenCalledWith(DUNGEON_ACTIVITY.id); }); it("does not consume the activity when loading the dungeon runtime throws", async () => { const consumeActivity = vi.fn(); const dependencies = entryDependencies(() => true); dependencies.installDungeonRuntime.mockRejectedValueOnce(new Error("runtime unavailable")); await expect(enterOnlineGroupActivity( DUNGEON_ACTIVITY, "damage", consumeActivity, dependencies, )).rejects.toThrow("runtime unavailable"); expect(dependencies.enterDungeon).not.toHaveBeenCalled(); expect(consumeActivity).not.toHaveBeenCalled(); }); normalReleaseIt("does not load or enter a shelved group activity", async () => { const consumeActivity = vi.fn(); const dependencies = entryDependencies(() => true); await expect(enterOnlineGroupActivity( MANASTORM_ACTIVITY, "healer", consumeActivity, dependencies, )).resolves.toBe(false); expect(dependencies.installManastormRuntime).not.toHaveBeenCalled(); expect(dependencies.enterManastorm).not.toHaveBeenCalled(); expect(consumeActivity).not.toHaveBeenCalled(); }); shelvedModeIt("restores Manastorm group entry in the developer mode", async () => { const consumeActivity = vi.fn(); const dependencies = entryDependencies(() => true); await expect(enterOnlineGroupActivity( MANASTORM_ACTIVITY, "healer", consumeActivity, dependencies, )).resolves.toBe(true); expect(dependencies.installManastormRuntime).toHaveBeenCalledOnce(); expect(dependencies.enterManastorm).toHaveBeenCalledWith("healer", 3, 6); expect(consumeActivity).toHaveBeenCalledWith(MANASTORM_ACTIVITY.id); }); });