Update Healer Man server
This commit is contained in:
1 parent
bd073fb00f
commit
83eac6d476
15 files changed
+438
-272
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { useDungeonRaceStore, type RaceServerState } from "./dungeonRaceStore";
|
||||
import type { CharacterProfile, PlayerSession } from "./types";
|
||||
|
||||
const request = vi.hoisted(() => vi.fn());
|
||||
vi.mock("./onlineAccountClient", () => ({ requestOnlineJson: request }));
|
||||
|
||||
const session: PlayerSession = {
|
||||
kind: "account", ownerId: "player-1", displayName: "Racer", accessToken: "race-token",
|
||||
};
|
||||
const character = { id: "character-1" } as CharacterProfile;
|
||||
const queued: RaceServerState = { status: "queued", serverTime: Date.now(), joinedAt: Date.now() };
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
const promise = new Promise<T>((done) => { resolve = done; });
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
describe("dungeon race exit", () => {
|
||||
beforeEach(() => {
|
||||
request.mockReset();
|
||||
useDungeonRaceStore.getState().reset();
|
||||
});
|
||||
afterEach(() => useDungeonRaceStore.getState().reset());
|
||||
|
||||
it("clears an active race immediately and waits for the server leave before requeuing", async () => {
|
||||
const deleteRequest = deferred<RaceServerState>();
|
||||
request.mockImplementation((path: string, init: RequestInit) =>
|
||||
path === "/api/dungeon-races/queue" && init.method === "DELETE"
|
||||
? deleteRequest.promise : Promise.resolve(queued));
|
||||
await useDungeonRaceStore.getState().startOnline(session, character, "healer", "authored");
|
||||
useDungeonRaceStore.setState({ status: "active", inGame: true, matchId: "old-match",
|
||||
dungeonId: "bite-sized-stormwind-stockade", startAt: Date.now() - 10_000 });
|
||||
|
||||
const leaving = useDungeonRaceStore.getState().leave();
|
||||
expect(useDungeonRaceStore.getState()).toMatchObject({ mode: null, status: "idle", inGame: false,
|
||||
matchId: null, startAt: null });
|
||||
const requeue = useDungeonRaceStore.getState().startOnline(session, character, "healer", "authored");
|
||||
await Promise.resolve();
|
||||
expect(request.mock.calls.filter(([, init]) => init.method === "POST")).toHaveLength(1);
|
||||
|
||||
deleteRequest.resolve(queued);
|
||||
await Promise.all([leaving, requeue]);
|
||||
expect(request.mock.calls.map(([, init]) => init.method)).toEqual(["POST", "DELETE", "POST"]);
|
||||
expect(useDungeonRaceStore.getState().status).toBe("queued");
|
||||
});
|
||||
|
||||
it("cancels a queue request in flight without restoring its late response", async () => {
|
||||
const firstQueue = deferred<RaceServerState>();
|
||||
request.mockImplementation((path: string, init: RequestInit) =>
|
||||
path === "/api/dungeon-races/queue" && init.method === "POST"
|
||||
? firstQueue.promise : Promise.resolve(queued));
|
||||
const starting = useDungeonRaceStore.getState().startOnline(session, character, "healer", "authored");
|
||||
const leaving = useDungeonRaceStore.getState().leave();
|
||||
expect(useDungeonRaceStore.getState().mode).toBeNull();
|
||||
firstQueue.resolve(queued);
|
||||
await Promise.all([starting, leaving]);
|
||||
expect(useDungeonRaceStore.getState()).toMatchObject({ mode: null, status: "idle", matchId: null });
|
||||
expect(request.mock.calls.map(([, init]) => init.method)).toEqual(["POST", "DELETE"]);
|
||||
});
|
||||
|
||||
it("ignores an older preparation response after the match has started", async () => {
|
||||
const stale = deferred<RaceServerState>();
|
||||
request.mockImplementation((path: string) => path === "/api/dungeon-races/state"
|
||||
? stale.promise : Promise.resolve(queued));
|
||||
await useDungeonRaceStore.getState().startOnline(session, character, "healer", "authored");
|
||||
useDungeonRaceStore.setState({ status: "active", matchId: "race-1", inGame: true });
|
||||
const syncing = useDungeonRaceStore.getState().sync();
|
||||
stale.resolve({ status: "preparing", matchId: "race-1", serverTime: Date.now() });
|
||||
await syncing;
|
||||
expect(useDungeonRaceStore.getState().status).toBe("active");
|
||||
});
|
||||
|
||||
it("ends offline practice locally without a server request", async () => {
|
||||
useDungeonRaceStore.setState({ mode: "practice", status: "settled", inGame: true,
|
||||
matchId: "practice-1", startAt: Date.now() - 30_000, result: "loss" });
|
||||
await useDungeonRaceStore.getState().leave();
|
||||
expect(useDungeonRaceStore.getState()).toMatchObject({ mode: null, status: "idle", inGame: false,
|
||||
matchId: null, startAt: null, result: null });
|
||||
expect(request).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user