Files
healer-man/src/app/romDualClassPersistence.test.ts
T
2026-09-14 13:20:20 -04:00

104 lines
3.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
PROFILE_STORAGE_KEYS,
assignRomSecondaryClass,
createCharacter,
createOfflineSession,
listCharacters,
swapRomClasses,
updateCharacterActionBindings,
updateCharacterProgression,
type StorageLike,
} from "./accountRepository";
class MemoryStorage implements StorageLike {
private readonly values = new Map<string, string>();
getItem(key: string) { return this.values.get(key) ?? null; }
setItem(key: string, value: string) { this.values.set(key, value); }
removeItem(key: string) { this.values.delete(key); }
}
const appearance = { skinColor: 0, face: 0, hairStyle: 0, hairColor: 0, feature: 0 };
describe("RoM dual-class persistence", () => {
it("migrates v1 profiles to v2 without losing bindings or progression", () => {
const storage = new MemoryStorage();
storage.setItem(PROFILE_STORAGE_KEYS.database, JSON.stringify({
version: 1,
accounts: [],
rosters: {
"offline-roster": [{
id: "legacy",
ownerId: "offline-roster",
name: "Legacy",
raceId: "human",
classId: "priest",
gender: "female",
appearance,
level: 42,
experience: 777,
talentRanks: { legacy: 3 },
actionBindings: { "primary:face-bottom": "priest-flash-heal" },
inventory: [],
equipment: {},
location: "Dungeon Entrance",
createdAt: 1,
lastPlayedAt: 2,
}],
},
}));
expect(listCharacters("offline-roster", storage)[0]).toMatchObject({
categoryId: "wow",
secondaryClassId: null,
level: 42,
experience: 777,
talentRanks: { legacy: 3 },
actionBindings: { "primary:face-bottom": "priest-flash-heal" },
actionLoadouts: {
"priest:none": { "primary:face-bottom": "priest-flash-heal" },
},
});
expect(JSON.parse(storage.getItem(PROFILE_STORAGE_KEYS.database)!).version).toBe(7);
});
it("locks selection to level ten and preserves separate bars through atomic swaps", () => {
const storage = new MemoryStorage();
const ownerId = createOfflineSession().ownerId;
const created = createCharacter({
ownerId,
name: "Runesworn",
categoryId: "rom",
raceId: "rom-human",
classId: "rom-warrior",
gender: "male",
appearance,
}, storage).character!;
expect(assignRomSecondaryClass(ownerId, created.id, "rom-scout", storage)).toMatchObject({ ok: false });
updateCharacterProgression(ownerId, created.id, { level: 10, experience: 81, talentRanks: {} }, storage);
expect(assignRomSecondaryClass(ownerId, created.id, "rom-warden", storage)).toMatchObject({ ok: false });
expect(assignRomSecondaryClass(ownerId, created.id, "rom-scout", storage)).toMatchObject({
ok: true,
character: { classId: "rom-warrior", secondaryClassId: "rom-scout" },
});
updateCharacterActionBindings(ownerId, created.id, { "primary:face-bottom": "warrior-scout-a" }, storage);
expect(swapRomClasses(ownerId, created.id, storage)).toMatchObject({
ok: true,
character: { classId: "rom-scout", secondaryClassId: "rom-warrior", actionBindings: {} },
});
updateCharacterActionBindings(ownerId, created.id, { "primary:face-bottom": "scout-warrior-b" }, storage);
expect(swapRomClasses(ownerId, created.id, storage).character).toMatchObject({
classId: "rom-warrior",
secondaryClassId: "rom-scout",
level: 10,
experience: 81,
actionBindings: { "primary:face-bottom": "warrior-scout-a" },
});
expect(assignRomSecondaryClass(ownerId, created.id, "rom-priest", storage)).toMatchObject({ ok: false });
const saved = listCharacters(ownerId, storage)[0]!;
expect(saved.actionLoadouts).toMatchObject({
"rom-warrior:rom-scout": { "primary:face-bottom": "warrior-scout-a" },
"rom-scout:rom-warrior": { "primary:face-bottom": "scout-warrior-b" },
});
});
});