# 统一 EvidenceItem 与可追溯链黄金测试(plan.md §3.4 / 矩阵 114 行) # ============================================================ # 覆盖:构造/序列化、链确定性、元素变化断链、ref 解析、schedule-version 兼容。 from __future__ import annotations import pytest from server.agent_core.evidence import ( EvidenceItem, evidence_ref, parse_evidence_ref, trace_chain, verify_chain, schedule_version_item, ) def test_evidence_ref_roundtrip(): """证据引用:: 生成与解析往返。""" ref = evidence_ref("schedule-version", 12) assert ref == "schedule-version:12" assert parse_evidence_ref(ref) == ("schedule-version", "12") with pytest.raises(ValueError): parse_evidence_ref("no-colon") def test_evidence_ref_kind_whitelist(): """未知 kind 拒绝(白名单防注入)。""" with pytest.raises(ValueError): evidence_ref("../../etc", "x") with pytest.raises(ValueError): EvidenceItem(kind="hack", ref="x") def test_trace_chain_deterministic(): """链确定性:同输入同哈希;数量变化断链。""" items = [ EvidenceItem(kind="run", ref="run-1", engine="CP", seed=42), EvidenceItem(kind="knowledge", ref="kb-1", knowledgeVersion="v3"), EvidenceItem(kind="confirm", ref="c-1", confirmId="abc123"), ] a = trace_chain(items) b = trace_chain(items) assert a["chainHash"] == b["chainHash"] assert a["count"] == 3 assert verify_chain(items, a["chainHash"]) is True assert verify_chain(items[:-1], a["chainHash"]) is False # 数量变化断链 def test_trace_chain_sensitive_to_content_and_order(): """内容/顺序变化断链(可复算可追溯)。""" items = [ EvidenceItem(kind="run", ref="run-1", engine="CP", seed=42), EvidenceItem(kind="algorithm", ref="alg-1", version="1.2"), ] base = trace_chain(items)["chainHash"] # 内容变化(seed 改) changed = [EvidenceItem(kind="run", ref="run-1", engine="CP", seed=99), EvidenceItem(kind="algorithm", ref="alg-1", version="1.2")] assert trace_chain(changed)["chainHash"] != base # 顺序变化 swapped = list(reversed(items)) assert trace_chain(swapped)["chainHash"] != base def test_schedule_version_item_compatible(): """schedule-version 证据项与既有协议兼容。""" item = schedule_version_item(7, runId="run-9") assert item.kind == "schedule-version" assert item.ref == "7" assert item.version == "7" ref = evidence_ref(item.kind, item.ref) assert parse_evidence_ref(ref) == ("schedule-version", "7") def test_trace_chain_serializes_items(): """trace_chain 输出 items 为规范化 dict 列表(可复算输入)。""" items = [EvidenceItem(kind="report", ref="rpt-1", meta={"x": 1})] out = trace_chain(items) assert out["items"][0]["kind"] == "report" assert out["items"][0]["meta"] == {"x": 1} # 用输出 dict 复算同链 assert trace_chain(out["items"])["chainHash"] == out["chainHash"]