101 lines
5.4 KiB
Python
101 lines
5.4 KiB
Python
"""抽取版本与补偿:批量原子登记、失败窗口断点续跑、版本不互抹与观察对照。"""
|
||
|
||
import pytest
|
||
|
||
from muse.任务运行.接口 import 任务状态
|
||
from muse.共享.错误 import Muse错误
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
|
||
|
||
def _来源(工具, 标题: str, 章数: int = 4) -> dict:
|
||
正文 = "".join(f"第{i}回 风\n内容{'甲' * 20}。\n" for i in range(1, 章数 + 1))
|
||
return 工具.导入来源(标题, 正文)
|
||
|
||
|
||
def test_批量原子登记与版本身份__18b03c(研究环境):
|
||
工具 = 研究环境
|
||
来源 = _来源(工具, "样章:原子") # 3 章 → 2 窗(窗口章数默认 3?默认 3 章 1 窗)
|
||
任务 = 工具.发起(来源["source_id"], 理由="题材接近", 窗口章数=2)
|
||
快照 = 工具.装配.任务运行.读取任务(任务["task_id"])
|
||
计划窗数 = len(快照.冻结输入["输入"]["窗计划"])
|
||
for 序 in range(计划窗数):
|
||
工具.剧本.append(
|
||
工具.分析输出([{"name": f"实体{序}", "type": "人物", "evidence": ["内容甲"]}])
|
||
)
|
||
终态 = 工具.跑任务(任务["task_id"])
|
||
assert 终态.状态 == 任务状态.已完成
|
||
版本步 = next(s for s in 终态.步骤 if s["step_id"] == "分析版本登记")
|
||
版本 = 版本步["checkpoint"]["版本"]
|
||
assert 版本["版本ID"] == 任务["版本ID"], "检查点版本身份应与发起预计算一致"
|
||
assert 版本["revision"] == 来源["revision"]
|
||
assert len(版本["完成窗"]) == 计划窗数
|
||
assert 版本["样本依据"]["理由"] == "题材接近"
|
||
|
||
|
||
def test_失败窗口断点续跑只补缺失窗__18b04d(研究环境):
|
||
工具 = 研究环境
|
||
来源 = _来源(工具, "样章:断点")
|
||
任务 = 工具.发起(来源["source_id"])
|
||
快照 = 工具.装配.任务运行.读取任务(任务["task_id"])
|
||
计划 = 快照.冻结输入["输入"]["窗计划"]
|
||
assert len(计划) >= 2, "用例需要至少两个窗口"
|
||
# 首窗有效、次窗合同无效:窗口一结果经检查点持久化,任务失败停可解释断点。
|
||
工具.剧本.append(工具.分析输出([{"name": "首窗实体", "type": "人物", "evidence": ["内容甲"]}]))
|
||
工具.剧本.append({"类型": "文本", "文本": {"chapters": []}})
|
||
with pytest.raises((Muse错误, RuntimeError, OSError)):
|
||
工具.跑任务(任务["task_id"])
|
||
失败态 = 工具.装配.任务运行.读取任务(任务["task_id"])
|
||
assert 失败态.状态 == 任务状态.已失败
|
||
中间检查点 = next(s for s in 失败态.步骤 if s["step_id"] == "逐窗提取")
|
||
已存窗 = set((中间检查点.get("checkpoint") or {}).get("窗口结果") or {})
|
||
assert "w001" in 已存窗, "首窗成果应在失败前已持久化"
|
||
assert "w002" not in 已存窗, "失败窗不应伪称完成"
|
||
# 恢复:只补缺失窗;剧本只放一条,若重跑已存窗会因剧本耗尽拿到空对象而再次失败。
|
||
运行 = 工具.装配.任务运行
|
||
运行.控制任务(
|
||
任务["task_id"],
|
||
"post-author",
|
||
任务状态.已失败,
|
||
"恢复",
|
||
命令ID=f"resume-{任务['task_id'][:8]}",
|
||
)
|
||
工具.剧本.append(工具.分析输出([{"name": "次窗实体", "type": "地点", "evidence": ["内容甲"]}]))
|
||
终态 = 工具.跑任务(任务["task_id"])
|
||
assert 终态.状态 == 任务状态.已完成
|
||
版本步 = next(s for s in 终态.步骤 if s["step_id"] == "分析版本登记")
|
||
版本 = 版本步["checkpoint"]["版本"]
|
||
assert sorted(版本["完成窗"]) == sorted(窗["窗ID"] for 窗 in 计划)
|
||
|
||
|
||
def test_来源前进旧版本结论保留且新拆书指向新版本__18b05e(研究环境):
|
||
工具 = 研究环境
|
||
来源 = _来源(工具, "样章:漂移")
|
||
任务 = 工具.发起(来源["source_id"])
|
||
快照 = 工具.装配.任务运行.读取任务(任务["task_id"])
|
||
for _ in 快照.冻结输入["输入"]["窗计划"]:
|
||
工具.剧本.append(
|
||
工具.分析输出([{"name": "旧版实体", "type": "人物", "evidence": ["内容甲"]}])
|
||
)
|
||
# 外部漂移:拆书进行中来源前进到 r2;r1 冻结分析照常完成,不被抹掉。
|
||
工具.导入来源("样章:漂移", "第1回 风\n内容换新版本。" * 3)
|
||
终态 = 工具.跑任务(任务["task_id"])
|
||
assert 终态.状态 == 任务状态.已完成
|
||
旧版本 = next(s for s in 终态.步骤 if s["step_id"] == "分析版本登记")["checkpoint"]["版本"]
|
||
assert 旧版本["revision"] == 1 and 旧版本["content_hash"] == 来源["content_hash"]
|
||
# 新拆书取最新版本:版本身份不同,旧结论仍可回查。
|
||
新任务 = 工具.发起(来源["source_id"])
|
||
assert 新任务["版本ID"] != 旧版本["版本ID"]
|
||
新快照 = 工具.装配.任务运行.读取任务(新任务["task_id"])
|
||
assert 新快照.冻结输入["输入"]["拆书"]["revision"] == 2
|
||
for _ in 新快照.冻结输入["输入"]["窗计划"]:
|
||
工具.剧本.append(
|
||
工具.分析输出([{"name": "新版实体", "type": "人物", "evidence": ["内容换新"]}])
|
||
)
|
||
assert 工具.跑任务(新任务["task_id"]).状态 == 任务状态.已完成
|
||
from muse.资料研究.分析版本 import 版本台账
|
||
|
||
台账 = 版本台账(工具.装配.任务运行.列出任务("post-author"))
|
||
本源 = [行 for 行 in 台账 if 行["source_id"] == 来源["source_id"]]
|
||
assert {行["版本ID"] for 行 in 本源} == {旧版本["版本ID"], 新任务["版本ID"]}
|