接续 88dd570,保存 W20–W24 已实现的共享接口、业务入口、迁移、工作台、测试与文档。 W20/W22/W23 保持 in_progress,W21/W24 保持 verified;此提交不宣称方法或规则正式启用、多轮返修、真实角色评测完成。 W25 新增实验、标定、逐调用交付与角色执行及其迁移/测试/索引留在实施工作树,原有私人和旧实现保留项不纳入。 验证:离线 571、前端 33 通过;PG 469 项通过、2 项浏览器未启用,2 项误带入的 W25 用例已移出本提交;最终任务与交付边界 37 项通过。make 检查、最终类型、84 项资源及 diff 检查通过。未重跑浏览器或 Pi 宿主,不以合成调用认证外部模型效果。 独立整体审查四维通过;证据保存在 R2-20260909/提交W20-W24。
125 lines
5.2 KiB
Python
125 lines
5.2 KiB
Python
"""真实诊断与明确作者范围生成新候选,当前正文保持;失败无副作用。"""
|
|
|
|
from dataclasses import asdict, replace
|
|
|
|
import pytest
|
|
import test_规则库与诊断 as 诊断测试
|
|
|
|
from muse.共享.错误 import Muse错误
|
|
from muse.审校修订.接口 import 修订请求, 允许选区
|
|
from muse.正式变更.接口 import 固定哈希
|
|
from muse.正文写作.接口 import 文本节点, 正文草稿, 段落, 段落哈希, 选段修改
|
|
|
|
pytestmark = pytest.mark.数据库
|
|
诊断环境 = 诊断测试.诊断环境
|
|
|
|
|
|
def 准备(env):
|
|
app, 身份, target = env
|
|
svc = app.要求审校()
|
|
svc.导入规则候选(身份)
|
|
report = svc.生成诊断(身份, target, 预览候选=True)
|
|
finding = next(f for f in report["report"]["findings"] if f["rule_id"] == "l002")
|
|
doc = app.要求正文().读取正文(身份, "diagnosis-ch")
|
|
from pydantic import TypeAdapter
|
|
|
|
draft = TypeAdapter(正文草稿).validate_python(doc["document"])
|
|
p = draft.paragraphs[0]
|
|
patch = 选段修改(p.paragraph_id, 段落哈希(p), 0, 7, "值得注意的是,", "", finding["id"])
|
|
return (
|
|
svc,
|
|
身份,
|
|
修订请求(
|
|
report["diagnosis_id"],
|
|
固定哈希(report["report"]),
|
|
(patch,),
|
|
(允许选区("p1", 0, 7),),
|
|
(finding["id"],),
|
|
"移除作者确认无功能的元话语",
|
|
),
|
|
doc,
|
|
)
|
|
|
|
|
|
def test_真实诊断批准选区仅生成候选且同命令幂等__231001(诊断环境):
|
|
svc, 身份, 请求, 原稿 = 准备(诊断环境)
|
|
app = 诊断环境[0]
|
|
receipt = svc.提出修订(身份, "patch-1", 请求)
|
|
assert receipt == svc.提出修订(身份, "patch-1", 请求)
|
|
candidate = app.要求正文().读取候选(身份, receipt["target_ref"])
|
|
assert candidate["visible_text"] == "窗外正在下雨。"
|
|
assert candidate["decision"] == "undecided"
|
|
assert app.要求正文().读取正文(身份, "diagnosis-ch") == 原稿
|
|
assert receipt["results"][0]["checks"]["semantic_status"] == "unverified"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"bad", ["missing_finding", "unapproved", "outside", "hash", "text", "purpose"]
|
|
)
|
|
def test_伪发现和越范围修订不产生候选__231002(诊断环境, bad):
|
|
svc, 身份, r, 原稿 = 准备(诊断环境)
|
|
app = 诊断环境[0]
|
|
with pytest.raises((Muse错误, ValueError)):
|
|
if bad == "missing_finding":
|
|
r = replace(r, patches=(replace(r.patches[0], finding_id="fake"),))
|
|
elif bad == "unapproved":
|
|
r = replace(r, approved_findings=())
|
|
elif bad == "outside":
|
|
r = replace(r, allowed_ranges=(允许选区("p1", 1, 7),))
|
|
elif bad == "hash":
|
|
r = replace(r, diagnosis_hash="0" * 64)
|
|
elif bad == "text":
|
|
r = replace(r, patches=(replace(r.patches[0], original="伪造"),))
|
|
else:
|
|
r = replace(r, purpose="")
|
|
svc.提出修订(身份, "bad-patch", r)
|
|
assert app.要求正文().列出候选(身份, "diagnosis-ch") == []
|
|
assert app.要求正文().读取正文(身份, "diagnosis-ch") == 原稿
|
|
|
|
|
|
def test_旧诊断不能覆盖作者后来保存的新稿__231003(诊断环境):
|
|
svc, 身份, r, _ = 准备(诊断环境)
|
|
app = 诊断环境[0]
|
|
app.要求正文().保存人工(
|
|
身份, "later", "diagnosis-ch", 1, 正文草稿((段落("p1", (文本节点("作者的新句子。"),)),))
|
|
)
|
|
with pytest.raises(Muse错误):
|
|
svc.提出修订(身份, "stale-patch", r)
|
|
assert app.要求正文().读取正文(身份, "diagnosis-ch")["visible_text"] == "作者的新句子。"
|
|
assert app.要求正文().列出候选(身份, "diagnosis-ch") == []
|
|
|
|
|
|
def test_cli_default_persists_revision__1d9f2c(诊断环境, tmp_path):
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
_, 身份, req, original = 准备(诊断环境)
|
|
app = 诊断环境[0]
|
|
secret = tmp_path / "password"
|
|
secret.write_text("synthetic-only")
|
|
secret.chmod(0o600)
|
|
config = tmp_path / "author.toml"
|
|
config.write_text(
|
|
'["数据库"]\n"取值方式"="受控存储"\n"位置"='
|
|
+ json.dumps(app.要求数据库().引用.位置)
|
|
+ '\n["资源"]\n"发布身份"="test"\n["HTTP"]\n"作者ID"='
|
|
+ json.dumps(身份.作者)
|
|
+ '\n"口令文件"='
|
|
+ json.dumps(str(secret))
|
|
+ "\n"
|
|
)
|
|
path = tmp_path / "revision.json"
|
|
path.write_text(
|
|
json.dumps({"command_id": "cli-revision", "request": asdict(req)}, ensure_ascii=False)
|
|
)
|
|
command = [sys.executable, "-I", "-m", "muse", "审校", str(config), "作者修订", str(path)]
|
|
first = subprocess.run(command, cwd=tmp_path, capture_output=True, text=True, timeout=30)
|
|
assert first.returncode == 0, first.stderr
|
|
second = subprocess.run(command, cwd=tmp_path, capture_output=True, text=True, timeout=30)
|
|
assert second.returncode == 0 and json.loads(first.stdout) == json.loads(second.stdout)
|
|
candidates = app.要求正文().列出候选(身份, "diagnosis-ch")
|
|
assert len(candidates) == 1 and candidates[0]["decision"] == "undecided"
|
|
assert candidates[0]["visible_text"] == "窗外正在下雨。"
|
|
assert app.要求正文().读取正文(身份, "diagnosis-ch") == original
|