From 2d5acc5bb0aeae47a80a6552ea0f30b6b2785901 Mon Sep 17 00:00:00 2001 From: lili Date: Wed, 17 Jun 2026 14:27:16 -0700 Subject: [PATCH] =?UTF-8?q?test(adapter):=20E1=20=E9=B2=81=E6=A3=92?= =?UTF-8?q?=E6=80=A7=E8=BE=B9=E7=95=8C=E2=80=94=E2=80=94=E4=BE=BF=E5=AE=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=95=B8=E5=BD=A2=20gameDefinition=20?= =?UTF-8?q?=E4=BC=98=E9=9B=85=E9=99=8D=E7=BA=A7(+5=20=E6=B5=8B,=E5=85=B1?= =?UTF-8?q?=2023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 生产路硬化:cheap 模型产的 gameDefinition 常残缺/畸形,适配器须不崩。 - 空 gameDefinition / 实体缺 transform / 组件引用不存在 / 畸形 rule condition / 不支持 behavior op / init 前调 render·update。 - 全优雅降级(几何兜底、条件不误触发、op 忽略、阶段防御),无缺口需修;回归保护。 Co-Authored-By: Claude Opus 4.8 --- game-runtime/src/adapter/adapter.test.mjs | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/game-runtime/src/adapter/adapter.test.mjs b/game-runtime/src/adapter/adapter.test.mjs index 8e5b356d..7d65421d 100644 --- a/game-runtime/src/adapter/adapter.test.mjs +++ b/game-runtime/src/adapter/adapter.test.mjs @@ -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); }); +});