170 lines
9.5 KiB
GDScript
170 lines
9.5 KiB
GDScript
# tests/unit/schema_vocab_violation_test.gd — schema 词表违约词的拒绝路径集中断言面(迭代 55「QA覆盖余量收口批」T1/V1)
|
||
# 覆盖:十七违约词族七检查——①empty_table 八表空行集注入(board_gen/enemy/level/meta/reward/shop/skill/status_effect)
|
||
# ②settlement missing_segment/pperfect_oob/segorder_violation 三注入 ③feat+treasure zero_flat 白名单违约注入
|
||
# ④meta attune_refresh_upgrade keystone_cost 越域注入 ⑤treasure use_phase 出域注入(USE_PHASES=active_dig 枚值面)
|
||
# ⑥board_gen solver_rules="single" frozen 拒(同钉 single_subset 冻结语义) ⑦meta_tree 彩蛋 4 行(>3)load_bearing 拒
|
||
# 注入形态沿在役先例(meta_progression_forge_test.gd:581 同款):load(schema).new() 新实例+真行深拷贝注入——
|
||
# 禁改共享 Resource(共享 Resource 池污染 Gotcha)。违约词经 validate() 诊断面直调取 errs 断言(非致命通道,
|
||
# 不进 manifest negative 名单——本面非运行期致命路径,不触 test-conventions §5.6)。
|
||
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload,ConfigDB 显式实例化构造注入。
|
||
# 运行:godot --headless --path game --script res://tests/unit/schema_vocab_violation_test.gd(expect 0)
|
||
extends SceneTree
|
||
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||
|
||
var _db
|
||
|
||
|
||
func _init() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (违约词拒绝路径——validate 诊断面直调断言宿主)
|
||
_db = ConfigDBScript.new()
|
||
# 表加载失败不中止(collect_only=true——诊断在此展开,红由具体断言落,沿 guard 套件同款)
|
||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||
if not load_ok:
|
||
push_error("[schema_vocab_violation_test] 表加载返回失败——诊断如下(红由具体断言落)")
|
||
for d in _db.diagnostics():
|
||
if d.severity == "error" or d.severity == "fatal":
|
||
push_error(" [%s] %s: %s" % [d.severity, d.category, d.message])
|
||
_check_empty_tables()
|
||
_check_settlement_vocab()
|
||
_check_zero_flat_guard()
|
||
_check_keystone_cost_domain()
|
||
_check_use_phase_domain()
|
||
_check_solver_frozen()
|
||
_check_meta_tree_load_bearing()
|
||
quit(Kit.finish("schema_vocab_violation_test"))
|
||
|
||
|
||
# errs 数组按 category(可选 message 子串)命中判定
|
||
func _has(errs: Array, cat: String, needle: String = "") -> bool:
|
||
for e in errs:
|
||
if String(e["category"]) != cat:
|
||
continue
|
||
if needle == "" or String(e["message"]).contains(needle):
|
||
return true
|
||
return false
|
||
|
||
|
||
# ---- ① empty_table 八表空行集注入(fresh 新实例 rows/行族全空)----
|
||
|
||
func _check_empty_tables() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (空表违约词拒绝路径——validate 诊断面直调取 errs)
|
||
var schemas := {
|
||
"board_gen": "res://data/schemas/board_gen_table.gd",
|
||
"enemy": "res://data/schemas/enemy_table.gd",
|
||
"level": "res://data/schemas/level_table.gd",
|
||
"meta": "res://data/schemas/meta_table.gd",
|
||
"reward": "res://data/schemas/reward_table.gd",
|
||
"shop": "res://data/schemas/shop_table.gd",
|
||
"skill": "res://data/schemas/skill_table.gd",
|
||
"status_effect": "res://data/schemas/status_effect_table.gd",
|
||
}
|
||
for table_name in schemas.keys():
|
||
var t = load(String(schemas[table_name])).new()
|
||
Kit.check("G1-① 空表注入被拒:%s validate() errs 含 empty_table〔QA余 V1〕" % table_name,
|
||
_has(t.validate(), "empty_table"))
|
||
|
||
|
||
# ---- ② settlement 三注入(missing_segment/pperfect_oob/segorder_violation)----
|
||
|
||
func _check_settlement_vocab() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (settlement 三违约词拒绝路径——validate 诊断面直调取 errs)
|
||
# missing_segment:fresh 新实例 core/damage_roll 段缺失
|
||
var seg_missing = load("res://data/schemas/settlement_table.gd").new()
|
||
Kit.check("G1-② settlement 段缺失注入被拒:errs 含 missing_segment〔QA余 V1〕",
|
||
_has(seg_missing.validate(), "missing_segment"))
|
||
# pperfect_oob:真表深拷贝改 p_perfect=150(载体域 {100,200} 外——禁改共享 Resource,deep duplicate 隔离)
|
||
var st = (_db.combat.settlement as Resource).duplicate(true)
|
||
st.core.p_perfect = 150
|
||
Kit.check("G1-② p_perfect=150(域 {100,200} 外)注入被拒:errs 含 pperfect_oob〔QA余 V1〕",
|
||
_has(st.validate(), "pperfect_oob"))
|
||
# segorder_violation:真表深拷贝改段序(FROZEN_SEGMENT_ORDER 恒等面外)
|
||
var st2 = (_db.combat.settlement as Resource).duplicate(true)
|
||
st2.segment_order = PackedStringArray(["verdict", "buff_debuff", "combat", "decay", "grant"])
|
||
Kit.check("G1-② segment_order 违例注入被拒:errs 含 segorder_violation〔QA余 V1〕",
|
||
_has(st2.validate(), "segorder_violation"))
|
||
|
||
|
||
# ---- ③ zero_flat 白名单违约注入(feat+treasure 两表)----
|
||
|
||
func _check_zero_flat_guard() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (零裸 flat 白名单违约词拒绝路径——validate 诊断面直调取 errs)
|
||
# feat:真行深拷贝+白名单外键(ROW_KEYS 外)
|
||
var feat_row: Dictionary = (_db.crossrun.feats.rows[0] as Dictionary).duplicate(true)
|
||
feat_row["ghost_flat"] = 1
|
||
var ft = load("res://data/schemas/feat_table.gd").new()
|
||
ft.rows = [feat_row]
|
||
Kit.check("G1-③ feat 行键出白名单注入被拒:errs 含 zero_flat〔QA余 V1〕", _has(ft.validate(), "zero_flat"))
|
||
# treasure:真行深拷贝+白名单外键(KEY_WHITELIST 外)
|
||
var tr_row: Dictionary = (_db.crossrun.treasure_items.rows[0] as Dictionary).duplicate(true)
|
||
tr_row["ghost_flat"] = 1
|
||
var tt = load("res://data/schemas/treasure_item_table.gd").new()
|
||
tt.rows = [tr_row]
|
||
Kit.check("G1-③ treasure 行键出白名单注入被拒:errs 含 zero_flat〔QA余 V1〕", _has(tt.validate(), "zero_flat"))
|
||
|
||
|
||
# ---- ④ meta attune_refresh_upgrade keystone_cost 越域注入 ----
|
||
|
||
func _check_keystone_cost_domain() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (keystone_cost 越域拒绝路径——validate 诊断面直调取 errs)
|
||
var bad: Dictionary = (_db.crossrun.meta.attune_refresh_upgrade[0] as Dictionary).duplicate(true)
|
||
bad["keystone_cost"] = 1000 # 域 [1,999] 外
|
||
var mt = load("res://data/schemas/meta_table.gd").new()
|
||
var bad_family: Array[Dictionary] = [bad] # Array[Dictionary] 严格型——未类型化字面量赋值会 SCRIPT ERROR 中断
|
||
mt.attune_refresh_upgrade = bad_family
|
||
Kit.check("G1-④ meta attune_refresh_upgrade keystone_cost=1000 越域注入被拒:errs 含 domain(keystone_cost 越域)〔QA余 V1〕",
|
||
_has(mt.validate(), "domain", "keystone_cost 越域"))
|
||
|
||
|
||
# ---- ⑤ treasure use_phase 出域注入(覆盖 active_dig 枚值面)----
|
||
|
||
func _check_use_phase_domain() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (use_phase 出域拒绝路径——validate 诊断面直调取 errs)
|
||
var TT = load("res://data/schemas/treasure_item_table.gd")
|
||
# 枚值面钉:USE_PHASES 单元素 = active_dig(出域注入覆盖的正是该枚值面)
|
||
Kit.check("G1-⑤ use_phase 枚值面钉:USE_PHASES=[active_dig]〔QA余 V1〕", TT.USE_PHASES == ["active_dig"])
|
||
var bad: Dictionary = (_db.crossrun.treasure_items.rows[0] as Dictionary).duplicate(true)
|
||
bad["use_phase"] = "post_clear_x" # 出域
|
||
var tt = TT.new()
|
||
tt.rows = [bad]
|
||
Kit.check("G1-⑤ treasure use_phase 出域注入被拒:errs 含 enum(use_phase 出域)〔QA余 V1〕",
|
||
_has(tt.validate(), "enum", "use_phase 出域"))
|
||
|
||
|
||
# ---- ⑥ board_gen solver_rules="single" frozen 拒(同钉 single_subset 冻结语义)----
|
||
|
||
func _check_solver_frozen() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (solver_rules 冻结语义拒绝路径——validate 诊断面直调取 errs)
|
||
# 冻结语义正证:真行(single_subset)零 frozen 违例——frozen 只拒 non-single_subset
|
||
var good: Dictionary = (_db.combat.board_gen.rows[0] as Dictionary).duplicate(true)
|
||
var bt = load("res://data/schemas/board_gen_table.gd").new()
|
||
var good_rows: Array[Dictionary] = [good] # Array[Dictionary] 严格型——未类型化字面量赋值会 SCRIPT ERROR 中断
|
||
bt.rows = good_rows
|
||
Kit.check("G1-⑥ 冻结语义正证:solver_rules=single_subset 真行零 frozen 违例〔QA余 V1〕",
|
||
not _has(bt.validate(), "frozen"))
|
||
# 冻结拒:single 在 SOLVER_RULES 词表内(enum 合法)但被冻结面拒——注入仅命中 frozen 违约词
|
||
var bad: Dictionary = (_db.combat.board_gen.rows[0] as Dictionary).duplicate(true)
|
||
bad["solver_rules"] = "single"
|
||
var bt2 = load("res://data/schemas/board_gen_table.gd").new()
|
||
var bad_rows: Array[Dictionary] = [bad]
|
||
bt2.rows = bad_rows
|
||
Kit.check("G1-⑥ solver_rules=single(enum 合法、冻结面外)注入被拒:errs 含 frozen〔QA余 V1〕",
|
||
_has(bt2.validate(), "frozen"))
|
||
|
||
|
||
# ---- ⑦ meta_tree 彩蛋 4 行(>3)load_bearing 注入 ----
|
||
|
||
func _check_meta_tree_load_bearing() -> void:
|
||
# 判据出处: design/engineering/test-conventions.md:67 (彩蛋上限违约词拒绝路径——validate 诊断面直调取 errs)
|
||
var easter: Dictionary = (_db.crossrun.meta_tree.row_by_id("easter_ley") as Dictionary).duplicate(true)
|
||
var mt = load("res://data/schemas/meta_tree_table.gd").new()
|
||
var bad_rows: Array = []
|
||
for i in range(4):
|
||
var r: Dictionary = easter.duplicate(true)
|
||
r["node_id"] = "easter_x%d" % i
|
||
bad_rows.append(r)
|
||
mt.rows = bad_rows
|
||
Kit.check("G1-⑦ meta_tree 彩蛋 4 行(>3)注入被拒:errs 含 load_bearing(彩蛋节点 4 > 3)〔QA余 V1〕",
|
||
_has(mt.validate(), "load_bearing", "彩蛋节点"))
|