"""任务范围新节(目标章细纲、事实系统版本与用途绑定)的离线形状校验。 只测恢复冻结范围对冻结输入的判定:形状与身份不一致必须失败关闭, 不连数据库、不调用模型。 """ import pytest from muse.上下文.接口 import 上下文服务, 上下文错误 from muse.任务运行.模型 import 任务快照, 任务状态, 执行计划 from muse.共享.调用身份 import 用途 from muse.基础设施.数据库.连接 import 数据库工厂 from muse.配置 import 数据库引用 def 服务() -> 上下文服务: 工厂 = 数据库工厂(数据库引用("环境变量", "MUSE_TEST_DATABASE_URL"), 用途.生产) return 上下文服务(工厂, None) def 快照(范围: dict) -> 任务快照: return 任务快照( "task-shape", 任务状态.待运行, "ctx-author", "hash", { "作品ID": 范围["work_id"], "执行用途": "production", "内容用途": "generation", "冻结上下文": {"source_scope": 范围}, }, 执行计划("shape-flow", "1", ()), (), 0, ) def 文稿(编号: str) -> dict: return { "chapter_id": 编号, "branch_id": "main", "revision": 1, "document_id": "doc-" + 编号, "document_hash": "hash-" + 编号, "position": 1, } def 任务范围() -> dict: """含历史文稿、目标章细纲与事实系统版本的完整任务范围。""" return { "work_id": "ctx-work", "as_of": 2, "source_ids": ["doc-ch-1", "plan-target", "obj-fact"], "documents": [文稿("ch-1")], "directory_revision": 3, "authorized_by": "ctx-author", "run_purpose": "production", "content_purpose": "generation", "target_plan": { "chapter_id": "ch-3", "plan_id": "plan-target", "revision": 1, "content_hash": "plan-hash", }, "facts": {"system_revision": 2}, } def test_完整任务范围原样恢复__d1a2f3() -> None: 范围 = 任务范围() assert 服务().恢复冻结范围(快照(范围)) == 范围 def test_无历史文稿但绑定细纲与事实仍可恢复__e4b5c6() -> None: 范围 = {**任务范围(), "documents": [], "source_ids": ["plan-target", "obj-fact"]} assert 服务().恢复冻结范围(快照(范围)) == 范围 @pytest.mark.parametrize( "改动", [ {"target_plan": {"chapter_id": "ch-3", "plan_id": "plan-target", "revision": 0}}, {"target_plan": {"chapter_id": "ch-3", "plan_id": "plan-target", "revision": True}}, {"target_plan": {"chapter_id": "", "plan_id": "plan-target", "revision": 1}}, {"facts": {"system_revision": -1}}, {"facts": {"system_revision": True}}, {"facts": {}}, {"documents": [], "target_plan": None, "facts": None}, ], ids=[ "zero-plan-revision", "boolean-plan-revision", "empty-chapter", "negative-system-revision", "boolean-system-revision", "missing-system-revision", "empty-scope", ], ) def test_非法细纲事实形状或空范围拒绝__f7a8b9(改动: dict) -> None: with pytest.raises(上下文错误) as 异常: 服务().恢复冻结范围(快照({**任务范围(), **改动})) assert 异常.value.错误码 == "CONTEXT_SCOPE_INVALID" def test_细纲身份不在来源清单拒绝__a3c4d5() -> None: 范围 = {**任务范围(), "source_ids": ["doc-ch-1", "obj-fact"]} with pytest.raises(上下文错误) as 异常: 服务().恢复冻结范围(快照(范围)) assert 异常.value.错误码 == "CONTEXT_SCOPE_INVALID" @pytest.mark.parametrize( "改动", [ {"run_purpose": None}, {"content_purpose": "planning"}, {"run_purpose": "production", "content_purpose": "extraction"}, ], ids=["missing-run-purpose", "content-mismatch", "content-unbound"], ) def test_新节必须绑定与任务一致的用途__b6c7d8(改动: dict) -> None: 范围 = {**任务范围(), **改动} if 改动.get("run_purpose") is None: del 范围["run_purpose"] with pytest.raises(上下文错误) as 异常: 服务().恢复冻结范围(快照(范围)) assert 异常.value.错误码 == "CONTEXT_SCOPE_INVALID" def test_仅历史文稿的范围不要求用途键__c9d0e1() -> None: 范围 = 任务范围() for 键 in ("run_purpose", "content_purpose", "target_plan", "facts"): del 范围[键] 范围["source_ids"] = ["doc-ch-1"] assert 服务().恢复冻结范围(快照(范围)) == 范围