Release Healer Man 0.1.43

This commit is contained in:
phenom committed 2026-09-14 13:20:20 -04:00
1 parent c7e8163790
commit ccc01396cd
86 files changed
+2951 -198

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { PlayerSession } from "./types";
function storage() {
const values = new Map<string, string>();
return {
getItem: (key: string) => values.get(key) ?? null,
setItem: (key: string, value: string) => { values.set(key, value); },
removeItem: (key: string) => { values.delete(key); },
};
}
const draft = {
ownerId: "account-one", name: "Collector", raceId: "human" as const,
classId: "priest" as const, specializationId: "priest:holy", gender: "female" as const,
appearance: { skinColor: 0, face: 0, hairStyle: 0, hairColor: 0, feature: 0 },
};
const session: PlayerSession = { kind: "account", ownerId: draft.ownerId, displayName: "Collector", accessToken: "test-token" };
const cards = { counts: { "wow:lady anacondra": { normal: 7, rare: 1 } }, lastClaims: { spawn: "200:1:spawn" } };
beforeEach(() => { vi.useFakeTimers(); });
afterEach(() => { vi.useRealTimers(); vi.unstubAllGlobals(); });
describe("account boss card integration", () => {
it("keeps earned cards when switching characters and isolates account sign-in", async () => {
vi.stubGlobal("window", { localStorage: storage(), sessionStorage: storage() });
const repository = await import("./accountRepository");
const { useShellStore } = await import("./shellStore");
const { useCombatStore } = await import("../game/combatStore");
const first = repository.createCharacter(draft).character!;
const second = repository.createCharacter({ ...draft, name: "Other" }).character!;
const offlineSession: PlayerSession = { ...session, accessToken: undefined };
const shell = useShellStore.getState();
shell.beginSession(offlineSession);
useShellStore.setState({ phase: "game", activeCharacter: first });
shell.saveAccountBossCards(cards);
shell.returnToCharacters();
shell.selectCharacter(second.id);
expect(useShellStore.getState().bossCards).toEqual(cards);
useCombatStore.getState().initializeCharacter({ ...second, bossCards: useShellStore.getState().bossCards });
useCombatStore.getState().collectSharedBossCards({ bossKey: "wow:lady anacondra", spawnId: "spawn", claimId: "300:1:spawn", rarities: ["normal"] }, "Lady Anacondra", 300);
shell.saveAccountBossCards(useCombatStore.getState().bossCards);
shell.selectCharacter(first.id);
expect(useShellStore.getState().bossCards.counts["wow:lady anacondra"]?.normal).toBe(8);
expect(repository.listCharacters(session.ownerId).every((character) => character.bossCards === undefined)).toBe(true);
shell.signOut();
expect(useShellStore.getState().bossCards.counts).toEqual({});
shell.beginSession({ ...offlineSession, ownerId: "account-two" });
expect(useShellStore.getState().bossCards.counts).toEqual({});
shell.beginSession(offlineSession);
expect(useShellStore.getState().bossCards.counts["wow:lady anacondra"]?.normal).toBe(8);
});
it("uploads the account collection without characters and restores it on another device", async () => {
const firstDevice = storage();
const secondDevice = storage();
vi.stubGlobal("window", { localStorage: firstDevice });
let cloudData: unknown = { version: 1, characters: [] };
const fetchMock = vi.fn(async (_url: string, init: RequestInit) => {
if (init.method === "PUT") cloudData = JSON.parse(String(init.body)).data;
return new Response(JSON.stringify({ revision: 1, updatedAt: 1, data: cloudData }), { status: 200 });
});
vi.stubGlobal("fetch", fetchMock);
const repository = await import("./accountRepository");
const client = await import("./onlineAccountClient");
client.activateOnlineSession(session);
repository.saveAccountBossCards(session.ownerId, cards);
await vi.advanceTimersByTimeAsync(501);
expect(cloudData).toMatchObject({ version: 2, characters: [], bossCards: cards });
expect(client.useCloudSaveStatus.getState().state).toBe("synced");
vi.stubGlobal("window", { localStorage: secondDevice });
await client.hydrateOnlineSession(session);
expect(repository.loadAccountBossCards(session.ownerId)).toEqual(cards);
expect(repository.loadAccountBossCards("different-account").counts).toEqual({});
const { collectBossCardDrop } = await import("../game/bossCards");
const restored = repository.loadAccountBossCards(session.ownerId);
expect(collectBossCardDrop(restored, { bossKey: "wow:lady anacondra", spawnId: "spawn", claimId: "200:1:spawn", rarities: ["normal", "rare"] })).toBe(restored);
await client.logoutOnlineSession(session);
});
it("publishes legacy cloud migration once and preserves it through repeated downloads", async () => {
vi.stubGlobal("window", { localStorage: storage() });
const repository = await import("./accountRepository");
const first = repository.createCharacter(draft).character!;
const second = repository.createCharacter({ ...draft, name: "Other" }).character!;
let cloudData: unknown = { version: 1, characters: [
{ ...first, bossCards: cards },
{ ...second, bossCards: { counts: { "wow:lady anacondra": { normal: 3 } }, lastClaims: {} } },
] };
vi.stubGlobal("fetch", vi.fn(async (_url: string, init: RequestInit) => {
if (init.method === "PUT") cloudData = JSON.parse(String(init.body)).data;
return new Response(JSON.stringify({ revision: 1, updatedAt: 1, data: cloudData }), { status: 200 });
}));
const client = await import("./onlineAccountClient");
await client.hydrateOnlineSession(session);
expect(repository.loadAccountBossCards(session.ownerId).counts["wow:lady anacondra"]?.normal).toBe(10);
await vi.advanceTimersByTimeAsync(501);
expect(cloudData).toMatchObject({ version: 2, bossCards: { counts: { "wow:lady anacondra": { normal: 10, rare: 1 } } } });
expect((cloudData as { characters: { bossCards?: unknown }[] }).characters.every((character) => character.bossCards === undefined)).toBe(true);
await client.hydrateOnlineSession(session);
expect(repository.loadAccountBossCards(session.ownerId).counts["wow:lady anacondra"]?.normal).toBe(10);
await client.logoutOnlineSession(session);
});
});