import { describe, it, expect } from 'vitest'; import { deserialize, loadGame, saveGame, SaveStorage, serialize } from '../../src/sim/save'; import { createNewGame } from '../../src/sim/state'; import { issueCard } from '../../src/sim/loyalty'; import { STARTING_CASH_CENTS } from '../../src/sim/economy'; function memoryStorage(): SaveStorage { const data = new Map(); return { getItem: (k) => data.get(k) ?? null, setItem: (k, v) => void data.set(k, v) }; } describe('save', () => { it('round-trips the full state', () => { const s = createNewGame(7); issueCard(s.loyalty); s.prices['tetris|new'] = 2500; expect(deserialize(serialize(s))).toEqual(s); }); it('rejects unknown save versions', () => { const s = { ...createNewGame(1), saveVersion: 99 }; expect(() => deserialize(JSON.stringify(s))).toThrow('Unsupported save version: 99'); }); it('rejects malformed saves', () => { expect(() => deserialize('{"saveVersion":1}')).toThrow('Corrupted save'); }); it('rejects invalid JSON and non-object saves', () => { expect(() => deserialize('{not json')).toThrow('Corrupted save'); expect(() => deserialize('null')).toThrow('Corrupted save'); expect(() => deserialize('[]')).toThrow('Corrupted save'); expect(() => deserialize('42')).toThrow('Corrupted save'); }); it('rejects saves with a missing field', () => { const { day: _day, ...rest } = createNewGame(1); expect(() => deserialize(JSON.stringify(rest))).toThrow('Corrupted save'); }); it('rejects saves missing bankruptcy fields', () => { const { negativeMonthEnds: _n, ...noMonths } = createNewGame(1); expect(() => deserialize(JSON.stringify(noMonths))).toThrow('Corrupted save'); const { bankrupt: _b, ...noBankrupt } = createNewGame(1); expect(() => deserialize(JSON.stringify(noBankrupt))).toThrow('Corrupted save'); expect(() => deserialize(JSON.stringify({ ...createNewGame(1), bankrupt: 'no' }))).toThrow('Corrupted save'); }); it('keeps the current and monthly saves apart', () => { const storage = memoryStorage(); const s = createNewGame(1); saveGame(storage, s, { monthly: true }); s.cash = 1; saveGame(storage, s, { monthly: false }); expect(loadGame(storage, 'current')!.cash).toBe(1); expect(loadGame(storage, 'monthly')!.cash).toBe(STARTING_CASH_CENTS); }); it('returns null when nothing is saved', () => { expect(loadGame(memoryStorage(), 'current')).toBeNull(); }); });