26 IO tests green: serializer, parser (speed/enumRef/raw), TsConfigFile round-trip (load/patch/add/delete/save preserving symbolic expressions).
102 lines
4.1 KiB
TypeScript
102 lines
4.1 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync, copyFileSync, mkdtempSync, readdirSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
import { TsConfigFile } from '../src/io/TsConfigFile';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const fixture = join(here, 'fixtures', 'heroSet.sample.ts');
|
|
|
|
test('load + getKeys', () => {
|
|
const f = new TsConfigFile(fixture, 'HeroInfo');
|
|
f.load();
|
|
assert.deepEqual(f.getKeys(), ['5011', '6001']);
|
|
});
|
|
test('read returns structured value with speed/enumRef preserved', () => {
|
|
const f = new TsConfigFile(fixture, 'HeroInfo');
|
|
f.load();
|
|
const v = f.read('5011')!;
|
|
assert.equal(v.kind, 'obj');
|
|
assert.deepEqual(v.props['fac'], { kind: 'enumRef', qualifier: 'FacSet', member: 'HERO' });
|
|
const skill = v.props['skills'].props['6001'];
|
|
assert.deepEqual(skill.props['cd'], { kind: 'speed', level: 'Slow3' });
|
|
});
|
|
test('getText after load equals original file (no mutation on load)', () => {
|
|
const f = new TsConfigFile(fixture, 'HeroInfo');
|
|
f.load();
|
|
assert.equal(f.getText(), readFileSync(fixture, 'utf8'));
|
|
});
|
|
test('read missing key returns null', () => {
|
|
const f = new TsConfigFile(fixture, 'HeroInfo');
|
|
f.load();
|
|
assert.equal(f.read('9999'), null);
|
|
});
|
|
|
|
function withTempFixture(): { dir: string; file: string } {
|
|
const dir = mkdtempSync(join(tmpdir(), 'phcfg-'));
|
|
const file = join(dir, 'heroSet.sample.ts');
|
|
copyFileSync(fixture, file);
|
|
return { dir, file };
|
|
}
|
|
|
|
test('patch updates one entry; reload reads new value; other entries intact', () => {
|
|
const { file } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
const v = f.read('5011')!;
|
|
v.props['ap'] = { kind: 'num', value: 99 };
|
|
f.patch('5011', v);
|
|
assert.equal(f.isDirty(), true);
|
|
assert.equal(f.save().ok, true);
|
|
const f2 = new TsConfigFile(file, 'HeroInfo'); f2.load();
|
|
assert.deepEqual(f2.read('5011')!.props['ap'], { kind: 'num', value: 99 });
|
|
assert.equal(f2.read('6001')!.props['name'].value, '兽人战士'); // 另一条未破坏
|
|
});
|
|
|
|
test('patch preserves speed + enumRef on reload', () => {
|
|
const { file } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
const v = f.read('5011')!;
|
|
v.props['hp'] = { kind: 'num', value: 500 };
|
|
f.patch('5011', v); f.save();
|
|
const f2 = new TsConfigFile(file, 'HeroInfo'); f2.load();
|
|
const again = f2.read('5011')!;
|
|
assert.deepEqual(again.props['fac'], { kind: 'enumRef', qualifier: 'FacSet', member: 'HERO' });
|
|
assert.deepEqual(again.props['skills'].props['6001'].props['cd'], { kind: 'speed', level: 'Slow3' });
|
|
});
|
|
|
|
test('add appends entry readable after save+reload', () => {
|
|
const { file } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
f.add('5099', { kind: 'obj', props: { uuid: { kind: 'num', value: 5099 }, name: { kind: 'str', value: '新英雄' } } });
|
|
f.save();
|
|
const f2 = new TsConfigFile(file, 'HeroInfo'); f2.load();
|
|
assert.ok(f2.getKeys().includes('5099'));
|
|
assert.equal(f2.read('5099')!.props['name'].value, '新英雄');
|
|
});
|
|
|
|
test('delete removes entry', () => {
|
|
const { file } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
f.delete('6001'); f.save();
|
|
const f2 = new TsConfigFile(file, 'HeroInfo'); f2.load();
|
|
assert.ok(!f2.getKeys().includes('6001'));
|
|
assert.ok(f2.getKeys().includes('5011'));
|
|
});
|
|
|
|
test('save writes a .bak backup of the pre-save file', () => {
|
|
const { file } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
const v = f.read('5011')!; v.props['hp'] = { kind: 'num', value: 1 }; f.patch('5011', v);
|
|
f.save();
|
|
assert.equal(readFileSync(file + '.bak', 'utf8'), readFileSync(fixture, 'utf8'));
|
|
});
|
|
|
|
test('save with no edits is a no-op (ok, no .bak written)', () => {
|
|
const { file, dir } = withTempFixture();
|
|
const f = new TsConfigFile(file, 'HeroInfo'); f.load();
|
|
assert.equal(f.save().ok, true);
|
|
assert.equal(readdirSync(dir).some(p => p.endsWith('.bak')), false);
|
|
});
|