Files
gplan-2/game/tests/unit/data_config/aux_target_violation_test.gd
T
DKChan 63a08d1e01 feat(t1-t3): 魔法道具数据面+库存管线+授予路由生命周期(三卡一轮交付——文件交叉合并提交)
T1 数据面:AuxItemTable schema(七字段+retired/deferred 两 additive 标记+真视窄域/board_wide 双承重护栏)+六行 tres(三有效+罗盘 retired 留痕+显影尘/全图卷 deferred)+ConfigDB 加载/访问器+RewardTable 十型扩值(aux_item)+三投放行 5/4/1+aux_none 负占位行(雷格极性字典兜底零兑付)+双表悬空校验(reward_dangling_aux,负占位豁免)+F2 投放维度覆盖(豁免面 plan/info/counter 登记)+两 fatal 夹具(expect=1 复制真表注入违规)+run_all manifest+2
T2 core 骨架:AuxItems 类(api 8 入口+probe 探针;零随机零特判;Used 不可逆;真视恒可用/预读点击级零信息拒绝;3x3 边缘窗口∩板)+action_economy.grant_ap 第 9 API(当轮加量,区别 queue_ap_delta 排队)
T3 授予路由+生命周期:_grant_routed aux 分支(正=入库存发「获得」行;负=零兑付 effect 型「道具随雷沉没」行)+use_aux 四步受理(相位 gate→validate→逐 kind 空间先 AP 后→consume;结算明细无道具行)+叠加层三清(装板点/战斗结束)+LevelRunner 关卡域库存持有注入+关卡末 reset+aux_cleared 快照计数扩签
冒烟锚随词表扩值更新:九型→十型封闭/字典 18→22 行;负向字典扫描三处稳健化(aux 不入五型轴+break 改 percent_map 三型齐备+rng 每板新实例——原第一板 break 掩盖 set_master_seed _counter 守卫违例)
闸:冒烟 577/0(净增 1)+单测 11 套全过(含两新夹具)+四探针 PASS
2026-09-29 07:07:38 +08:00

77 lines
3.0 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.
# 致命类用例(独立进程,期望退出码 1)
# aux_target_violation——真视窄域护栏(魔法道具 GDD 结构冻结,迭代 7 T1):mine_reveal 行 target
# 越域至 board_wide 时,AuxItemTable.validate 必须报 load_bearing 错、load_tables 整体拒绝。
# 运行:godot --headless --path game --script res://tests/unit/data_config/aux_target_violation_test.gd
# 退出码协议:1 = 校验正确拒绝(预期);2 = 违规表被放行(回归信号)。
# 时序注:纯加载期校验——不触运行期 fatal 通道,断言 load_tables 返回 false + 诊断在案即 quit(1)。
extends SceneTree
const ConfigDBScript := preload("res://core/config_db.gd")
const SRC_DIR := "res://data/tables"
const BAD_DIR := "user://aux_fixture_target_violation"
func _copy_tables() -> bool:
DirAccess.make_dir_recursive_absolute(BAD_DIR)
var dir := DirAccess.open(SRC_DIR)
if dir == null:
return false
dir.list_dir_begin()
var f := dir.get_next()
while f != "":
if not dir.current_is_dir():
var err := dir.copy(SRC_DIR + "/" + f, BAD_DIR + "/" + f)
if err != OK:
return false
f = dir.get_next()
return true
func _inject_violation() -> bool:
var path := BAD_DIR + "/aux_items.tres"
var f := FileAccess.open(path, FileAccess.READ)
if f == null:
return false
var s := f.get_as_text()
f.close()
# 探巢摆钟(mine_reveal 活行)target area_3x3 → board_wide:双护栏同时命中
var bad := s.replace('"item_id": "nest_pendulum",\r\n"display_name": "探巢摆钟",', '"item_id": "nest_pendulum",\r\n"display_name": "探巢摆钟",')
if bad == s:
# 换行形态回退(LF)
bad = s.replace('"item_id": "nest_pendulum",\n"display_name": "探巢摆钟",', '"item_id": "nest_pendulum",\n"display_name": "探巢摆钟",')
# 直接改 target:摆钟行是全表唯一的 mine_reveal 单维行——把它的 area_3x3 改 board_wide
# (罗盘 retired 行同值 area_3x3 但 retired 免校验——替换须定位摆钟块内)
var blocks := s.split("}, {")
var hit := false
for i in blocks.size():
if '"item_id": "nest_pendulum"' in blocks[i]:
blocks[i] = blocks[i].replace('"target": "area_3x3"', '"target": "board_wide"')
hit = true
s = "}, {".join(blocks)
var w := FileAccess.open(path, FileAccess.WRITE)
if w == null or not hit:
return false
w.store_string(s)
w.close()
return true
func _init() -> void:
if not _copy_tables() or not _inject_violation():
push_error("[aux_target_violation_test] 夹具目录准备失败")
quit(2)
return
var db = ConfigDBScript.new()
var ok: bool = db.load_tables(BAD_DIR, false)
if ok:
push_error("[aux_target_violation_test] 违规表被放行——真视窄域护栏失守(回归)")
quit(2)
return
if db.diagnostics_count("error") >= 1:
print("[aux_target_violation_test] 护栏触发(load_bearing 诊断在案,加载被拒)")
quit(1)
return
push_error("[aux_target_violation_test] 加载被拒但无 load_bearing 诊断——护栏通道静默化(回归)")
quit(2)