test(adapter): E1 鲁棒性边界——便宜模型畸形 gameDefinition 优雅降级(+5 测,共 23)

生产路硬化:cheap 模型产的 gameDefinition 常残缺/畸形,适配器须不崩。
- 空 gameDefinition / 实体缺 transform / 组件引用不存在 / 畸形 rule condition / 不支持 behavior op / init 前调 render·update。
- 全优雅降级(几何兜底、条件不误触发、op 忽略、阶段防御),无缺口需修;回归保护。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-17 14:27:16 -07:00
parent 44fc82f456
commit 2d5acc5bb0

View File

@ -214,3 +214,46 @@ test('控制类paddle 对齐下落球 → 接住计分', () => {
for (let i = 0; i < 30; i++) game.update(0.1); // 推进至球落到 paddle 层
assert.ok(game._forensicsView().state().score >= 1, '对齐后应接住至少 1 次');
});
// ── 鲁棒性:便宜模型产的 gameDefinition 常残缺/畸形,生产路适配器须优雅降级不崩 ──
test('鲁棒:空 gameDefinition → 不崩、phase playing、可渲染', () => {
const game = adaptGameDefinition({})();
const { boot, g } = makeBoot();
game.init(boot);
assert.equal(game._forensicsView().state().phase, 'playing');
assert.doesNotThrow(() => { game.update(0.016); game.render(g); });
assert.ok(g._rec.fillRects.length >= 1, '至少画背景');
});
test('鲁棒:实体缺 transform / 组件引用不存在 → 默认值不崩', () => {
const src = { gameDefinition: { entities: [{ id: 'x', components: ['nope'] }], scenes: [{ id: 's', entityRefs: ['x'] }], rules: [] } };
const game = adaptGameDefinition(src)();
const { boot, g } = makeBoot();
game.init(boot);
assert.equal(typeof game._forensicsView().state().targets[0].x, 'number');
assert.doesNotThrow(() => game.render(g));
});
test('鲁棒:畸形 rule condition → 不触发不崩', () => {
const src = { gameDefinition: { entities: [], scenes: [], rules: [{ id: 'r', condition: '乱七八糟', outcome: 'win' }] }, config: {} };
const game = adaptGameDefinition(src)();
const { boot } = makeBoot();
game.init(boot);
game.update(0.016);
assert.equal(game._forensicsView().state().phase, 'playing'); // 畸形条件不误触发 win
});
test('鲁棒:不支持的 behavior op → 忽略不崩', () => {
const src = { gameDefinition: { entities: [], scenes: [], rules: [], behaviors: [{ id: 'b', trigger: 'input', op: 'unknown-op' }] }, config: {} };
const game = adaptGameDefinition(src)();
const { boot, inputBridge } = makeBoot();
game.init(boot);
assert.doesNotThrow(() => inputBridge._emit('pointerdown', { x: 100, y: 100 }));
assert.equal(game._forensicsView().state().score, 0);
});
test('鲁棒:render/update 在 init 前调 → 不崩(防御阶段错)', () => {
const game = adaptGameDefinition(SOURCE)();
const { g } = makeBoot();
assert.doesNotThrow(() => { game.update(0.016); game.render(g); });
});