# ============================================================ # M2 状态治理黄金测试(moduleId: golden-m2-state, 发版门禁 §14.3) # 固化三个核心不变式: # S1 Explore 沙盒隔离:方案对比绝不污染主干世界(§5.1 铁律) # S2 Checkpoint 往返:建档 → 破坏 → 回滚 → 完整恢复(§4.3) # S3 审计哈希链:正常链校验通过;篡改任意字段即断链(§3.6) # ============================================================ from __future__ import annotations # 前向类型引用 import copy # 世界对照拷贝 import json # 深比较辅助 from server.agent_core.audit import write_audit # 审计写入 from server.agent_core.registry import verify_audit_chain # 链校验 from server.aps_domain.scenario import compare_scenarios # 方案对比(沙盒) from server.state.checkpoints import CheckpointStore # 快照仓(用临时路径实例化) from server.state.seed import seed_world # 种子世界 def _next_id_factory(): """独立发号器(用例内存态)。""" counters: dict[str, int] = {} # 计数器 def next_id(kind: str) -> int: # 闭包发号 counters[kind] = counters.get(kind, 0) + 1 # 自增 return counters[kind] # 返回 return next_id # 闭包 # ---------------- S1:沙盒隔离 ---------------- def test_scenario_compare_does_not_mutate_world(): """方案对比在深拷贝沙盒运行:主干世界的所有表必须逐字节不变。""" world = seed_world() # 主干世界 before = json.dumps(world, ensure_ascii=False, sort_keys=True) # 运行前指纹 text, block = compare_scenarios(world) # 执行三策略沙盒对比 after = json.dumps(world, ensure_ascii=False, sort_keys=True) # 运行后指纹 assert before == after, "方案对比污染了主干世界(违反 §5.1 Explore 铁律)" # 逐字节一致 assert len(block.props["cards"]) == 3 # 产出三张方案卡 for card in block.props["cards"]: # 逐卡校验结构 assert card["kpi"]["woCount"] > 0 # 沙盒确实完成了排产 assert card["strategy"] in ("DELIVERY_FIRST", "CAPACITY_BALANCE", "COMPREHENSIVE") # 策略合法 assert "沙盒" in text # 文案明示未影响当前方案 # ---------------- S2:Checkpoint 往返 ---------------- def test_checkpoint_roundtrip(tmp_path): """建档 → 破坏世界 → 从档恢复:恢复结果与建档时刻逐字节一致。""" store = CheckpointStore(path=str(tmp_path / "ckpt.json")) # 临时路径快照仓(隔离生产文件) world = seed_world() # 初始世界 baseline = copy.deepcopy(world) # 建档时刻的对照副本 meta = store.create(world, label="测试基线", reason="manual") # 建档 assert meta["pairId"] # 拿到快照 ID world["salesOrders"].clear() # 破坏:清空订单 world["scheduleVersions"].append({"id": 999}) # 破坏:塞入脏版本 pair = store.get(meta["pairId"]) # 取回快照 assert pair is not None # 快照存在 restored = pair["world"] # 快照中的世界 assert json.dumps(restored, ensure_ascii=False, sort_keys=True) == \ json.dumps(baseline, ensure_ascii=False, sort_keys=True), "快照未完整保留建档时刻状态" # 逐字节恢复 # 快照与运行态隔离:破坏运行态不应影响已存快照(深拷贝验证) assert len(restored["salesOrders"]) == 7 # 快照内订单完好 # ---------------- S2b:容量淘汰 ---------------- def test_checkpoint_capacity_eviction(tmp_path): """快照数超过上限时淘汰最旧(防文件膨胀)。""" store = CheckpointStore(path=str(tmp_path / "ckpt.json")) # 临时快照仓 world = seed_world() # 世界 ids = [store.create(world, label=f"c{i}", reason="manual")["pairId"] for i in range(25)] # 建 25 档(上限 20) assert len(store.pairs) == 20 # 只保留 20 个 assert store.get(ids[0]) is None # 最旧的已被淘汰 assert store.get(ids[-1]) is not None # 最新的仍在 # ---------------- S3:审计哈希链 ---------------- def test_audit_chain_verify_and_tamper_detection(): """正常链校验通过;篡改任一历史事件的任一字段必须被检出。""" world = seed_world() # 世界(审计表随种子创建) nid = _next_id_factory() # 发号器 for i in range(5): # 连续写 5 条审计 write_audit(world, nid, actor="tester", category="TOOL", action=f"op{i}", target={"type": "T", "id": i}, power="P0", rationale={}) events = world["auditEvents"] # 审计链 assert verify_audit_chain(events)["ok"] is True # 完整链校验通过 tampered = copy.deepcopy(events) # 拷贝后篡改 tampered[2]["actor"] = "hacker" # 篡改第 3 条的行为人 result = verify_audit_chain(tampered) # 重新校验 assert result["ok"] is False # 必须检出断链 assert result["checked"] == 2 # 断点定位在被篡改处