From dd5ed4a753dac4f72d784d7d5207e3d9d894790b Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 29 Sep 2026 07:52:04 +0800 Subject: [PATCH] =?UTF-8?q?test(t7):=20AuxItems=20=E7=B1=BB=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=E2=80=94=E2=80=94=E7=BB=93=E6=9E=84=E5=86=BB=E7=BB=93?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E9=9D=A2=E5=8D=81=E4=BA=8C=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E5=85=A8=E8=BF=87=20=E2=91=A0=E6=96=B0=E5=A5=97=E4=BB=B6=20tes?= =?UTF-8?q?ts/unit/core/aux=5Fitems=5Ftest.gd=EF=BC=88run=5Fall=20manifest?= =?UTF-8?q?+1,expect=200=EF=BC=89:=E6=8E=88=E4=BA=88=E5=BD=92=E5=9B=A0/?= =?UTF-8?q?=E5=8F=A5=E6=9F=84=E5=8F=97=E7=90=86=E5=BA=8F/Used=20=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E9=80=86=E4=BA=8C=E6=AC=A1=20consume=20=E6=8B=92/?= =?UTF-8?q?=E7=9C=9F=E8=A7=86=E6=81=92=E5=8F=AF=E7=94=A8/=E9=A2=84?= =?UTF-8?q?=E8=AF=BB=E7=82=B9=E5=87=BB=E7=BA=A7=E9=9B=B6=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=E4=B8=8D=E6=B6=88=E8=80=97/3x3=20=E8=BE=B9?= =?UTF-8?q?=E7=BC=98=E7=AA=97=E5=8F=A3=E2=88=A9=E6=9D=BF(=E8=A7=92=204=20?= =?UTF-8?q?=E6=A0=BC)/=E8=B6=8A=E6=9D=BF=E6=8B=92/=E5=8F=A0=E5=8A=A0?= =?UTF-8?q?=E5=B1=82=E4=B8=A4=E9=9B=86/=E6=8D=A2=E5=9B=BE=E4=B8=89?= =?UTF-8?q?=E6=B8=85(retire=20=E4=B8=8D=E5=8A=A8=E5=BA=93=E5=AD=98)/?= =?UTF-8?q?=E5=85=B3=E5=8D=A1=E6=9C=AB=20reset/=E9=9B=B6=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E9=87=8D=E6=94=BE=E4=B8=A4=E9=81=8D=E6=8A=95=E5=BD=B1=E9=80=90?= =?UTF-8?q?=E4=BD=8D=20=E2=91=A1=E6=A1=A9=E8=A1=A8=E6=A8=A1=E5=BC=8F:StubD?= =?UTF-8?q?B/StubTable=20=E5=86=85=E8=81=94=E7=B1=BB=E5=96=82=E8=A1=8C?= =?UTF-8?q?=E5=AE=9A=E4=B9=89(=E4=BA=A7=E5=93=81=E7=A0=81=E9=9B=B6?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=88=86=E6=94=AF=E2=80=94=E2=80=94C1=20?= =?UTF-8?q?=E9=9B=B6=E7=89=B9=E5=88=A4=E5=AE=88=E4=BD=8F);=E4=BF=AE?= =?UTF-8?q?=E4=B8=89=E5=85=83=E8=AF=AD=E5=8F=A5=E5=8F=96=20void=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=E8=A7=A3=E6=9E=90=E9=94=99=20?= =?UTF-8?q?=E9=97=B8:aux=5Fitems=5Ftest=20PASS;run=5Fall=2012=20=E5=A5=97?= =?UTF-8?q?=E5=85=A8=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/tests/run_all.gd | 1 + game/tests/unit/core/aux_items_test.gd | 118 +++++++++++++++++++++ game/tests/unit/core/aux_items_test.gd.uid | 1 + 3 files changed, 120 insertions(+) create mode 100644 game/tests/unit/core/aux_items_test.gd create mode 100644 game/tests/unit/core/aux_items_test.gd.uid diff --git a/game/tests/run_all.gd b/game/tests/run_all.gd index 5afadfaa..45b1d68c 100644 --- a/game/tests/run_all.gd +++ b/game/tests/run_all.gd @@ -32,6 +32,7 @@ func _suites() -> Array: {"path": "res://tests/unit/data_config/aux_target_violation_test.gd", "expect": 1}, # 迭代 7 T1——真视窄域护栏 {"path": "res://tests/unit/data_config/reward_dangling_aux_test.gd", "expect": 1}, # 迭代 7 T1——aux 双表校验 {"path": "res://tests/unit/data_config/rng_service_test.gd", "expect": 0}, + {"path": "res://tests/unit/core/aux_items_test.gd", "expect": 0}, # 迭代 7 T7——魔法道具结构冻结断言面 {"path": "res://tests/unit/battle_settlement/damage_math_test.gd", "expect": 0}, {"path": "res://tests/unit/battle_settlement/damage_controller_test.gd", "expect": 0}, {"path": "res://tests/unit/contracts/system_contracts_test.gd", "expect": 0}, diff --git a/game/tests/unit/core/aux_items_test.gd b/game/tests/unit/core/aux_items_test.gd new file mode 100644 index 00000000..2dff7fea --- /dev/null +++ b/game/tests/unit/core/aux_items_test.gd @@ -0,0 +1,118 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 魔法道具 AuxItems 类单测(迭代 7 T7——结构冻结断言面;设计权威 魔法道具.md + registry aux_items)。 +# 覆盖:授予归因 / Used 不可逆 / 真视恒可用 / 预读点击级零信息拒绝 / 3×3 边缘窗口∩板 / +# 叠加层三清(retire_overlays 不动库存)/ 关卡末 reset / 零随机(同操作序列重放两遍投影逐位)。 +# 运行:godot --headless --path game --script res://tests/unit/core/aux_items_test.gd(expect 0) +extends SceneTree + +const AuxItemsScript := preload("res://core/aux_items.gd") + +var _problems: Array = [] + + +func check(cond: bool, msg: String) -> void: + if not cond: + _problems.append(msg) + + +class StubTable: + var rows := {} + func row_by_id(id: String) -> Dictionary: + return rows.get(id, {}) + func active_rows() -> Array: + return [] + + +class StubDB: + var aux_items: StubTable + + +func _stub_db() -> StubDB: + var t := StubTable.new() + t.rows = { + "sapper_order": {"item_id": "sapper_order", "display_name": "工兵令", "dimension_tags": ["action"], + "effect_kinds": ["ap_grant"], "target": "none", "magnitude": 2, "use_phase": "active_dig"}, + "nest_pendulum": {"item_id": "nest_pendulum", "display_name": "探巢摆钟", "dimension_tags": ["risk"], + "effect_kinds": ["mine_reveal"], "target": "area_3x3", "magnitude": 0, "use_phase": "active_dig"}, + "loot_compass": {"item_id": "loot_compass", "display_name": "探宝罗盘", "dimension_tags": ["plan"], + "effect_kinds": ["reward_preview"], "target": "area_3x3", "magnitude": 0, "use_phase": "active_dig"}, + } + var db := StubDB.new() + db.aux_items = t + return db + + +func _new_inv() -> Object: + return AuxItemsScript.new(_stub_db()) + + +func _unrevealed(_c: Vector2i) -> bool: + return true # 测试板全未开(预读恒有信息——点击级零信息另用假回调测) + + +func _all_revealed(_c: Vector2i) -> bool: + return false # 预读点击级零信息(窗口全已开) + + +func _init() -> void: + # ① 授予归因+受理序+Used 不可逆 + var inv = _new_inv() + var r1: Dictionary = inv.add_item("sapper_order", "safe_cell", Vector2i(3, 4), 2) + var r2: Dictionary = inv.add_item("nest_pendulum", "flag_hit", Vector2i(5, 6), 3) + check(int(r1.handle) == 1 and int(r2.handle) == 2, "句柄受理序单调(1→2——零随机)") + check(String(r1.source_class) == "safe_cell" and r1.origin_cell == Vector2i(3, 4) and int(r1.round_index) == 2, + "授予来源可归因(source/cell/round)") + var inv_proj: Array = inv.query_inventory(Callable()) + check(inv_proj.size() == 2 and not bool(inv_proj[0].used), "库存投影两件未用") + check(bool(inv.consume(int(r1.handle)).get("used", false)), "consume 返回已用快照回执") + check(inv.consume(int(r1.handle)).is_empty(), "Used 不可逆——二次 consume 拒绝(空回执)") + check(int(inv.inventory_count()) == 2, "Used 保留只读(库存计数不变)") + + # ② 真视恒可用+边缘窗口∩板(3×3 中心贴角——角 4 格) + var v_ok: Dictionary = inv.validate_use(int(r2.handle), Vector2i(0, 0), Vector2i(10, 10), Callable()) + check(bool(v_ok.ok) and v_ok.resolved.size() == 4, + "真视边缘窗口=窗口∩板(角 4 格——护栏只缩不扩)") + # 预读点击级零信息:中心板内但回调恒 false(全已开)→拒绝零消耗 + var inv2 = _new_inv() + var rp: Dictionary = inv2.add_item("loot_compass", "safe_cell", Vector2i(1, 1), 1) + var v_zero: Dictionary = inv2.validate_use(int(rp.handle), Vector2i(5, 5), Vector2i(10, 10), Callable()) + check(not bool(v_zero.ok) and int(inv2.inventory_count()) == 1, + "预读点击级零信息拒绝(不消耗——库存不变)") + # 目标越板 + var v_oob: Dictionary = inv2.validate_use(int(rp.handle), Vector2i(12, 5), Vector2i(10, 10), Callable()) + check(not bool(v_zero.ok) and not bool(v_oob.ok), "越板目标拒绝") + + # ③ 叠加层+三清(retire 不动库存;reset 全清) + inv2.apply_overlay("mine_reveal", v_ok.resolved) + inv2.apply_overlay("reward_preview", [Vector2i(5, 5)]) + var ov: Dictionary = inv2.query_overlays() + check(ov.mine_reveal.size() == 4 and ov.reward_preview.size() == 1, "叠加层两集落位(真视 4/预读 1)") + inv2.retire_overlays() + check(inv2.query_overlays().mine_reveal.is_empty() and int(inv2.inventory_count()) == 1, + "换图三清:叠加层消亡+库存不动(关卡域)") + inv2.reset() + check(inv2.inventory_count() == 0 and inv2.probe().used == 0, "关卡末 reset 全清(幂等)") + + # ④ 零随机重放:同操作序列两遍,投影逐位相同 + var replay_a: Array = [] + var replay_b: Array = [] + for rep in 2: + var inv3 = _new_inv() + var h1: Dictionary = inv3.add_item("sapper_order", "safe_cell", Vector2i(2, 3), 1) + inv3.validate_use(int(h1.handle), Vector2i(4, 4), Vector2i(10, 10), Callable()) + inv3.apply_overlay("mine_reveal", [Vector2i(4, 4)]) + inv3.consume(int(h1.handle)) + if rep == 0: + replay_a = [inv3.query_inventory(Callable()), inv3.query_overlays(), inv3.probe()] + else: + replay_b = [inv3.query_inventory(Callable()), inv3.query_overlays(), inv3.probe()] + check(str(replay_a) == str(replay_b), "零随机重放:库存+叠加层+探针投影逐位相同") + + # ---- 汇总 ---- + if _problems.is_empty(): + print("[aux_items_test] PASS — 授予归因/Used 不可逆/真视恒可用/零信息拒绝/边缘窗口/三清/零随机重放 全过") + quit(0) + else: + for p in _problems: + push_error("[aux_items_test] FAIL — " + p) + quit(1) diff --git a/game/tests/unit/core/aux_items_test.gd.uid b/game/tests/unit/core/aux_items_test.gd.uid new file mode 100644 index 00000000..88eb53c2 --- /dev/null +++ b/game/tests/unit/core/aux_items_test.gd.uid @@ -0,0 +1 @@ +uid://dqx67xrrbvp82