206 lines
8.6 KiB
Python
206 lines
8.6 KiB
Python
"""探索清单与确定性回放的合同行为:未知工具、越界参数、缺来源拒绝;顺序、范围、理由与消费哈希可核对。
|
|
|
|
离线纯函数与合成读取器;不连库、不代表真实来源授权。旧 TC 分配不承身份,按功能价值新写。
|
|
"""
|
|
|
|
import pytest
|
|
from jsonschema import Draft202012Validator
|
|
|
|
from muse.上下文.接口 import (
|
|
上下文错误,
|
|
探索条目,
|
|
来源绑定,
|
|
校验清单,
|
|
记录探索,
|
|
逐项比对,
|
|
)
|
|
from muse.任务运行.接口 import (
|
|
内容哈希,
|
|
只读工具集,
|
|
工具定义,
|
|
工具来源,
|
|
工具结果,
|
|
工具范围,
|
|
工具请求,
|
|
模型协议错误,
|
|
)
|
|
|
|
范围 = {
|
|
"work_id": "work-a",
|
|
"as_of": 2,
|
|
"source_ids": ["doc-1", "plan-1", "fact-lin"],
|
|
"documents": [
|
|
{
|
|
"chapter_id": "ch-1",
|
|
"branch_id": "main",
|
|
"revision": 1,
|
|
"document_id": "doc-1",
|
|
"document_hash": "hash-doc-1",
|
|
"position": 1,
|
|
}
|
|
],
|
|
"directory_revision": 3,
|
|
"authorized_by": "author-a",
|
|
"run_purpose": "production",
|
|
"content_purpose": "generation",
|
|
"target_plan": {
|
|
"chapter_id": "ch-3",
|
|
"plan_id": "plan-1",
|
|
"revision": 1,
|
|
"content_hash": "hash-plan-1",
|
|
},
|
|
"facts": {"system_revision": 5},
|
|
}
|
|
|
|
投影 = "visible-text-codepoints/v1:production:generation"
|
|
文档来源 = 工具来源("doc-1", "1", "hash-doc-1", 投影)
|
|
文档绑定 = (来源绑定("doc-1", "1", "hash-doc-1", 投影),)
|
|
文档结果 = 工具结果({"chapter_id": "ch-1", "text": "林深停在码头,雨还没停。"}, (文档来源,))
|
|
|
|
|
|
def 带参条目(工具名: str, 参数: dict, 顺序: int = 1, 调用ID: str = "call-x") -> 探索条目:
|
|
"""以固定来源绑定与消费哈希构造任意参数条目;参数合法性正是被测对象。"""
|
|
return 探索条目(
|
|
调用ID=调用ID,
|
|
工具名=工具名,
|
|
顺序=顺序,
|
|
参数=参数,
|
|
来源引用=文档绑定,
|
|
消费哈希=内容哈希(文档结果.内容),
|
|
)
|
|
|
|
|
|
def test_未知工具与未授权工具分别明确拒绝__b7e0a1() -> None:
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((带参条目("read_anything", {"chapter_id": "ch-1"}),), 范围)
|
|
assert 异常.value.错误码 == "CONTEXT_INPUT_INVALID"
|
|
assert "未知工具" in 异常.value.说明 and "read_anything" in 异常.value.说明
|
|
# 工具真实存在但任务范围未冻结对应来源:拒绝的是授权,不是字符串存在性
|
|
无细纲 = {**范围, "target_plan": None}
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((带参条目("query_work_plan", {}),), 无细纲)
|
|
assert 异常.value.错误码 == "SCOPE_DENIED"
|
|
assert "query_work_plan" in 异常.value.说明 and "冻结范围" in 异常.value.说明
|
|
无事实 = {**范围, "facts": None}
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((带参条目("query_facts_as_of", {}),), 无事实)
|
|
assert 异常.value.错误码 == "SCOPE_DENIED"
|
|
|
|
|
|
def test_参数越出冻结范围与扩权字段拒绝__b7e0a2() -> None:
|
|
未冻结版本 = 带参条目("read_history_document", {"chapter_id": "ch-1", "revision": 2})
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((未冻结版本,), 范围)
|
|
assert 异常.value.错误码 == "SCOPE_DENIED"
|
|
assert "不在冻结范围内" in 异常.value.说明 and "版本 2" in 异常.value.说明
|
|
未冻结文稿 = 带参条目(
|
|
"read_history_document", {"chapter_id": "ch-2", "revision": 1, "branch_id": "draft"}
|
|
)
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((未冻结文稿,), 范围)
|
|
assert 异常.value.错误码 == "SCOPE_DENIED"
|
|
扩权 = 带参条目("read_history_document", {"chapter_id": "ch-1", "revision": 1, "as_of": 9})
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((扩权,), 范围)
|
|
assert 异常.value.错误码 == "SCOPE_DENIED"
|
|
assert "扩权" in 异常.value.说明 or "不得" in 异常.value.说明
|
|
# 角色视角必须携带知情主体,其他视角不得携带:与工具读取同一规则
|
|
无知情主体 = 带参条目("query_facts_as_of", {"view": "character"})
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((无知情主体,), 范围)
|
|
assert 异常.value.错误码 == "CONTEXT_INPUT_INVALID"
|
|
assert "知情主体" in 异常.value.说明
|
|
非法视角 = 带参条目("query_facts_as_of", {"view": "narrator"})
|
|
with pytest.raises(上下文错误) as 异常:
|
|
校验清单((非法视角,), 范围)
|
|
assert 异常.value.错误码 == "CONTEXT_INPUT_INVALID"
|
|
|
|
|
|
def test_回放比对指明版本哈希投影与内容漂移__b7e0a3() -> None:
|
|
基准 = 带参条目(
|
|
"read_history_document", {"chapter_id": "ch-1", "revision": 1}, 调用ID="call-hist"
|
|
)
|
|
assert 逐项比对(基准, 文档结果) == ()
|
|
版本漂移 = 逐项比对(
|
|
基准, 工具结果(文档结果.内容, (工具来源("doc-1", "2", "hash-doc-1", 投影),))
|
|
)
|
|
assert len(版本漂移) == 1 and "数据版本" in 版本漂移[0] and "call-hist" in 版本漂移[0]
|
|
哈希漂移 = 逐项比对(基准, 工具结果(文档结果.内容, (工具来源("doc-1", "1", "hash-改", 投影),)))
|
|
assert len(哈希漂移) == 1 and "结构哈希" in 哈希漂移[0]
|
|
投影漂移 = 逐项比对(
|
|
基准, 工具结果(文档结果.内容, (工具来源("doc-1", "1", "hash-doc-1", "proj/v2"),))
|
|
)
|
|
assert len(投影漂移) == 1 and "投影版本" in 投影漂移[0]
|
|
内容漂移 = 逐项比对(基准, 工具结果({"chapter_id": "ch-1", "text": "改过的正文"}, (文档来源,)))
|
|
assert any("消费哈希" in 项 for 项 in 内容漂移)
|
|
来源消失 = 逐项比对(基准, 工具结果(文档结果.内容, ()))
|
|
assert any("已不在" in 项 for 项 in 来源消失)
|
|
清单外来源 = 逐项比对(
|
|
基准, 工具结果(文档结果.内容, (文档来源, 工具来源("doc-9", "1", "hash-doc-1", 投影)))
|
|
)
|
|
assert any("清单外来源 doc-9" in 项 for 项 in 清单外来源)
|
|
|
|
|
|
def test_记录探索消费哈希与来源绑定同键__b7e0a4() -> None:
|
|
记录 = []
|
|
|
|
def 读取(_, 参数):
|
|
assert 参数 == {"chapter_id": "ch-1", "revision": 1}
|
|
return 文档结果
|
|
|
|
定义 = 工具定义(
|
|
"read_history_document",
|
|
"读取历史正文",
|
|
{
|
|
"type": "object",
|
|
"additionalProperties": False,
|
|
"required": ["chapter_id", "revision"],
|
|
"properties": {
|
|
"chapter_id": {"type": "string", "minLength": 1},
|
|
"revision": {"type": "integer", "minimum": 1},
|
|
},
|
|
},
|
|
读取,
|
|
)
|
|
Draft202012Validator.check_schema(定义.参数合同)
|
|
集合 = 只读工具集(
|
|
(定义,),
|
|
("read_history_document",),
|
|
工具范围("task-r", "work-a", ("doc-1",), 2, "production", "generation"),
|
|
记录.append,
|
|
)
|
|
参数 = {"chapter_id": "ch-1", "revision": 1}
|
|
结果 = 集合.调用(工具请求("call-hist", "read_history_document", 参数))
|
|
条目 = 记录探索((), "call-hist", "read_history_document", 参数, 结果)[0]
|
|
# 清单条目与 S02 工具读取事件留痕同键:来源四元组逐字段一致
|
|
留痕 = 记录[0]["source_refs"][0]
|
|
绑定 = 条目.来源引用[0]
|
|
assert (绑定.来源ID, 绑定.数据版本, 绑定.结构哈希, 绑定.投影版本) == (
|
|
留痕["source_id"],
|
|
留痕["revision"],
|
|
留痕["schema_hash"],
|
|
留痕["projection_version"],
|
|
)
|
|
assert 条目.消费哈希 == 内容哈希(结果.内容)
|
|
assert 条目.顺序 == 1
|
|
校验清单((条目,), 范围)
|
|
# 工具读取被 S02 拒绝时不产生清单可承接的结果
|
|
with pytest.raises(模型协议错误):
|
|
集合.调用(
|
|
工具请求("call-bad", "read_history_document", {"chapter_id": "ch-1", "work_id": "x"})
|
|
)
|
|
assert len(记录) == 1
|
|
|
|
|
|
def test_三类工具清单按序通过且顺序可核对__b7e0a5() -> None:
|
|
清单: tuple[探索条目, ...] = ()
|
|
for 调用ID, 工具名, 参数 in [
|
|
("call-plan", "query_work_plan", {}),
|
|
("call-facts", "query_facts_as_of", {}),
|
|
("call-hist", "read_history_document", {"chapter_id": "ch-1", "revision": 1, "end": 6}),
|
|
]:
|
|
清单 = 记录探索(清单, 调用ID, 工具名, 参数, 文档结果)
|
|
校验清单(清单, 范围) # 不抛异常即通过
|
|
assert [项.调用ID for 项 in 清单] == ["call-plan", "call-facts", "call-hist"]
|
|
assert [项.顺序 for 项 in 清单] == [1, 2, 3]
|