实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
1513 lines
64 KiB
Python
1513 lines
64 KiB
Python
"""声音从实际样张到明确确认;报告和案例沿各自公共入口验证。"""
|
||
|
||
import hashlib
|
||
import itertools
|
||
import json
|
||
from copy import deepcopy
|
||
from dataclasses import replace
|
||
|
||
import pytest
|
||
from test_两阶段写手 import 生成环境 as _生成环境夹具
|
||
from test_完整文学审校 import 发起 as 发起全文审校
|
||
from test_完整文学审校 import 文学环境 as _文学环境夹具
|
||
from test_完整文学审校 import 片结果 as 合成片结果
|
||
from test_完整文学审校 import 跑 as 跑全文审校
|
||
|
||
from muse.作品规划.接口 import 档案保存
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.审校修订.接口 import (
|
||
声音定位,
|
||
声音提案请求,
|
||
声音样张引用,
|
||
审校错误,
|
||
核对规则库,
|
||
案例复核请求,
|
||
案例定位,
|
||
案例来源,
|
||
案例规则请求,
|
||
案例采集请求,
|
||
读取内置规则,
|
||
)
|
||
from muse.正式变更.接口 import 固定哈希
|
||
from muse.正文写作.接口 import 文本节点, 正文草稿, 段落
|
||
from muse.资料研究.接口 import 导入请求
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
|
||
|
||
@pytest.fixture
|
||
def 全文生成环境(内置结构测试库, tmp_path):
|
||
# 生成环境正文直接建档案结构,必须复用已导入内置结构的种子库。
|
||
yield from _生成环境夹具.__wrapped__(内置结构测试库, tmp_path)
|
||
|
||
|
||
@pytest.fixture
|
||
def 全文报告环境(全文生成环境):
|
||
return _文学环境夹具.__wrapped__(全文生成环境)
|
||
|
||
|
||
@pytest.fixture
|
||
def 声音环境(章后环境):
|
||
env = 章后环境
|
||
装配, 身份 = env["装配"], env["作者"]
|
||
作品 = 装配.要求作品()
|
||
结构 = 作品.新档案结构(身份, "voice-work", "work_core", 1)
|
||
作品.保存档案(
|
||
身份, "voice-work-create", 档案保存("voice-work", 0, {"名称": "合成声音作品"}, 结构)
|
||
)
|
||
作品.添加章节(身份, "voice-ch-create", "voice-work", "voice-ch", "样张", 预期目录版本=0)
|
||
装配.要求正文().保存人工(
|
||
身份,
|
||
"voice-text",
|
||
"voice-ch",
|
||
0,
|
||
正文草稿((段落("v1", (文本节点("甲走进门。雨落下来。他没有再说话。"),)),)),
|
||
)
|
||
文 = 装配.要求正文().读取正文(身份, "voice-ch")
|
||
请求 = 声音提案请求(
|
||
"voice-work",
|
||
0,
|
||
"style",
|
||
1,
|
||
{"句式画像": "短句叙述", "达标样张": "甲走进门。"},
|
||
(声音样张引用("document", "voice-ch", 文["revision"], 文["document_hash"], "main"),),
|
||
verbal_tics=("没有再说话",),
|
||
protected_expressions=("雨落下来。",),
|
||
)
|
||
return 装配, 身份, 请求, 文
|
||
|
||
|
||
def _提出(服务, 身份, 命令, 请求):
|
||
回执 = 服务.提出声音(身份, 命令, 请求)
|
||
r = 回执["results"][0]
|
||
loc = 声音定位(
|
||
请求.work_id, r["proposal_id"], 1, r["proposal_hash"], 请求.expected_voice_revision
|
||
)
|
||
return 回执, loc
|
||
|
||
|
||
def _确认(服务, 身份, 命令, loc):
|
||
审阅 = 服务.打开声音审阅(身份, loc)
|
||
return 服务.决定声音(身份, 命令, loc, 审阅ID=审阅["review_id"], 审阅哈希=审阅["review_hash"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22c001",
|
||
environment="隔离PG与合成样张",
|
||
given="本书真实正文样张、声音未决提案和有效/无效审阅",
|
||
when="经真实S04/S01/B05/B03公共用例保存和确认声音",
|
||
then=["候选无当前投影;真实S01确认后才可读取声音,原文不变"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音候选不当确认且真实审阅后才有当前投影__22c001(声音环境):
|
||
装配, 身份, 请求, 文 = 声音环境
|
||
服务 = 装配.要求审校()
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
回执, loc = _提出(服务, 身份, "voice-proposal", 请求)
|
||
assert 回执 == 服务.提出声音(身份, "voice-proposal", 请求)
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
for 假审阅 in ("missing", "", "00000000-0000-4000-8000-000000000000"):
|
||
with pytest.raises(Muse错误):
|
||
服务.决定声音(身份, "bad-review", loc, 审阅ID=假审阅, 审阅哈希="0" * 64)
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
结果 = _确认(服务, 身份, "voice-confirm", loc)
|
||
assert 结果["action"] == "adopt" and 结果["results"][0]["revision"] == 1
|
||
声音 = 服务.当前声音(身份, 请求.work_id, 字段用途="aiContext")
|
||
assert 声音["narrator"] == 请求.narrator
|
||
assert 声音["protected_expressions"] == ["雨落下来。"]
|
||
assert 声音["revision"] == 1
|
||
assert 装配.要求正文().读取正文(身份, "voice-ch") == 文
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22c002",
|
||
environment="隔离PG与合成样张",
|
||
given="无依据口癖/保护/样句、错哈希、第二人物声音或缺叙述者",
|
||
when="经真实S04/S01/B05/B03公共用例保存和确认声音",
|
||
then=["拒绝不完整/未grounding提案,不生成声音版本"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
@pytest.mark.parametrize("坏处", ["口癖", "保护", "样句", "哈希", "人物资产", "叙述者"])
|
||
def test_声音缺依据或重复人物归属不能创建提案__22c002(声音环境, 坏处):
|
||
from pydantic import ValidationError
|
||
|
||
装配, 身份, 请求, _ = 声音环境
|
||
with pytest.raises((Muse错误, ValidationError)):
|
||
if 坏处 == "口癖":
|
||
请求 = replace(请求, verbal_tics=("样张中不存在",))
|
||
elif 坏处 == "保护":
|
||
请求 = replace(请求, protected_expressions=("虚构保护句",))
|
||
elif 坏处 == "样句":
|
||
请求 = replace(请求, narrator={"达标样张": "虚构的达标样张"})
|
||
elif 坏处 == "哈希":
|
||
请求 = replace(请求, samples=(replace(请求.samples[0], source_hash="0" * 64),))
|
||
elif 坏处 == "人物资产":
|
||
声音提案请求(
|
||
work_id="voice-work",
|
||
expected_voice_revision=0,
|
||
schema_id="style",
|
||
base_version=1,
|
||
narrator={"句式画像": "短句"},
|
||
samples=请求.samples,
|
||
characters={"甲": "再造角色声"},
|
||
)
|
||
else:
|
||
请求 = replace(请求, narrator={})
|
||
装配.要求审校().提出声音(身份, "bad-voice", 请求)
|
||
assert 装配.要求审校().当前声音(身份, "voice-work") is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22c003",
|
||
environment="隔离PG与合成样张",
|
||
given="同一声音基线上的两份已审提案",
|
||
when="经真实S04/S01/B05/B03公共用例保存和确认声音",
|
||
then=["另一声音已确认后拒绝旧提案,不覆盖新当前版本"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音确认不能覆盖已推进的当前版本__22c003(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
服务 = 装配.要求审校()
|
||
_, 一 = _提出(服务, 身份, "voice-a", 请求)
|
||
_, 二 = _提出(服务, 身份, "voice-b", replace(请求, narrator={"句式画像": "长短交替"}))
|
||
旧审阅 = 服务.打开声音审阅(身份, 二)
|
||
_确认(服务, 身份, "adopt-a", 一)
|
||
with pytest.raises(Muse错误, match="基线已变化"):
|
||
服务.决定声音(
|
||
身份, "adopt-b", 二, 审阅ID=旧审阅["review_id"], 审阅哈希=旧审阅["review_hash"]
|
||
)
|
||
assert 服务.当前声音(身份, "voice-work")["narrator"] == 请求.narrator
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22c004",
|
||
environment="隔离PG与合成样张",
|
||
given="作者显式选入的B03资料样张,analysis许可存在/缺失",
|
||
when="经真实S04/S01/B05/B03公共用例保存和确认声音",
|
||
then=["核真实用途与源版本,未许可不出当前声音;合法来源可经明确确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
@pytest.mark.parametrize("授权", [True, False])
|
||
def test_显式资料样张核来源用途再进入声音__22c004(声音环境, 授权):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
with 装配.要求数据库().连接() as 连, 连.transaction():
|
||
源 = 装配.要求资料().导入(
|
||
连,
|
||
身份.作者,
|
||
导入请求(
|
||
"note",
|
||
"作者选择的样张",
|
||
"synthetic://voice-source",
|
||
"甲走进门。雨落下来。他没有再说话。",
|
||
("analysis",) if 授权 else (),
|
||
),
|
||
)
|
||
请求 = replace(
|
||
请求, samples=(声音样张引用("source", 源.source_id, 源.revision, 源.content_hash),)
|
||
)
|
||
if not 授权:
|
||
with pytest.raises(Muse错误):
|
||
装配.要求审校().提出声音(身份, "unlicensed-voice", 请求)
|
||
else:
|
||
_, loc = _提出(装配.要求审校(), 身份, "source-voice", 请求)
|
||
_确认(装配.要求审校(), 身份, "source-voice-adopt", loc)
|
||
assert 装配.要求审校().当前声音(身份, "voice-work")["revision"] == 1
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-a724239fa69b",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音样张引用另一作品正文时基线强制拒绝__a72423(声音环境):
|
||
装配, 身份, 请求, 文 = 声音环境
|
||
作品 = 装配.要求作品()
|
||
结构 = 作品.新档案结构(身份, "voice-work-b", "work_core", 1)
|
||
作品.保存档案(
|
||
身份, "voice-work-b-create", 档案保存("voice-work-b", 0, {"名称": "另一作品"}, 结构)
|
||
)
|
||
作品.添加章节(
|
||
身份, "voice-ch-b-create", "voice-work-b", "voice-ch-b", "别气样张", 预期目录版本=0
|
||
)
|
||
装配.要求正文().保存人工(
|
||
身份,
|
||
"voice-text-b",
|
||
"voice-ch-b",
|
||
0,
|
||
正文草稿((段落("vb-p1", (文本节点("另一作品的正文。"),)),)),
|
||
)
|
||
文B = 装配.要求正文().读取正文(身份, "voice-ch-b")
|
||
错配 = replace(
|
||
请求,
|
||
samples=(
|
||
声音样张引用("document", "voice-ch-b", 文B["revision"], 文B["document_hash"], "main"),
|
||
),
|
||
)
|
||
with pytest.raises(审校错误, match="样张的作品或正文哈希不符"):
|
||
装配.要求审校().提出声音(身份, "voice-mismatch-baseline", 错配)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-21ed6b89ab91",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音预防输入作品不一致同样拒绝__21ed6b(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
作品 = 装配.要求作品()
|
||
结构 = 作品.新档案结构(身份, "voice-work-c", "work_core", 1)
|
||
作品.保存档案(
|
||
身份, "voice-work-c-create", 档案保存("voice-work-c", 0, {"名称": "第三作品"}, 结构)
|
||
)
|
||
作品.添加章节(
|
||
身份, "voice-ch-c-create", "voice-work-c", "voice-ch-c", "预防样张", 预期目录版本=0
|
||
)
|
||
装配.要求正文().保存人工(
|
||
身份,
|
||
"voice-text-c",
|
||
"voice-ch-c",
|
||
0,
|
||
正文草稿((段落("vc-p1", (文本节点("预防输入正文。"),)),)),
|
||
)
|
||
文C = 装配.要求正文().读取正文(身份, "voice-ch-c")
|
||
错配 = replace(
|
||
请求,
|
||
verbal_tics=("预防输入正文",),
|
||
samples=(
|
||
声音样张引用("document", "voice-ch-c", 文C["revision"], 文C["document_hash"], "main"),
|
||
),
|
||
)
|
||
with pytest.raises(审校错误, match="作品或正文哈希"):
|
||
装配.要求审校().提出声音(身份, "voice-mismatch-prevention", 错配)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-ab52c9241b8e",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音提案口癖必须来自真实样张__ab52c9(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
with pytest.raises(Muse错误):
|
||
装配.要求审校().提出声音(
|
||
身份, "voice-tic-mismatch", replace(请求, verbal_tics=("样张中不存在",))
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-30b54339e6dd",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音保护表达必须来自真实样张__30b543(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
with pytest.raises(Muse错误):
|
||
装配.要求审校().提出声音(
|
||
身份, "voice-protect-mismatch", replace(请求, protected_expressions=("虚构保护句",))
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3d99ae0ba955",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音决定需要真实审阅回执__3d99ae(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
服务 = 装配.要求审校()
|
||
回执, loc = _提出(服务, 身份, "voice-real-review", 请求)
|
||
for 假审阅 in ("missing", "", "00000000-0000-4000-8000-000000000000"):
|
||
with pytest.raises(Muse错误):
|
||
服务.决定声音(身份, "voice-fake-review", loc, 审阅ID=假审阅, 审阅哈希="0" * 64)
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-8b9b9e08b9ac",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音提案来源哈希漂移在提出时拒绝__8b9b9e(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
with pytest.raises(Muse错误):
|
||
装配.要求审校().提出声音(
|
||
身份,
|
||
"voice-hash-drift",
|
||
replace(请求, samples=(replace(请求.samples[0], source_hash="0" * 64),)),
|
||
)
|
||
assert 装配.要求审校().当前声音(身份, "voice-work") is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3abe1c197cb2",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音候选不进入当前投影__3abe1c(声音环境):
|
||
装配, 身份, 请求, _ = 声音环境
|
||
服务 = 装配.要求审校()
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
_提出(服务, 身份, "voice-no-projection", 请求)
|
||
assert 服务.当前声音(身份, 请求.work_id) is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-ab359a56afb0",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则候选保持候选且例证跨作品可回查__ab359a(四类案例):
|
||
装配, 身份, 建立 = 四类案例
|
||
服务 = 装配.要求审校()
|
||
引用 = 建立()
|
||
结果 = 服务.提出案例规则(身份, 案例规则请求(_规则定义("candidate-ab359a"), 引用))
|
||
assert 结果["status"] == "candidate"
|
||
库 = 服务.读取规则库(身份, 预览候选=True)
|
||
规则 = 库["rules"]["candidate-ab359a"]
|
||
assert 规则["case_card_ids"] == sorted(c.case_id for c in 引用)
|
||
# 四类案例夹具按 work-a/work-b 交替建立;例证作品分组可从已确认案例回查。
|
||
作品集 = {
|
||
服务.读取案例(身份, c.case_id)["payload"]["evidence"]["before"]["work_group"] for c in 引用
|
||
}
|
||
assert 作品集 == {"work-a", "work-b"}
|
||
|
||
|
||
@pytest.fixture
|
||
def 案例环境(章后环境):
|
||
装配, 身份 = 章后环境["装配"], 章后环境["作者"]
|
||
|
||
def 请求(
|
||
*,
|
||
文本="值得注意的是,门外下雨了。",
|
||
来源="synthetic:card-backfill-l002-001",
|
||
用途=("sample",),
|
||
作品="work-a",
|
||
起点=0,
|
||
终点=None,
|
||
保全=True,
|
||
片段哈希=None,
|
||
来源哈希=None,
|
||
):
|
||
with 装配.要求数据库().连接() as 连, 连.transaction():
|
||
源 = 装配.要求资料().导入(
|
||
连,
|
||
身份.作者,
|
||
导入请求("reference", "合成案例来源", 来源, 文本, 用途, 作品),
|
||
)
|
||
end = len(文本) if 终点 is None else 终点
|
||
定位 = 案例来源(
|
||
"source",
|
||
源.source_id,
|
||
源.revision,
|
||
来源哈希 or 源.content_hash,
|
||
起点,
|
||
end,
|
||
片段哈希 or hashlib.sha256(文本[起点:end].encode()).hexdigest(),
|
||
)
|
||
return 案例采集请求(定位, preserve_text=保全), 源, 文本
|
||
|
||
return 装配, 身份, 请求
|
||
|
||
|
||
def _采集(装配, 身份, 命令, 请求):
|
||
结果 = 装配.要求审校().采集案例(身份, 命令, 请求)["results"][0]
|
||
return 案例定位(结果["case_id"], 结果["revision"], 结果["case_hash"])
|
||
|
||
|
||
def _复核(定位, 标签="sf"):
|
||
return 案例复核请求(
|
||
定位,
|
||
标签,
|
||
"retain" if 标签 == "snf" else "repair",
|
||
"作者确认:此处无功能" if 标签 == "sf" else "作者确认案例边界。",
|
||
)
|
||
|
||
|
||
def _确认案例(装配, 身份, 定位, 命令="confirm-case", 标签="sf"):
|
||
服务 = 装配.要求审校()
|
||
请求 = _复核(定位, 标签)
|
||
审阅 = 服务.打开案例审阅(身份, 请求)
|
||
return 服务.决定案例(
|
||
身份,
|
||
命令,
|
||
请求,
|
||
审阅ID=审阅["review_id"],
|
||
审阅哈希=审阅["review_hash"],
|
||
)
|
||
|
||
|
||
def _规则定义(rule_id="candidate-l999", version=1):
|
||
return {
|
||
"id": rule_id,
|
||
"name": "待评估元话语",
|
||
"layer": "lexical",
|
||
"carrier_scope": "narration",
|
||
"trigger": {"type": "model_judgment", "criteria": "上下文功能判断"},
|
||
"default_disposition": "candidate",
|
||
"function_check": ["是否承担叙事功能"],
|
||
"fix_hint": "先仲裁再决定",
|
||
"version": version,
|
||
}
|
||
|
||
|
||
@pytest.fixture
|
||
def 四类案例(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
轮次 = itertools.count()
|
||
|
||
def 建立(*, 标签=("sf", "snf", "boundary", "regression"), 作品=("work-a", "work-b")):
|
||
# 采集与确认命令按调用轮次唯一:同命令重放返回历史回执,其定位在确认后已过期。
|
||
轮 = next(轮次)
|
||
结果 = []
|
||
for i, kind in enumerate(标签):
|
||
文本 = f"第{轮}轮第{i}份例证。前半句作为证据,后半句保留语境。"
|
||
请求, _, _ = 生成(
|
||
文本=文本,
|
||
来源=f"synthetic:rule-source:{轮}-{i}",
|
||
作品=作品[i % len(作品)],
|
||
终点=7,
|
||
)
|
||
定位 = _采集(装配, 身份, f"sample-case-{轮}-{i}", 请求)
|
||
_确认案例(装配, 身份, 定位, f"confirm-{轮}-{i}", kind)
|
||
已存 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
结果.append(案例定位(定位.case_id, 已存["revision"], 已存["case_hash"]))
|
||
return tuple(结果)
|
||
|
||
return 装配, 身份, 建立
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-31eae78cbd8c",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_发布案例改用公开请求合同读回__31eae7(案例环境):
|
||
from pydantic import TypeAdapter
|
||
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
定位 = _采集(装配, 身份, "card-backfill-l002-001", 请求)
|
||
案例 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
重验 = TypeAdapter(案例采集请求).validate_python(案例["payload"]["request"])
|
||
assert 重验 == 请求 and 案例["case_hash"] == 定位.case_hash
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-9b9153c89f65",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_回填先为待复核且明确确认后才可作样例__9b9153(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
定位 = _采集(装配, 身份, "backfill-shadow", 请求)
|
||
服务 = 装配.要求审校()
|
||
assert 服务.读取案例(身份, 定位.case_id)["state"] == "shadow"
|
||
assert not 服务.读取案例(身份, 定位.case_id)["sampleable"]
|
||
_确认案例(装配, 身份, 定位)
|
||
已确认 = 服务.读取案例(身份, 定位.case_id)
|
||
assert 已确认["state"] == "confirmed" and 已确认["sampleable"]
|
||
assert 已确认["payload"]["label"] == "sf"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-07715d77085a",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_未授权来源仅留哈希且不能确认__07715d(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, 文本 = 生成(用途=("analysis",))
|
||
定位 = _采集(装配, 身份, "unlicensed-card", 请求)
|
||
服务 = 装配.要求审校()
|
||
案例 = 服务.读取案例(身份, 定位.case_id)
|
||
证据 = 案例["payload"]["evidence"]["before"]
|
||
assert 证据["excerpt"] is None and 证据["context"] is None
|
||
assert 证据["excerpt_hash"] == hashlib.sha256(文本.encode()).hexdigest()
|
||
with pytest.raises(Muse错误, match="哈希元信息"):
|
||
服务.打开案例审阅(身份, _复核(定位))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3bb0818a8c8d",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_只有哈希的案例请求拒绝偷注原文__3bb081(案例环境):
|
||
from dataclasses import asdict
|
||
|
||
from pydantic import TypeAdapter, ValidationError
|
||
|
||
_, _, 生成 = 案例环境
|
||
请求, _, _ = 生成(用途=("analysis",))
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(案例采集请求).validate_python(
|
||
{**asdict(请求), "excerpt": "偷偷保存的第三方原文"}
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-876da9487f56",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则始终是候选且缺反证不能建立__876da9(四类案例):
|
||
装配, 身份, 建立 = 四类案例
|
||
服务 = 装配.要求审校()
|
||
引用 = 建立()
|
||
结果 = 服务.提出案例规则(身份, 案例规则请求(_规则定义(), 引用))
|
||
assert 结果["status"] == "candidate"
|
||
assert 服务.读取规则库(身份)["rules"] == {}
|
||
单边 = 建立(标签=("sf", "sf", "sf", "sf"))
|
||
with pytest.raises(Muse错误, match="四类"):
|
||
服务.提出案例规则(身份, 案例规则请求(_规则定义("candidate-l998"), 单边))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-aff5d8c29022",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_实时反馈必须同时绑定前后版本和决定回执__aff5d8(案例环境):
|
||
from pydantic import ValidationError
|
||
|
||
_, _, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
with pytest.raises((Muse错误, ValidationError)):
|
||
案例采集请求(请求.before, mode="live_feedback")
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-8246112bdea3",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置发布输入导入后与候选读回指纹一致__824611(章后环境):
|
||
装配, 身份 = 章后环境["装配"], 章后环境["作者"]
|
||
内置 = 读取内置规则()
|
||
装配.要求审校().导入规则候选(身份)
|
||
已存 = 装配.要求审校().读取规则库(身份, 预览候选=True)
|
||
# 正式合同:导入按规则不可变版本持久化其引用的例证;发布输入中未被任何规则引用
|
||
# (反向引用为空)的事故锚点留在安装包发布输入内,不进入规则版本,也不改变指纹。
|
||
引用 = {ref for r in 内置["rules"].values() for refs in r["samples"].values() for ref in refs}
|
||
未引用 = {ref for ref, s in 内置["samples"].items() if not s["rules"]}
|
||
assert 未引用 == set(内置["samples"]) - set(已存["samples"])
|
||
assert 已存["rules"] == 内置["rules"]
|
||
assert 已存["samples"] == {ref: 内置["samples"][ref] for ref in 引用}
|
||
assert 已存["fingerprint"] == 内置["fingerprint"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-179b3c21692c",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则库失败关闭且不回退内置种子__179b3c(章后环境, monkeypatch):
|
||
from psycopg import OperationalError
|
||
|
||
服务 = 章后环境["装配"].要求审校()
|
||
|
||
def 断库(*args, **kwargs):
|
||
raise OperationalError("connection refused")
|
||
|
||
monkeypatch.setattr(type(服务.数据库), "连接", 断库)
|
||
with pytest.raises(OperationalError, match="connection refused"):
|
||
服务.读取规则库(章后环境["作者"], 预览候选=True)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-e88dcd446de0",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则缺少正例时在入库前失败关闭__e88dcd(章后环境):
|
||
库 = deepcopy(读取内置规则())
|
||
库["rules"]["l001"]["samples"]["sf"] = []
|
||
with pytest.raises(Muse错误, match="四类例证"):
|
||
章后环境["装配"].要求审校().导入规则候选(章后环境["作者"], 规则库=库)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-0587dbd22c64",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则身份重复被公开合同拒绝__0587db():
|
||
库 = 读取内置规则()
|
||
规则 = list(库["rules"].values())
|
||
with pytest.raises(Muse错误, match="重复"):
|
||
核对规则库([*规则, 规则[0]], list(库["samples"].values()))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-ac1163ea3657",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则与样例读回还原不可变载荷哈希__ac1163(章后环境):
|
||
装配, 身份 = 章后环境["装配"], 章后环境["作者"]
|
||
服务 = 装配.要求审校()
|
||
服务.导入规则候选(身份)
|
||
库 = 服务.读取规则库(身份, 预览候选=True)
|
||
for rule_id, rule in 库["rules"].items():
|
||
版本 = 服务.读取规则版本(身份, rule_id, rule["version"])
|
||
assert 版本["payload"]["rule"] == rule
|
||
assert 版本["payload_hash"] == 固定哈希(版本["payload"])
|
||
assert set(版本["payload"]["samples"]) == {
|
||
ref for refs in rule["samples"].values() for ref in refs
|
||
}
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-64a9f29d1b89",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_真实隔离库候选读回不需要旧烟雾开关__64a9f2(章后环境):
|
||
服务, 身份 = 章后环境["装配"].要求审校(), 章后环境["作者"]
|
||
导入 = 服务.导入规则候选(身份)
|
||
读回 = 服务.读取规则库(身份, 预览候选=True)
|
||
assert 导入["persisted"] and len(读回["rules"]) == len(导入["results"])
|
||
assert all(rule["status"] == "candidate" for rule in 读回["rules"].values())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-a0fd532aef44",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_首次发布输入为每条规则建立独立版本__a0fd53(章后环境):
|
||
服务, 身份 = 章后环境["装配"].要求审校(), 章后环境["作者"]
|
||
结果 = 服务.导入规则候选(身份)
|
||
assert 结果["persisted"] and all(not r["duplicate"] for r in 结果["results"])
|
||
assert len({r["version_id"] for r in 结果["results"]}) == len(读取内置规则()["rules"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5ddea6a3d165",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_同内容第二次导入幂等且不增加版本__5ddea6(章后环境):
|
||
服务, 身份 = 章后环境["装配"].要求审校(), 章后环境["作者"]
|
||
首次 = 服务.导入规则候选(身份)
|
||
再次 = 服务.导入规则候选(身份)
|
||
assert all(r["duplicate"] for r in 再次["results"])
|
||
assert [r["version_id"] for r in 再次["results"]] == [r["version_id"] for r in 首次["results"]]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-ab996bb220ec",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_变更规则只能追加递增候选版本__ab996b(章后环境):
|
||
服务, 身份 = 章后环境["装配"].要求审校(), 章后环境["作者"]
|
||
服务.导入规则候选(身份)
|
||
库 = deepcopy(读取内置规则())
|
||
库["rules"]["l001"]["fix_hint"] = "更新后的修复提示"
|
||
with pytest.raises(Muse错误, match="递增版本"):
|
||
服务.导入规则候选(身份, 规则库=库)
|
||
库["rules"]["l001"]["version"] = 2
|
||
服务.导入规则候选(身份, 规则库=库)
|
||
assert 服务.读取规则版本(身份, "l001", 1)["version"] == 1
|
||
assert 服务.读取规则版本(身份, "l001", 2)["payload"]["rule"]["fix_hint"] == "更新后的修复提示"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-bc74312a5f4f",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_作者额外候选不被种子强制覆盖__bc7431(四类案例):
|
||
装配, 身份, 建立 = 四类案例
|
||
服务 = 装配.要求审校()
|
||
服务.提出案例规则(身份, 案例规则请求(_规则定义("z999"), 建立()))
|
||
服务.导入规则候选(身份)
|
||
读回 = 服务.读取规则库(身份, 预览候选=True)
|
||
assert "z999" in 读回["rules"] and 读回["rules"]["z999"]["status"] == "candidate"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-59c4d824b968",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则预览只返回计划且不触碰数据库__59c4d8(章后环境):
|
||
服务, 身份 = 章后环境["装配"].要求审校(), 章后环境["作者"]
|
||
计划 = 服务.导入规则候选(身份, 预览=True)
|
||
assert 计划 == {
|
||
"persisted": False,
|
||
"fingerprint": 读取内置规则()["fingerprint"],
|
||
"count": len(读取内置规则()["rules"]),
|
||
}
|
||
assert 服务.读取规则库(身份, 预览候选=True)["rules"] == {}
|
||
|
||
|
||
def _实时反馈请求(案例环境):
|
||
装配, 身份, _ = 案例环境
|
||
正文 = 装配.要求正文()
|
||
原文, 候选 = "候选正文", "候选正文。"
|
||
正文.保存人工(
|
||
身份,
|
||
"feedback-before",
|
||
"ch-1",
|
||
0,
|
||
正文草稿((段落("feedback-p1", (文本节点(原文),)),)),
|
||
)
|
||
前 = 正文.读取正文(身份, "ch-1")
|
||
回执 = 正文.保存人工(
|
||
身份,
|
||
"run-7",
|
||
"ch-1",
|
||
1,
|
||
正文草稿((段落("feedback-p1", (文本节点(候选),)),)),
|
||
)
|
||
后 = 正文.读取正文(身份, "ch-1")
|
||
before = 案例来源(
|
||
"document",
|
||
"ch-1",
|
||
1,
|
||
前["document_hash"],
|
||
0,
|
||
len(原文),
|
||
hashlib.sha256(原文.encode()).hexdigest(),
|
||
"main",
|
||
)
|
||
after = 案例来源(
|
||
"document",
|
||
"ch-1",
|
||
2,
|
||
后["document_hash"],
|
||
0,
|
||
len(候选),
|
||
hashlib.sha256(候选.encode()).hexdigest(),
|
||
"main",
|
||
)
|
||
return (
|
||
装配,
|
||
身份,
|
||
案例采集请求(
|
||
before,
|
||
after,
|
||
mode="live_feedback",
|
||
preserve_text=True,
|
||
feedback_command_id=回执["command_id"],
|
||
),
|
||
回执,
|
||
)
|
||
|
||
|
||
def _配置审校CLI(装配, 身份, tmp_path):
|
||
from muse.配置 import 服务配置
|
||
|
||
口令 = tmp_path / "cli-password"
|
||
口令.write_text("synthetic-cli-password")
|
||
口令.chmod(0o600)
|
||
装配.配置 = replace(
|
||
装配.配置,
|
||
HTTP=服务配置(
|
||
str(口令),
|
||
作者ID=身份.作者,
|
||
公开地址="http://testserver",
|
||
允许来源=("http://testserver",),
|
||
),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-b3ddcb88c6f9",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_来源哈希必须由所登记的原文字节决定__b3ddcb(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成(文本="值得注意的是。", 来源哈希="0" * 64)
|
||
with pytest.raises(Muse错误, match="来源哈希"):
|
||
装配.要求审校().采集案例(身份, "wrong-source-hash", 请求)
|
||
assert 装配.要求审校().列出案例(身份) == []
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-7720243828bc",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_多来源清单按稳定身份排序且研究授权只显示哈希__772024(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
for i, 文本 in enumerate(("值得注意的是。", "嘴角微微上扬。")):
|
||
请求, _, _ = 生成(文本=文本, 来源=f"synthetic:inventory:{i}", 用途=("analysis",))
|
||
_采集(装配, 身份, f"inventory-{1 - i}", 请求)
|
||
清单 = 装配.要求审校().列出案例(身份)
|
||
assert len(清单) == len({c["case_id"] for c in 清单}) == 2
|
||
assert [c["case_id"] for c in 清单] == sorted(c["case_id"] for c in 清单)
|
||
assert all(c["state"] == "shadow" for c in 清单)
|
||
assert all(c["payload"]["evidence"]["before"]["excerpt"] is None for c in 清单)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-31b308b1132c",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_研究授权回填可以从同一数据库身份重放__31b308(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
文本 = "第一行。值得注意的是,门外下雨了。\n"
|
||
起点 = 文本.index("值得")
|
||
请求, _, _ = 生成(文本=文本, 用途=("analysis",), 起点=起点, 终点=len(文本) - 1)
|
||
定位 = _采集(装配, 身份, "research-replay", 请求)
|
||
首次 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
再次 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
assert 首次 == 再次 and 首次["state"] == "shadow"
|
||
assert 首次["payload"]["request"]["before"]["start"] == 起点
|
||
assert 首次["payload"]["evidence"]["before"]["excerpt"] is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5f50720bdba1",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_可作样例来源保留确切片段上下文与位置__5f5072(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
文本 = "她抬头。嘴角微微上扬。\n"
|
||
起点, 终点 = 文本.index("嘴角"), 文本.index("。\n") + 1
|
||
请求, _, _ = 生成(文本=文本, 来源="synthetic:owned", 起点=起点, 终点=终点)
|
||
定位 = _采集(装配, 身份, "owned-backfill", 请求)
|
||
证据 = 装配.要求审校().读取案例(身份, 定位.case_id)["payload"]["evidence"]["before"]
|
||
assert 证据["excerpt"] == "嘴角微微上扬。"
|
||
assert "她抬头" in 证据["context"]
|
||
assert 请求.before.start == 起点 and 请求.before.end == 终点
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-81d43ffe316c",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_同一采集命令重放去重且只留一个案例__81d43f(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成(文本="值得注意的是。", 来源="synthetic:same")
|
||
首次 = _采集(装配, 身份, "same-pattern", 请求)
|
||
重放 = _采集(装配, 身份, "same-pattern", 请求)
|
||
assert 重放 == 首次 and len(装配.要求审校().列出案例(身份)) == 1
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1062c87c6b5e",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_反馈案例绑定实际创作决定回执__1062c8(案例环境):
|
||
装配, 身份, 请求, 回执 = _实时反馈请求(案例环境)
|
||
定位 = _采集(装配, 身份, "feedback-case", 请求)
|
||
案例 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
assert 案例["payload"]["request"]["mode"] == "live_feedback"
|
||
assert 案例["payload"]["feedback"] == {
|
||
key: 回执[key] for key in ("receipt_id", "command_id", "action")
|
||
}
|
||
assert 案例["state"] == "shadow"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-2a5b8f6ffec2",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_未授权案例数据库也不存原文且不能确认__2a5b8f(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, 文本 = 生成(文本="值得注意的是。", 来源="synthetic:third-party", 用途=("analysis",))
|
||
定位 = _采集(装配, 身份, "unlicensed-persist", 请求)
|
||
with pytest.raises(Muse错误, match="哈希元信息"):
|
||
_确认案例(装配, 身份, 定位)
|
||
with 装配.要求数据库().连接(只读=True) as 连:
|
||
载荷 = 连.execute(
|
||
"SELECT payload::text FROM muse_quality_case_version WHERE case_id=%s", (定位.case_id,)
|
||
).fetchone()[0]
|
||
assert 文本 not in 载荷
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-055c73ace6f4",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_未变来源每次读回都重验为当前可用__055c73(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
定位 = _采集(装配, 身份, "verified-source", 请求)
|
||
_确认案例(装配, 身份, 定位)
|
||
读回 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
assert 读回["sampleable"] and 读回["unavailable_sources"] == []
|
||
assert 读回 == 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-7464250e5d21",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_来源推进不改写案例但使旧审阅失效__746425(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, 文本 = 生成(来源="synthetic:changed-source")
|
||
定位 = _采集(装配, 身份, "source-before-change", 请求)
|
||
服务 = 装配.要求审校()
|
||
复核 = _复核(定位)
|
||
审阅 = 服务.打开案例审阅(身份, 复核)
|
||
with 装配.要求数据库().连接() as 连, 连.transaction():
|
||
装配.要求资料().导入(
|
||
连,
|
||
身份.作者,
|
||
导入请求(
|
||
"reference",
|
||
"合成案例来源",
|
||
"synthetic:changed-source",
|
||
文本 + "已经改成另一版正文。",
|
||
("sample",),
|
||
"work-a",
|
||
),
|
||
)
|
||
with pytest.raises(Muse错误):
|
||
服务.决定案例(
|
||
身份,
|
||
"stale-source-review",
|
||
复核,
|
||
审阅ID=审阅["review_id"],
|
||
审阅哈希=审阅["review_hash"],
|
||
)
|
||
assert 服务.读取案例(身份, 定位.case_id)["state"] == "shadow"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c83b1ea9504a",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_篡改片段锚点在案例入库前拒绝__c83b1e(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
文本 = "值得注意的是。"
|
||
请求, _, _ = 生成(文本=文本, 起点=1, 片段哈希=hashlib.sha256(文本.encode()).hexdigest())
|
||
with pytest.raises(Muse错误, match="片段或来源哈希"):
|
||
装配.要求审校().采集案例(身份, "tampered-anchor", 请求)
|
||
assert 装配.要求审校().列出案例(身份) == []
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-7774c6a20238",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_来源授权不可用时读回失败关闭__7774c6(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, 来源, _ = 生成(来源="synthetic:unavailable")
|
||
定位 = _采集(装配, 身份, "unavailable-source", 请求)
|
||
with 装配.要求数据库().连接() as 连, 连.transaction():
|
||
连.execute(
|
||
"UPDATE muse_source SET authorized_uses=%s::jsonb WHERE source_id=%s",
|
||
('["analysis"]', 来源.source_id),
|
||
)
|
||
读回 = 装配.要求审校().读取案例(身份, 定位.case_id)
|
||
assert 读回["unavailable_sources"] == ["before"] and not 读回["sampleable"]
|
||
assert 读回["payload"]["evidence"]["before"]["excerpt"] is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1c36821e1438",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_案例清单对同来源多案例各只读回一次__1c3682(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
文本 = "值得注意的是。嘴角微微上扬。"
|
||
请求一, 来源, _ = 生成(文本=文本, 来源="synthetic:shared-source", 终点=9)
|
||
请求二 = 案例采集请求(
|
||
replace(
|
||
请求一.before,
|
||
start=9,
|
||
end=len(文本),
|
||
excerpt_hash=hashlib.sha256(文本[9:].encode()).hexdigest(),
|
||
),
|
||
preserve_text=True,
|
||
)
|
||
for i, 请求 in enumerate((请求一, 请求二)):
|
||
定位 = _采集(装配, 身份, f"shared-source-{i}", 请求)
|
||
_确认案例(装配, 身份, 定位, f"confirm-shared-{i}")
|
||
清单 = 装配.要求审校().列出案例(身份)
|
||
assert len(清单) == 2 and all(c["sampleable"] for c in 清单)
|
||
assert {c["payload"]["request"]["before"]["ref_id"] for c in 清单} == {来源.source_id}
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5a6e2469583a",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_采集回执可以直接固定下一步审阅定位__5a6e24(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成(文本="值得注意的是。", 来源="synthetic:owned-wrapper")
|
||
定位 = _采集(装配, 身份, "receipt-wrapper", 请求)
|
||
# S01 审阅回执结构:候选身份在快照内以候选ID/候选版本/候选哈希固定。
|
||
审阅 = 装配.要求审校().打开案例审阅(身份, _复核(定位))
|
||
快照 = 审阅["snapshot"]
|
||
assert 快照["候选ID"] == 定位.case_id
|
||
assert 快照["候选版本"] == 定位.revision
|
||
assert 快照["候选哈希"] == 定位.case_hash
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-98a3969a2ce3",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_案例确认要求真实审阅回执__98a396(案例环境):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
定位 = _采集(装配, 身份, "verified-review", 请求)
|
||
with pytest.raises(Muse错误):
|
||
装配.要求审校().决定案例(
|
||
身份,
|
||
"fake-case-review",
|
||
_复核(定位),
|
||
审阅ID="00000000-0000-4000-8000-000000000000",
|
||
审阅哈希="0" * 64,
|
||
)
|
||
_确认案例(装配, 身份, 定位, "verified-confirm")
|
||
assert 装配.要求审校().读取案例(身份, 定位.case_id)["state"] == "confirmed"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-f21ddb209c73",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_未知载体字段不能混入案例合同__f21ddb(案例环境):
|
||
from dataclasses import asdict
|
||
|
||
from pydantic import TypeAdapter, ValidationError
|
||
|
||
_, _, 生成 = 案例环境
|
||
请求, _, _ = 生成()
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(案例采集请求).validate_python({**asdict(请求), "carrier": "unknown"})
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-41e8763c05b4",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_案例只在人工复核后投影成规则样例__41e876(四类案例):
|
||
装配, 身份, 建立 = 四类案例
|
||
引用 = 建立()
|
||
服务 = 装配.要求审校()
|
||
服务.提出案例规则(身份, 案例规则请求(_规则定义(), 引用))
|
||
库 = 服务.读取规则库(身份, 预览候选=True)
|
||
for sample in 库["samples"].values():
|
||
assert sample["case_card_id"] in {c.case_id for c in 引用}
|
||
assert sample["source"] == "licensed" and sample["carrier"] == "mixed"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c45d9e768e76",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置规则与所有四类例证经同一合同校验__c45d9e():
|
||
库 = 读取内置规则()
|
||
重验 = 核对规则库(list(库["rules"].values()), list(库["samples"].values()))
|
||
assert 重验 == 库
|
||
assert {sample["type"] for sample in 库["samples"].values()} == {
|
||
"sf",
|
||
"snf",
|
||
"boundary",
|
||
"regression",
|
||
}
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-02fcb3e5524a",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置发布规则全部仅为候选__02fcb3():
|
||
库 = 读取内置规则()
|
||
assert 库["rules"] and all(rule["status"] == "candidate" for rule in 库["rules"].values())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-9efb708e26f6",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_审校CLI默认走数据库而离线需显式指定__9efb70():
|
||
import inspect
|
||
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
assert inspect.signature(运行审校命令).parameters["离线"].default is False
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-b1ebdb636f6f",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI采集后自动进入数据库案例清单__b1ebdb(案例环境, tmp_path):
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
装配, 身份, 生成 = 案例环境
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
请求, _, _ = 生成(来源="synthetic:cli-inventory")
|
||
路径 = tmp_path / "capture.json"
|
||
from dataclasses import asdict
|
||
|
||
路径.write_text(
|
||
json.dumps({"command_id": "cli-capture", "request": asdict(请求)}, ensure_ascii=False)
|
||
)
|
||
回执 = 运行审校命令(装配, "案例采集", str(路径))
|
||
清单 = 运行审校命令(装配, "案例清单", None)
|
||
assert [c["case_id"] for c in 清单] == [回执["results"][0]["case_id"]]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-477b4a572369",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI离线是诊断和修订回放的显式逃生口__477b4a(tmp_path):
|
||
from muse.共享.错误 import 配置错误
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
路径 = tmp_path / "offline.json"
|
||
路径.write_text(json.dumps({"text": "值得注意的是。", "work_id": "work-a"}, ensure_ascii=False))
|
||
with pytest.raises(配置错误, match="离线模式仅用于"):
|
||
运行审校命令(None, "案例清单", str(路径), 离线=True)
|
||
assert 运行审校命令(None, "诊断", str(路径), 离线=True)["persisted"] is False
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-95a9024e5a4c",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI拒绝从文件扫描原文暗中生成案例__95a902(案例环境, tmp_path):
|
||
from muse.共享.错误 import 配置错误
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
装配, 身份, _ = 案例环境
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
路径 = tmp_path / "work.txt"
|
||
路径.write_text("值得注意的是。")
|
||
with pytest.raises(配置错误, match="未知审校动作"):
|
||
运行审校命令(装配, "scan", str(路径))
|
||
assert 装配.要求审校().列出案例(身份) == []
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5bb941f5815a",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI实时反馈默认持久化真实决定回执__5bb941(案例环境, tmp_path):
|
||
from dataclasses import asdict
|
||
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
装配, 身份, 请求, 回执 = _实时反馈请求(案例环境)
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
路径 = tmp_path / "feedback.yaml"
|
||
路径.write_text(
|
||
json.dumps({"command_id": "cli-feedback", "request": asdict(请求)}, ensure_ascii=False)
|
||
)
|
||
定位 = 运行审校命令(装配, "案例采集", str(路径))["results"][0]
|
||
读回 = 运行审校命令(装配, "案例", 定位["case_id"])
|
||
assert 读回["payload"]["feedback"]["command_id"] == 回执["command_id"] == "run-7"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-4b6ea1536967",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_案例重验直接读数据库不生成仓外边车文件__4b6ea1(案例环境, tmp_path):
|
||
装配, 身份, 生成 = 案例环境
|
||
请求, _, _ = 生成(来源="synthetic:no-sidecar")
|
||
定位 = _采集(装配, 身份, "no-sidecar", 请求)
|
||
装配.要求审校().读取案例(身份, 定位.case_id)
|
||
assert not list(tmp_path.glob("*.inventory.json"))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-10cdc16e5ad7",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_离线诊断不写数据库且明示未持久化__10cdc1(tmp_path):
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
路径 = tmp_path / "receipt.json"
|
||
路径.write_text(json.dumps({"text": "值得注意的是。", "work_id": "work-a"}, ensure_ascii=False))
|
||
报告 = 运行审校命令(None, "诊断", str(路径), 离线=True)
|
||
assert 报告["persisted"] is False and 报告["mode"] == "candidate_preview"
|
||
assert 报告["work_id"] == "work-a"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-fc782c51428f",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_案例CLI不暴露确认投影或启用旧命令__fc782c(案例环境, tmp_path):
|
||
from muse.共享.错误 import 配置错误
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
装配, 身份, _ = 案例环境
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
目标 = tmp_path / "request.json"
|
||
目标.write_text("{}")
|
||
for 旧命令 in ("confirm", "project", "activate"):
|
||
with pytest.raises(配置错误, match="未知审校动作"):
|
||
运行审校命令(装配, 旧命令, str(目标))
|
||
|
||
|
||
@pytest.fixture
|
||
def 诊断环境(章后环境):
|
||
装配, 身份 = 章后环境["装配"], 章后环境["作者"]
|
||
文本 = "值得注意的是,窗外正在下雨。"
|
||
装配.要求正文().保存人工(
|
||
身份,
|
||
"diagnosis-source",
|
||
"ch-2",
|
||
0,
|
||
正文草稿((段落("diagnosis-p1", (文本节点(文本),)),)),
|
||
)
|
||
正文 = 装配.要求正文().读取正文(身份, "ch-2")
|
||
目标 = {
|
||
"work_id": "post-work",
|
||
"chapter_id": "ch-2",
|
||
"branch_id": "main",
|
||
"revision": 正文["revision"],
|
||
"document_hash": 正文["document_hash"],
|
||
}
|
||
return 装配, 身份, 目标
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-71e761cc843b",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_诊断正文哈希不匹配时在报告入库前拒绝__71e761(诊断环境):
|
||
装配, 身份, 目标 = 诊断环境
|
||
with pytest.raises(审校错误, match="哈希不符"):
|
||
装配.要求审校().生成诊断(
|
||
身份,
|
||
{
|
||
**目标,
|
||
"document_hash": "0" * 64,
|
||
},
|
||
)
|
||
with 装配.要求数据库().连接(只读=True) as 连:
|
||
assert 连.execute("SELECT count(*) FROM muse_diagnosis").fetchone()[0] == 0
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-4c7dc3fdcabe",
|
||
environment="隔离 PostgreSQL,检测替身",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI默认持久化诊断并可按身份读回__4c7dc3(诊断环境, tmp_path):
|
||
from pydantic import TypeAdapter
|
||
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
from muse.接入.审校操作 import 诊断请求
|
||
|
||
装配, 身份, 目标 = 诊断环境
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
请求 = TypeAdapter(诊断请求).validate_python({"target": 目标, "preview_candidates": True})
|
||
路径 = tmp_path / "diagnosis.json"
|
||
路径.write_text(json.dumps(请求.model_dump(mode="json"), ensure_ascii=False))
|
||
结果 = 运行审校命令(装配, "诊断", str(路径))
|
||
assert 结果["report"]["persisted"] is True
|
||
assert 运行审校命令(装配, "诊断报告", 结果["diagnosis_id"])["report"] == 结果["report"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-873f1d93b7df",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI诊断默认持久化且同一作者可读回__873f1d(诊断环境, tmp_path):
|
||
from pydantic import TypeAdapter
|
||
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
from muse.接入.审校操作 import 诊断请求
|
||
|
||
装配, 身份, 目标 = 诊断环境
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
装配.要求审校().导入规则候选(身份)
|
||
请求 = TypeAdapter(诊断请求).validate_python({"target": 目标, "preview_candidates": True})
|
||
路径 = tmp_path / "diag-cli.json"
|
||
路径.write_text(json.dumps(请求.model_dump(mode="json"), ensure_ascii=False))
|
||
结果 = 运行审校命令(装配, "诊断", str(路径))
|
||
assert 结果["report"]["persisted"] is True
|
||
|
||
读回 = 运行审校命令(装配, "诊断报告", 结果["diagnosis_id"])
|
||
assert 读回["diagnosis_id"] == 结果["diagnosis_id"]
|
||
assert 读回["stale"] is False and 读回["stale_reasons"] == []
|
||
报告 = 读回["report"]
|
||
assert 报告 == 结果["report"]
|
||
assert 报告["target"] == 目标 and 报告["work_id"] == "post-work"
|
||
assert 报告["schema_version"] == "diagnosis-v1"
|
||
assert 报告["mode"] == "candidate_preview" and 报告["persisted"] is True
|
||
assert 报告["text_hash"] == hashlib.sha256("值得注意的是,窗外正在下雨。".encode()).hexdigest()
|
||
assert 报告["coverage"]["text_length"] == len("值得注意的是,窗外正在下雨。")
|
||
规则库 = 读取内置规则()
|
||
assert 报告["rule_library_version"] == 规则库["fingerprint"]
|
||
assert 报告["findings"]
|
||
for 发现 in 报告["findings"]:
|
||
assert 发现["rule_version"] == 规则库["rules"][发现["rule_id"]]["version"]
|
||
旧配置 = 装配.配置
|
||
assert 旧配置.HTTP is not None
|
||
try:
|
||
装配.配置 = replace(旧配置, HTTP=replace(旧配置.HTTP, 作者ID="synthetic-other-author"))
|
||
with pytest.raises(审校错误, match="诊断不存在、不属于当前作者或哈希不符") as 错误:
|
||
运行审校命令(装配, "诊断报告", 结果["diagnosis_id"])
|
||
assert "值得注意的是" not in json.dumps(错误.value.呈现(), ensure_ascii=False)
|
||
assert 结果["diagnosis_id"] not in json.dumps(错误.value.呈现(), ensure_ascii=False)
|
||
finally:
|
||
装配.配置 = 旧配置
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5cd6b80a3e5e",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI离线诊断不触数据库且明示未持久化__5cd6b8(tmp_path):
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
路径 = tmp_path / "offline-diag.json"
|
||
路径.write_text(json.dumps({"text": "值得注意的是。", "work_id": "work-a"}, ensure_ascii=False))
|
||
报告 = 运行审校命令(None, "诊断", str(路径), 离线=True)
|
||
assert 报告["persisted"] is False and 报告["mode"] == "candidate_preview"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-93daa381c03a",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则验证CLI无离线旗标必须显式评测配置__93daa3(章后环境, tmp_path):
|
||
from pydantic import TypeAdapter
|
||
|
||
from muse.共享.错误 import 配置错误
|
||
from muse.接入.cli.经验命令 import 运行经验命令
|
||
from muse.接入.经验操作 import 规则改进验证输入
|
||
|
||
装配, 身份 = 章后环境["装配"], 章后环境["作者"]
|
||
_配置审校CLI(装配, 身份, tmp_path)
|
||
输入 = TypeAdapter(规则改进验证输入).validate_python(
|
||
{
|
||
"command_id": "cli-rule-verify",
|
||
"selection": {
|
||
"improvement_id": "11111111-1111-4111-8111-111111111111",
|
||
"revision": 1,
|
||
"content_hash": "0" * 64,
|
||
},
|
||
"request": {
|
||
"dataset_version_id": "22222222-2222-4222-8222-222222222222",
|
||
"rule_id": "l001",
|
||
"rule_version": 1,
|
||
"rule_hash": "1" * 64,
|
||
},
|
||
}
|
||
)
|
||
目标 = tmp_path / "rule-verify.json"
|
||
目标.write_text(json.dumps(输入.model_dump(mode="json"), ensure_ascii=False), encoding="utf-8")
|
||
with pytest.raises(配置错误, match="评测配置"):
|
||
运行经验命令(装配, "申请规则验证", str(目标))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22c005",
|
||
environment="隔离PG与合成模型回执",
|
||
given="真实逐片审校任务完成回执与隔离 CLI 配置",
|
||
when="任务完成后经服务与统一 CLI 读回同一报告并核对逐片证据",
|
||
then=["全文报告由实际逐片回执持久化并原样读回;审校不改写原文"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_全文报告由实际逐片回执持久化并经CLI原样读回__22c005(全文报告环境, tmp_path):
|
||
from muse.任务运行.接口 import 任务状态
|
||
from muse.接入.cli.审校命令 import 运行审校命令
|
||
|
||
环境 = 全文报告环境
|
||
原正文 = 环境["审校正文"]
|
||
任务ID, 分片 = 发起全文审校(环境)
|
||
环境["剧本"].extend({"类型": "文本", "文本": 合成片结果(片, 有声音=False)} for 片 in 分片)
|
||
assert 跑全文审校(环境, 任务ID).状态 == 任务状态.已完成
|
||
服务读回 = (
|
||
环境["装配"].要求审校().读取文学工作面(环境["审校作者"], 环境["装配"].任务运行, 任务ID)
|
||
)
|
||
_配置审校CLI(环境["装配"], 环境["审校作者"], tmp_path)
|
||
assert 运行审校命令(环境["装配"], "报告", 任务ID) == 服务读回
|
||
assert 服务读回["report"]["persisted"] and 服务读回["report"]["model_verified"]
|
||
assert len(服务读回["report"]["evidence"]) == len(分片)
|
||
assert 环境["正文"].读取正文(环境["审校作者"], "ch-2") == 原正文
|