fix(ui-migration): 显式校验源数据顶层数组键,禁止静默兜底

添加 readArray() 辅助函数,对四处顶层数组键 (ObjectList, ImageFileList,
LayerList, GroupList) 显式校验,缺失或非数组时即刻抛 SourceError,
不再用 ?? [] 蒙混。遵循 CLAUDE.md 第二准则:数据缺失/非法一律显式暴露。

新增四个测试用例分别验证各键缺失时的错误行为。删除测试文件中未使用的
mkdirSync 导入。条目级缺省值 (entry.Event ?? {}, ObjectList ?? []) 保留,
因为单个对象/层没有事件/对象是合理的合法缺省。

真实数据护栏数字不变: 991 对象 / 55 界面 / 80 组 / 439 图 / 139 多帧图。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 00:37:37 +08:00
co-authored by Claude Opus 5
parent 9d58145bd8
commit 8901221e09
2 changed files with 60 additions and 5 deletions
@@ -1,7 +1,7 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { ROOT } from '../lib/paths.mjs';
import { loadSource, SourceError } from '../ui-migration/gameabc-source.mjs';
@@ -67,6 +67,50 @@ test('loadSource 对缺失文件显式抛错', () => {
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('loadSource 缺少顶层数组键 ObjectList 时显式抛错', () => {
const dir = fakeSource({
'gameabc_Object.json': { Property: [] }, // 缺 ObjectList
});
try {
assert.throws(() => loadSource(dir), SourceError);
assert.throws(() => loadSource(dir), /ObjectList/);
assert.throws(() => loadSource(dir), /gameabc_Object\.json/);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('loadSource 缺少顶层数组键 ImageFileList 时显式抛错', () => {
const dir = fakeSource({
'gameabc_Image.json': { id: 1 }, // 缺 ImageFileList
});
try {
assert.throws(() => loadSource(dir), SourceError);
assert.throws(() => loadSource(dir), /ImageFileList/);
assert.throws(() => loadSource(dir), /gameabc_Image\.json/);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('loadSource 缺少顶层数组键 LayerList 时显式抛错', () => {
const dir = fakeSource({
'gameabc_Layer.json': { Property: {} }, // 缺 LayerList
});
try {
assert.throws(() => loadSource(dir), SourceError);
assert.throws(() => loadSource(dir), /LayerList/);
assert.throws(() => loadSource(dir), /gameabc_Layer\.json/);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('loadSource 缺少顶层数组键 GroupList 时显式抛错', () => {
const dir = fakeSource({
'gameabc_GroupList.json': { data: [] }, // 缺 GroupList
});
try {
assert.throws(() => loadSource(dir), SourceError);
assert.throws(() => loadSource(dir), /GroupList/);
assert.throws(() => loadSource(dir), /gameabc_GroupList\.json/);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('loadSource 在真实源数据上得到已知规模(回归护栏)', () => {
const s = loadSource(REAL_SOURCE);
assert.equal(s.objects.size, 991, '对象总数');