80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
# 统一 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():
|
||
"""证据引用:<kind>:<id> 生成与解析往返。"""
|
||
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"]
|