Files
gplan-2/game/tests/unit/contracts/system_contracts_test.gd
T
DKChan 139cef098e 卡①T5(拆件批wr6/V13): damage_controller 拆分+参数束——open_ticket 12参→(kind,source_class,display,spec)四参 spec 束(必载 channel/base_value/target_rule/ri 缺载 assert;可选 origin_cell/attack_type/rider/highlight/context 缺省同原签名;返回单据字典九栏目+requires/source_unit 逐字不变=契约8);类变量 19→15(dig_streak/charge_layers/rider_tier_bonus/rider_tier_crit→inject 束;fx_reveal_request/fx_ac_request→fx_calls 束 {reveal,ac});函数级拆分=_hero_attack_ctx→_ctx_rewrites(按序改写族四拍+armor_roll 发射+clamp_check 唯一发射点)+_ctx_clamp_delta(克制点数+订阅改写,消费同批命中禁重复发射);_hero_attack_redeem→_kill_overflow(kill 发射+贯穿弹射);_resolve_enemy_skill→_enemy_roll(掷点/折扣/克制/池乘/下限)+_enemy_shield_apply(damage_taken 施加面 {total,shield} 穿线,发射位留主函数保减伤落快照先后序)。调用点 30 处全改 spec 形(core 14=reward_grant 3+grant_segment 7+combat_segment 3+控制器内部 overflow 1;damage_controller_test 15;system_contracts_test 1)+inject/fx_calls 注入点改键(combat_segment :34/:37/:62、begin_battle :341-347)。验收:gdstyle 基线外 0 NEW(damage_controller 9 条存量键全清:branches/length/locals×3/class-vars 19→15/params 12→4)+ unit/integration 62 套件 1698 PASS 零 SCRIPT ERROR(〔dc1〕damage_controller 44 检查全绿含 A/B 模式/死亡作废/requires 门/flush_submit/AC 合流/池乘区;〔dc2〕system_contracts 九栏目契约绿;〔dc3〕round_flow/seal_polarity/level_shop 全绿)+ system_test 70 项 0 失败三闸过〔拆件批 V13〕
2026-10-27 07:52:02 +08:00

122 lines
6.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# tests/unit/contracts/system_contracts_test.gd — 系统间接口契约断言(宁缺勿错——只断言已定稿行为)
# 运行:godot --headless --path game --script res://tests/unit/contracts/system_contracts_test.gd
# 契约线清单来源:design/engineering/architecture/architecture.md「API Boundaries」表 + 各系统 AC 前言(规范源)。
# 覆盖面:
# 各系统→D2 ConfigDB 类型化单点(TABLE_MANIFEST 词表封闭——伤害单据 kind/目标规则/结账政策常量);
# D2↔B1 掷点用途标签 ∈ RngService 封闭词表(EC12——settlement.armor_roll_stream 成员性);
# B1→D1 行协议公共字段形态(open_ticket 九栏缺省 + 协议公共键);
# B4→B3 增益授予布尔受理口径(域闭 EC1 / 未知 id EC2 / 受理后投影)。
extends SceneTree
const ConfigDBScript := preload("res://core/config_db.gd")
const RngServiceScript := preload("res://core/rng_service.gd")
const StatusContainerScript := preload("res://core/status_container.gd")
const UnitContainerScript := preload("res://core/unit_container.gd")
const DamageControllerScript := preload("res://core/damage_controller.gd")
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点
func _init() -> void:
var db = ConfigDBScript.new()
var load_ok: bool = db.load_tables("res://data/tables", true)
Kit.check("前置:表加载零 error", load_ok and db.diagnostics_count("error") == 0)
if load_ok:
_test_damage_controller_vocabularies()
_test_rng_tag_closure(db)
_test_ticket_nine_columns()
_test_status_boolean_acceptance(db)
quit(Kit.finish("system_contracts_test"))
# ---- B1 兑付配方词表:五配方/三目标规则/双结账政策——常量非空且互异(封闭枚举负空间) ----
func _test_damage_controller_vocabularies() -> void:
var kinds: Array = [
DamageControllerScript.KIND_HERO_ATTACK, DamageControllerScript.KIND_ENEMY_SKILL,
DamageControllerScript.KIND_BOARD_TYPED, DamageControllerScript.KIND_BOARD_BLAST,
DamageControllerScript.KIND_BOARD_FLAT_HERO,
]
var nonempty := true
for k in kinds:
if String(k).is_empty():
nonempty = false
Kit.check("五配方常量非空(开单面唯一分派键)", nonempty)
var unique_kinds: Dictionary = {}
for k in kinds:
unique_kinds[String(k)] = true
Kit.check("五配方常量互异(词表封闭无别名)", unique_kinds.size() == kinds.size())
var targets: Array = [
DamageControllerScript.TARGET_FRONT_ALIVE, DamageControllerScript.TARGET_HERO,
DamageControllerScript.TARGET_ALL_ALIVE,
]
var unique_targets: Dictionary = {}
for t in targets:
unique_targets[String(t)] = true
Kit.check("三目标规则常量互异(结账时刻现场解析词表)", unique_targets.size() == targets.size())
Kit.check("双结账政策常量互异(A 即结 / B 攒批回切位)",
String(DamageControllerScript.POLICY_IMMEDIATE) != String(DamageControllerScript.POLICY_SEGMENT))
# ---- D2↔B1:掷点用途标签 ∈ RngService 封闭词表(EC12——跨系统字面量契约的成员性断言) ----
func _test_rng_tag_closure(db) -> void:
var stream_label: String = String(db.settlement.damage_roll.armor_roll_stream)
Kit.check("settlement.armor_roll_stream ∈ RngService 封闭标签词表(EC12)",
RngServiceScript.PURPOSE_TAGS.has(stream_label))
Kit.check("RngService 用途标签词表非空且互异(封闭语义由流改标签)",
RngServiceScript.PURPOSE_TAGS.size() > 0)
# ---- B1→D1 行协议:单据九栏缺省齐备 + 协议公共键形态 ----
func _test_ticket_nine_columns() -> void:
var ctrl = _bare_controller()
var t: Dictionary = ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit", "契约探针",
{"channel": "棋盘兑付", "base_value": 1, "target_rule": DamageControllerScript.TARGET_HERO, "ri": 0})
var columns := ["kind", "source_class", "origin_cell", "round_index", "display",
"channel", "attack_type", "base_value", "target_rule", "rider", "highlight", "context",
"requires", "source_unit"]
var all_present := true
for c in columns:
if not t.has(c):
all_present = false
Kit.check("单据九栏 + 可选两栏缺省齐备(开单面只写差异栏前提)", all_present)
Kit.check("缺省无类型轴(attack_type 空 = 恒不查克制矩阵)", String(t.attack_type) == "")
Kit.check("缺省无前置依赖(requires = -1)", int(t.requires) == -1)
func _bare_controller():
var db = ConfigDBScript.new()
# 只取 open_ticket 纯构造面——四个依赖一律传真实实例,不用空桩:
# DamageController 的 db/rng/units/effects 全是定型标注,传无类型的空桩会触发
# 「Trying to assign value of type '' to a variable of type '…'」脚本错误——该错误不改退出码,
# 却让赋值静默失败(成员留 null),脚本错误扫描落地后必红。三者均可零消费构造。
var units = UnitContainerScript.new(db)
var effects = StatusContainerScript.new(db)
return DamageControllerScript.new(db, RngServiceScript.new(), units, effects)
# ---- B4→B3:增益授予布尔受理口径(受理/拒绝二元面 + 投影一致性) ----
func _test_status_boolean_acceptance(db) -> void:
var effects = StatusContainerScript.new(db)
# EC1 域闭:notify_battle_start 前授予 = false(受理面封闭)
var any_row: Dictionary = {}
if db.status_effects.rows.size() > 0:
any_row = db.status_effects.rows[0]
Kit.check("前置:状态表非空", not any_row.is_empty())
if any_row.is_empty():
return
var probe_id: String = String(any_row.effect_id)
var src := {"source_class": "safe_cell", "origin_cell": Vector2i(-1, -1), "round_index": 0}
Kit.check("域闭授予拒绝(EC1——战斗域外 apply_effect = false)", effects.apply_effect(probe_id, src) == false)
Kit.check("未知 effect_id 拒绝(EC2)", effects.apply_effect("no_such_effect_id__", src) == false)
effects.notify_battle_start()
Kit.check("开域后受理已知 id(布尔受理口径 true 通道)", effects.apply_effect(probe_id, src) == true)
var active: Array = effects.query_active(String(any_row.target_side))
var projected := false
for a in active:
if String(a.effect_id) == probe_id:
projected = int(a.effective_value) == int(a.layers) * int(a.snap_magnitude)
Kit.check("受理后投影 effective_value = 层数×授予时快照(抗热重载 EC9 面)", projected)