import { test } from 'node:test'; import assert from 'node:assert/strict'; import { validate } from '../src/shared/validation'; import { RecordValue } from '../src/io/recordValue'; const ctx = { hasSkill: (u: number) => [6001, 6301, 6501].includes(u), hasField: (u: number) => [7015].includes(u), hasHero: (u: number) => [5011, 6001].includes(u), heroListKeys: new Set(['5011']), }; function heroObj(extra: Record = {}): RecordValue { return { kind: 'obj', props: { uuid: { kind: 'num', value: 5011 }, name: { kind: 'str', value: 'x' }, path: { kind: 'str', value: 'p' }, fac: { kind: 'enumRef', qualifier: 'FacSet', member: 'HERO' }, lv: { kind: 'num', value: 1 }, type: { kind: 'enumRef', qualifier: 'HType', member: 'Melee' }, hp: { kind: 'num', value: 400 }, ap: { kind: 'num', value: 20 }, skills: { kind: 'obj', props: { 6001: { kind: 'obj', props: { uuid: { kind: 'num', value: 6001 }, lv: { kind: 'num', value: 1 }, cd: { kind: 'speed', level: 'Slow3' }, ccd: { kind: 'num', value: 0 } } } } }, info: { kind: 'str', value: 'ok' }, ...extra, } }; } test('valid hero → no errors', () => { const issues = validate('hero', new Map([['5011', heroObj()]]), ctx); assert.equal(issues.filter(i => i.severity === 'error').length, 0); }); test('duplicate uuid → error', () => { // 两条记录携带相同的 uuid 字段值(Map 键不同,uuid 字段相同) const dup = heroObj(); // uuid=5011 const issues = validate('hero', new Map([['5011', heroObj()], ['5012', dup]]), ctx); assert.ok(issues.some(i => i.severity === 'error' && i.code === 'dup-uuid')); }); test('missing required field → error', () => { const h = heroObj(); delete h.props['name']; const issues = validate('hero', new Map([['5011', h]]), ctx); assert.ok(issues.some(i => i.code === 'missing-required' && i.fieldPath === 'name')); }); test('trigger slot references missing skill → error', () => { const h = heroObj({ atked: { kind: 'arr', items: [{ kind: 'obj', props: { s_uuid: { kind: 'num', value: 9999 }, t_num: { kind: 'num', value: 3 } } }] } }); const issues = validate('hero', new Map([['5011', h]]), ctx); assert.ok(issues.some(i => i.code === 'dangling-ref')); }); test('field list references missing field skill → error', () => { const h = heroObj({ field: { kind: 'arr', items: [{ kind: 'num', value: 8888 }] } }); const issues = validate('hero', new Map([['5011', h]]), ctx); assert.ok(issues.some(i => i.code === 'dangling-ref')); }); test('HeroList/hero consistency: fac=HERO entry missing from HeroList → error', () => { const ctx2 = { ...ctx, heroListKeys: new Set([]) }; const issues = validate('hero', new Map([['5011', heroObj()]]), ctx2); assert.ok(issues.some(i => i.code === 'herolist-inconsistent')); }); test('invalid enum value → error', () => { const h = heroObj({ fac: { kind: 'enumRef', qualifier: 'FacSet', member: 'NOPE' } }); const issues = validate('hero', new Map([['5011', h]]), ctx); assert.ok(issues.some(i => i.code === 'bad-enum')); }); test('unknown overrides key → error', () => { const h = heroObj({ atked: { kind: 'arr', items: [{ kind: 'obj', props: { s_uuid: { kind: 'num', value: 6301 }, t_num: { kind: 'num', value: 3 }, overrides: { kind: 'obj', props: { bogus: { kind: 'num', value: 1 } } } } }] } }); const issues = validate('hero', new Map([['5011', h]]), ctx); assert.ok(issues.some(i => i.code === 'bad-override-key')); });