实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
158 lines
6.7 KiB
Python
158 lines
6.7 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,
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w23-231001",
|
||
environment="隔离PG",
|
||
given="test_真实诊断批准选区仅生成候选且同命令幂等__231001",
|
||
when="真实PG诊断与S01作者明确修订候选",
|
||
then=["test_真实诊断批准选区仅生成候选且同命令幂等__231001"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
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.case_id(
|
||
"NC-w23-231002",
|
||
environment="隔离PG",
|
||
given="test_伪发现和越范围修订不产生候选__231002",
|
||
when="真实PG诊断与S01作者明确修订候选",
|
||
then=["test_伪发现和越范围修订不产生候选__231002"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
@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") == 原稿
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w23-231003",
|
||
environment="隔离PG",
|
||
given="test_旧诊断不能覆盖作者后来保存的新稿__231003",
|
||
when="真实PG诊断与S01作者明确修订候选",
|
||
then=["test_旧诊断不能覆盖作者后来保存的新稿__231003"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
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") == []
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1d9f2c11aa8d",
|
||
environment="隔离PG与实际CLI子进程",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=[
|
||
"替代旧persist替身计数:实际子进程CLI与隔离PG保存一个未决候选,重放回执相同且不重复,正文不变。"
|
||
],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
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
|