import { describe, it, expect } from 'vitest'; import { wrap, unwrap, SaveCorruptError, SaveEnvelopeSchema, type SaveEnvelope, } from './envelope'; // Tests for the SaveEnvelope wrap/unwrap pair. The envelope is the load-bearing // shape from CLAUDE.md: `{schemaVersion, payload, checksum}`. Tampering or // lossy-storage corruption is detected via CRC-32 mismatch on unwrap. describe('wrap', () => { it('returns an envelope with schemaVersion, payload, and 8-char hex checksum', () => { const env = wrap({ foo: 'bar' }, 1); expect(env.schemaVersion).toBe(1); expect(env.payload).toEqual({ foo: 'bar' }); expect(env.checksum).toMatch(/^[0-9a-f]{8}$/); }); }); describe('unwrap', () => { it('round-trips several payload shapes', () => { const shapes: unknown[] = [ { foo: 'bar' }, { nested: { a: 1, b: { c: [1, 2, 3] } } }, { garden: { tiles: [{ id: 'tile-1' }] }, plants: [] }, [1, 2, 3], { empty: {} }, ]; for (const p of shapes) { expect(unwrap(wrap(p, 1))).toEqual(p); } }); it('throws SaveCorruptError when checksum is tampered', () => { const env = wrap({ x: 1 }, 1); const tampered: SaveEnvelope = { ...env, checksum: 'deadbeef' }; let caught: unknown = null; try { unwrap(tampered); } catch (e) { caught = e; } expect(caught).toBeInstanceOf(SaveCorruptError); const err = caught as SaveCorruptError; expect(err.expected).toBe('deadbeef'); expect(err.actual).toBe(env.checksum); }); it('throws SaveCorruptError when payload is tampered (checksum mismatch)', () => { const env = wrap({ x: 1 }, 1); const tampered: SaveEnvelope = { ...env, payload: { x: 2 } }; expect(() => unwrap(tampered)).toThrow(SaveCorruptError); }); }); describe('SaveEnvelopeSchema', () => { it('accepts a valid envelope', () => { const env = wrap({ foo: 'bar' }, 1); expect(SaveEnvelopeSchema.safeParse(env).success).toBe(true); }); it('accepts schemaVersion 0 (synthetic v0 per CONTEXT D-05)', () => { const env = { schemaVersion: 0, payload: {}, checksum: '00000000' }; expect(SaveEnvelopeSchema.safeParse(env).success).toBe(true); }); it('rejects malformed envelopes (missing keys)', () => { const noChecksum = { schemaVersion: 1, payload: {} }; const noVersion = { payload: {}, checksum: '00000000' }; expect(SaveEnvelopeSchema.safeParse(noChecksum).success).toBe(false); expect(SaveEnvelopeSchema.safeParse(noVersion).success).toBe(false); }); it('rejects malformed envelopes (non-hex checksum)', () => { const bad = { schemaVersion: 1, payload: {}, checksum: 'NOT-HEX!' }; expect(SaveEnvelopeSchema.safeParse(bad).success).toBe(false); }); it('rejects negative schemaVersion', () => { const bad = { schemaVersion: -1, payload: {}, checksum: '00000000' }; expect(SaveEnvelopeSchema.safeParse(bad).success).toBe(false); }); });