import assert from 'node:assert/strict'; import { test } from 'node:test'; import { createState } from '../public/js/engine/state.js'; import { buildExport, ImportError, parseImport } from '../public/js/services/saveio.js'; import { decode, encode } from '../public/js/services/storage.js'; import { loadContext } from './helpers.js'; const ctx = loadContext(); test('export then import restores the save', () => { const state = createState(ctx); state.funds = 42; state.generators.gen1 = 7; const imported = parseImport(buildExport(state), ctx); assert.equal(imported.funds, 42); assert.equal(imported.generators.gen1, 7); }); test('the export does not depend on the theme', () => { assert.equal('theme' in JSON.parse(buildExport(createState(ctx))), false); }); test('first-version exports (with a theme field) still import', () => { const legacy = { ...JSON.parse(buildExport(createState(ctx))), theme: 'witch' }; assert.equal(parseImport(JSON.stringify(legacy), ctx).units, 0); }); test('import also accepts text with a BOM and surrounding whitespace (.txt files)', () => { const text = ` \n${buildExport(createState(ctx))}\n\n`; assert.ok(parseImport(text, ctx)); }); const failure = (text) => { try { parseImport(text, ctx); } catch (error) { assert.ok(error instanceof ImportError); return error.code; } assert.fail('should have thrown'); }; test('import rejects bad input with a specific code', () => { assert.equal(failure(''), 'empty'); assert.equal(failure('not json'), 'invalid'); assert.equal(failure('{"hello":1}'), 'wrong_game'); const broken = JSON.parse(buildExport(createState(ctx))); broken.state = 'x'; assert.equal(failure(JSON.stringify(broken)), 'invalid'); }); test('cookie encoding is cookie-safe and lossless', () => { const text = JSON.stringify({ accents: 'éàü — ✓', long: 'x'.repeat(5000) }); const encoded = encode(text); assert.match(encoded, /^[A-Za-z0-9_-]*$/); assert.equal(decode(encoded), text); });