muse-agent-example/tests/集成/test_维护任务恢复.py
zizi 9e6f1c4481 R2 改造交付:新版模块化单体全量成果
- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具)
- 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺
- 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口
- 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影)
- 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威)
- R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
2026-09-15 12:47:42 +08:00

138 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""维护流程使用真实S02状态与owner回执,关闭会话不会取消已登记工作。"""
from dataclasses import replace
from datetime import timedelta
from uuid import uuid4
import pytest
import test_原文归档与恢复 as 原文测试
from muse.任务运行.接口 import 任务状态
from muse.共享.调用身份 import 用途
from muse.共享.错误 import 配置错误
from muse.启动 import 构建
from muse.基础设施.备份恢复 import 列出备份, 创建备份, 备份恢复错误
from muse.编排.数据维护 import 发起维护任务, 推进维护任务
from muse.资源加载 import 加载清单
from muse.配置 import 应用配置
pytestmark = pytest.mark.数据库
原文环境 = 原文测试.原文环境
def test_备份核对计划跨装配继续且冻结目录与命令范围__29c001(应用测试库, tmp_path):
root = tmp_path / "backups"
root.mkdir(mode=0o700)
manifest = 创建备份(
应用配置(应用测试库[用途.维护].引用, 加载清单()["构建身份"], 运行用途=用途.维护),
root / "one",
)
row = 列出备份(str(root))["items"][0]
config = 应用配置(应用测试库[用途.生产].引用, 加载清单()["构建身份"], 备份目录=str(root))
app = 构建(config)
request = {
"action": "verify_backup",
"entry_id": row["entry_id"],
"backup_id": manifest["backup_id"],
}
created = 发起维护任务(app, "author", "check-one", request)
task_id = created["task_id"]
assert 发起维护任务(app, "author", "check-one", request) == created
with pytest.raises(配置错误, match="重放"):
发起维护任务(app, "author", "check-one", {**request, "backup_id": "another"})
claim = app.任务运行.领取步骤(
"before-restart", ["maintenance.verify"], 任务ID=task_id, 作者="author"
)
assert claim is not None
app.任务运行.执行一步(claim)
checkpoint = app.任务运行.读取任务(task_id).步骤[0]["checkpoint"]
assert checkpoint["scope_hash"]
wrong = 构建(replace(config, 备份目录=str(tmp_path / "other")))
with pytest.raises(配置错误, match="范围已变化"):
推进维护任务(wrong, "author", task_id)
assert app.任务运行.读取任务(task_id).状态 == 任务状态.已失败
fresh = 构建(config)
fresh.任务运行.控制任务(task_id, "author", 任务状态.已失败, "恢复", 命令ID="resume")
with pytest.raises(配置错误, match="当前作者"):
推进维护任务(fresh, "another", task_id)
finished = 推进维护任务(fresh, "author", task_id)
assert finished["state"] == "completed"
assert finished["steps"][0]["checkpoint"] == checkpoint
result = finished["steps"][-1]["result"]
assert result["backup_id"] == manifest["backup_id"]
assert result["archive_verified"] and not result["restore_verified"]
def test_归档损坏不伪称通过且修复后原任务可恢复__29c002(应用测试库, tmp_path):
root = tmp_path / "backups"
root.mkdir(mode=0o700)
manifest = 创建备份(
应用配置(应用测试库[用途.维护].引用, 加载清单()["构建身份"], 运行用途=用途.维护),
root / "one",
)
row = 列出备份(str(root))["items"][0]
app = 构建(应用配置(应用测试库[用途.生产].引用, 加载清单()["构建身份"], 备份目录=str(root)))
task_id = 发起维护任务(
app,
"author",
"check-bad",
{
"action": "verify_backup",
"entry_id": row["entry_id"],
"backup_id": manifest["backup_id"],
},
)["task_id"]
dump = root / "one/database.dump"
original = dump.read_bytes()
dump.write_bytes(original + b"bad")
with pytest.raises(备份恢复错误):
推进维护任务(app, "author", task_id)
failed = app.任务运行.读取任务(task_id)
assert failed.状态 == 任务状态.已失败
assert not failed.步骤[-1]["result"]
dump.write_bytes(original)
app.任务运行.控制任务(task_id, "author", 任务状态.已失败, "恢复", 命令ID="repaired")
assert 推进维护任务(app, "author", task_id)["state"] == "completed"
def test_原文维护保留有效租约并保留孤儿清理真实回执__29c003(原文环境):
raw, original_task, authorization, data, digest, clock, pools = 原文环境
lease = raw.创建租约(
original_task, authorization, "keep", clock.现在() + timedelta(hours=1), 最少剩余秒=0
)
raw.写入(original_task, lease, digest, data)
orphan = str(uuid4())
raw.文件.写入(orphan, digest, data)
app = 构建(
应用配置(pools[用途.生产].引用, 加载清单()["构建身份"], 原文暂存=str(raw.文件.根)),
时钟=clock,
)
recover = 发起维护任务(
app,
"author",
"raw-recover",
{
"action": "recover_raw",
"target_task_id": original_task,
},
)["task_id"]
done = 推进维护任务(app, "author", recover)
assert done["steps"][-1]["result"]["items"] == [{"lease_id": lease, "action": "retained"}]
with pytest.raises(配置错误, match="不属于"):
发起维护任务(
app,
"other",
"wrong-owner",
{
"action": "recover_raw",
"target_task_id": original_task,
},
)
clean = 发起维护任务(app, "author", "orphan-clean", {"action": "clean_orphans"})["task_id"]
output = 推进维护任务(app, "author", clean)["steps"][-1]["result"]
assert output["cleaned_orphan_ids"] == [orphan]
receipt = raw.读取孤儿清理回执(orphan)
assert receipt["state"] == "completed" and receipt["receipt_id"] in output["receipts"]
assert raw.读取(original_task, lease, digest) == data
assert 推进维护任务(app, "author", clean)["steps"][-1]["result"] == output