737 lines
29 KiB
Python
737 lines
29 KiB
Python
"""声音草案统计不变成认可;规则及持久化观察分别沿实际接缝承接。"""
|
||
|
||
from copy import deepcopy
|
||
|
||
import pytest
|
||
from pydantic import ValidationError
|
||
|
||
from muse.共享.错误 import Muse错误
|
||
|
||
四类例证 = {"sf", "snf", "boundary", "regression"}
|
||
|
||
|
||
def _单规则库(规则库, 规则ID):
|
||
from muse.审校修订.接口 import 核对规则库
|
||
|
||
规则 = 规则库["rules"][规则ID]
|
||
引用 = [例证ID for 类型 in 四类例证 for 例证ID in 规则["samples"][类型]]
|
||
return 核对规则库([规则], [规则库["samples"][例证ID] for 例证ID in 引用])
|
||
|
||
|
||
def _稿(文本):
|
||
from muse.正文写作.接口 import 文本节点, 正文草稿, 段落
|
||
|
||
return 正文草稿((段落("p1", (文本节点(文本),)),))
|
||
|
||
|
||
def _整段替换(原稿, 替换):
|
||
from muse.正文写作.接口 import 段落哈希, 选段修改
|
||
|
||
段 = 原稿.paragraphs[0]
|
||
原文 = 段.content[0].text
|
||
return 选段修改("p1", 段落哈希(段), 0, len(原文), 原文, 替换, "finding-1")
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5efcd58a2596",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="调用新公开的声音草案/统计方法",
|
||
then=[
|
||
"候选状态仍非认可:新proposed且persisted=false替代旧candidate",
|
||
"同64句样张满足1000非空白码点与20句规模门,不代表风格代表性",
|
||
"人物声音保持unknown_fields中的characters",
|
||
"新metrics下句长中位数大于0,统计不推语义",
|
||
],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音草案公开采样规模和未知项__5efcd5():
|
||
from muse.审校修订.接口 import 生成声音草案
|
||
|
||
文本 = "".join(f"第{i}句,门外的雨还在落下,青石路上没有一个人。\n" for i in range(1, 65))
|
||
草案 = 生成声音草案("synthetic:demo", [{"source_ref": "demo.txt", "text": 文本}])
|
||
assert 草案["status"] == "proposed" and 草案["persisted"] is False
|
||
assert 草案["sampling"]["sample_sufficient"] is True
|
||
assert "characters" in 草案["unknown_fields"]
|
||
assert 草案["metrics"]["sentence_length"]["median"] > 0
|
||
for 候选 in 草案["example_candidates"]:
|
||
assert 文本[候选["start"] : 候选["end"]] == 候选["quote"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-f1f21ad8eece",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="调用新公开的声音草案/统计方法",
|
||
then=["错误source_sha256拒绝;通过新审校错误而不是旧ValueError呈现同含义"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音草案拒绝来源哈希漂移__f1f21a():
|
||
from muse.审校修订.接口 import 生成声音草案
|
||
|
||
with pytest.raises(Muse错误, match="source_sha256"):
|
||
生成声音草案(
|
||
"synthetic:demo",
|
||
[{"source_ref": "demo.txt", "source_sha256": "0" * 64, "text": "甲走进门。"}],
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3bba023da2a5",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="调用新公开的声音草案/统计方法",
|
||
then=["相同甲/乙/雨三句重复计算完全相同,小样本不冒充分规模"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音画像保持确定性__3bba02():
|
||
from muse.审校修订.接口 import 统计声音样本
|
||
|
||
文本 = "甲走进门。乙关上窗。雨落下来。"
|
||
assert 统计声音样本(文本) == 统计声音样本(文本)
|
||
assert 统计声音样本(文本)["sample_sufficient"] is False
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22b001",
|
||
environment="默认离线",
|
||
given="空样张、缺来源或不一致哈希",
|
||
when="调用声音统计/预览",
|
||
then=["拒绝不完整输入"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
@pytest.mark.parametrize(
|
||
"输入",
|
||
[
|
||
[],
|
||
[{"source_ref": "a", "text": " "}],
|
||
[{"text": "正文"}],
|
||
[{"source_ref": "a", "text": "正文", "source_sha256": "bad-hash"}],
|
||
],
|
||
)
|
||
def test_声音预览拒空样张和含糊哈希__22b001(输入):
|
||
from muse.审校修订.接口 import 生成声音草案
|
||
|
||
with pytest.raises(Muse错误):
|
||
生成声音草案("synthetic:demo", 输入)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22b002",
|
||
environment="默认离线",
|
||
given="重复来源、长串未闭引号及嵌套引号",
|
||
when="调用声音统计/预览",
|
||
then=["重复来源不扩大规模;引号率按完整非重叠片段计算,不作语义识别"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_重复来源不增加样本且引号统计不冒语义识别__22b002():
|
||
from muse.审校修订.接口 import 生成声音草案, 统计声音样本
|
||
|
||
条 = {"source_ref": "same-source", "text": "同一份样张。"}
|
||
with pytest.raises(Muse错误, match="重复"):
|
||
生成声音草案("synthetic:demo", [条, 条])
|
||
原文 = "「" * 10000 + "未闭合的外引号“甲”“乙”"
|
||
assert 统计声音样本(原文)["quoted_span_ratio"] == round(6 / len(原文), 4)
|
||
嵌套 = "「引用“文字”仍属同一个外层片段」"
|
||
assert 统计声音样本(嵌套)["quoted_span_ratio"] == 1.0
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3accffa197cb",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置规则候选逐条具备四类反向可查例证__3accff():
|
||
from muse.审校修订.接口 import 核对规则启用, 读取内置规则
|
||
|
||
规则库 = 读取内置规则()
|
||
assert len(规则库["rules"]) >= 10
|
||
for 规则 in 规则库["rules"].values():
|
||
assert 规则["status"] == "candidate"
|
||
assert set(规则["samples"]) == 四类例证
|
||
for 类型 in 四类例证:
|
||
assert 规则["samples"][类型]
|
||
for 例证ID in 规则["samples"][类型]:
|
||
例证 = 规则库["samples"][例证ID]
|
||
assert 例证["type"] == 类型
|
||
assert 规则["id"] in 例证["rules"]
|
||
with pytest.raises(Muse错误, match="B10逐例评测和正式批准"):
|
||
核对规则启用(next(iter(规则库["rules"].values())), 规则库["samples"], {"pass": True})
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-de273edf8d1a",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则缺回归例证时拒绝候选合同__de273e():
|
||
from muse.审校修订.接口 import 核对规则, 读取内置规则
|
||
|
||
规则库 = 读取内置规则()
|
||
损坏 = deepcopy(规则库["rules"]["l002"])
|
||
损坏["samples"]["regression"] = []
|
||
with pytest.raises(Muse错误, match="四类例证"):
|
||
核对规则(损坏, 规则库["samples"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-33e0bde5937f",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则引用不存在例证时拒绝候选合同__33e0bd():
|
||
from muse.审校修订.接口 import 核对规则, 读取内置规则
|
||
|
||
规则库 = 读取内置规则()
|
||
损坏 = deepcopy(规则库["rules"]["l002"])
|
||
损坏["samples"]["regression"] = ["不存在的样例id"]
|
||
with pytest.raises(Muse错误, match="四类例证"):
|
||
核对规则(损坏, 规则库["samples"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-680ac77e24a7",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_叙述规则屏蔽对白且命中仍只询问__680ac7():
|
||
from muse.审校修订.接口 import 诊断文本, 读取内置规则
|
||
|
||
文本 = "「值得注意的是,这里是对白。」值得注意的是,这里是叙述。"
|
||
规则库 = _单规则库(读取内置规则(), "l002")
|
||
诊断 = 诊断文本(文本, work_id="synthetic:demo", 规则库=规则库, 预览候选=True)
|
||
assert len(诊断["findings"]) == 1
|
||
发现 = 诊断["findings"][0]
|
||
assert 发现["quote"] == "值得注意的是"
|
||
assert 发现["start"] == 文本.rindex("值得注意的是")
|
||
assert 发现["carrier"] == "narration"
|
||
assert 发现["decision_proposal"] == "ask"
|
||
from muse.审校修订.机器味诊断 import 定位载体, 载体区间
|
||
|
||
assert 定位载体(文本, 1, 7, 载体区间(文本)) == "dialogue"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-dbe2980ca479",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_情态与新增时间数字不能通过保真门__dbe298():
|
||
from muse.审校修订.接口 import 核对修订保真
|
||
from muse.正文写作.接口 import 应用选段
|
||
|
||
用例 = [
|
||
("多半背景不凡", "背景都不凡", "推测情态"),
|
||
("谁也不知道", "那年秋分的夜里,谁也不知道", "新的时间"),
|
||
("谁也不知道", "2026年3天后的夜里,谁也不知道", "新的时间"),
|
||
]
|
||
for 原文, 替换, 错误片段 in 用例:
|
||
原稿 = _稿(原文)
|
||
修改 = _整段替换(原稿, 替换)
|
||
结果 = 核对修订保真(原稿, 应用选段(原稿, (修改,)), (修改,))
|
||
assert 结果["mechanical_pass"] is False
|
||
assert any(错误片段 in 错误 for 错误 in 结果["errors"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-ccc9845fd27d",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_无确认声音不能变成文学通过__ccc984():
|
||
from muse.审校修订.接口 import 生成审校分片, 组装文学报告
|
||
|
||
文本 = "甲走进门。乙关上窗。"
|
||
片 = 生成审校分片(文本, "a" * 64)
|
||
结果 = [
|
||
{
|
||
"shard_id": 当前.片ID,
|
||
"dimensions": {
|
||
"continuity": {"status": "reviewed", "summary": "无冲突。", "findings": []},
|
||
"craft": {"status": "reviewed", "summary": "已审。", "findings": []},
|
||
"voice": {"status": "uncalibrated", "summary": "没有已确认声音。", "findings": []},
|
||
},
|
||
}
|
||
for 当前 in 片
|
||
]
|
||
报告 = 组装文学报告(文本, "a" * 64, 片, 结果, 允许依据=(), 已确认声音=False)
|
||
assert 报告["dimension_status"]["voice"] == "uncalibrated"
|
||
assert 报告["model_verified"] is False and 报告["persisted"] is False
|
||
assert "pass" not in 报告
|
||
with pytest.raises(Muse错误):
|
||
组装文学报告(文本, "a" * 64, 片, 结果, 允许依据=(), 已确认声音={"status": "candidate"})
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-43819837fcfe",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_无声音账本时内置规则四类例证完整且为候选__438198():
|
||
from muse.审校修订.接口 import 读取内置规则
|
||
|
||
规则库 = 读取内置规则()
|
||
assert len(规则库["rules"]) >= 10
|
||
assert {s["type"] for s in 规则库["samples"].values()} >= {
|
||
"sf",
|
||
"snf",
|
||
"boundary",
|
||
"regression",
|
||
}
|
||
assert all(r["status"] == "candidate" for r in 规则库["rules"].values())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-f104cf5836ec",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音提案版本字段值边界拒绝__f104cf():
|
||
from muse.审校修订.接口 import 声音提案请求, 声音样张引用
|
||
|
||
样张 = 声音样张引用("document", "chapter-1", 1, "a" * 64, "main")
|
||
with pytest.raises(Muse错误, match="声音候选需要"):
|
||
声音提案请求(
|
||
work_id="synthetic:demo",
|
||
expected_voice_revision=-1,
|
||
schema_id="voice",
|
||
base_version=1,
|
||
narrator={"叙述": "冷峻"},
|
||
samples=(样张,),
|
||
)
|
||
with pytest.raises(Muse错误, match="声音候选需要"):
|
||
声音提案请求(
|
||
work_id="synthetic:demo",
|
||
expected_voice_revision=0,
|
||
schema_id="voice",
|
||
base_version=0,
|
||
narrator={"叙述": "冷峻"},
|
||
samples=(样张,),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-b4afca23803b",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音结构版本拒绝非整数__b4afca():
|
||
from muse.审校修订.接口 import 声音提案请求, 声音样张引用
|
||
|
||
样张 = 声音样张引用("document", "chapter-1", 1, "a" * 64, "main")
|
||
with pytest.raises(ValidationError) as 错误:
|
||
声音提案请求(
|
||
work_id="synthetic:demo",
|
||
expected_voice_revision=0,
|
||
schema_id="voice",
|
||
base_version="nope",
|
||
narrator={"叙述": "冷峻"},
|
||
samples=(样张,),
|
||
)
|
||
assert "base_version" in str(错误.value)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1fe56c6532f3",
|
||
environment="离线新版声音/规则公开函数与原合成输入;不认证生产启用或文学效果",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音提案缺叙述者内容时拒绝__1fe56c():
|
||
from muse.审校修订.接口 import 声音提案请求, 声音样张引用
|
||
|
||
样张 = 声音样张引用("document", "chapter-1", 1, "a" * 64, "main")
|
||
with pytest.raises(Muse错误, match="叙述者内容"):
|
||
声音提案请求("synthetic:demo", 0, "voice", 1, {}, (样张,))
|
||
|
||
|
||
def _样张():
|
||
from muse.审校修订.接口 import 声音样张引用
|
||
|
||
return 声音样张引用("document", "chapter-1", 1, "a" * 64, "main")
|
||
|
||
|
||
def _案例(case_id, 哈希):
|
||
from muse.审校修订.接口 import 案例定位
|
||
|
||
return 案例定位(case_id, 1, 哈希)
|
||
|
||
|
||
def _确定性规则():
|
||
from muse.审校修订.接口 import 读取内置规则
|
||
|
||
return [
|
||
规则
|
||
for 规则 in 读取内置规则()["rules"].values()
|
||
if 规则["trigger"]["type"] != "model_judgment"
|
||
]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-49204fa147de",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_确定性触发规则可回放四类合同__49204f():
|
||
from muse.审校修订.接口 import 核对规则, 读取内置规则
|
||
|
||
库 = 读取内置规则()
|
||
确定性 = _确定性规则()
|
||
assert len(确定性) >= 2
|
||
assert {规则["trigger"]["type"] for 规则 in 确定性} <= {"regex", "handler", "density"}
|
||
for 规则 in 确定性:
|
||
引用 = [
|
||
例证ID
|
||
for 类型 in ("sf", "snf", "boundary", "regression")
|
||
for 例证ID in 规则["samples"][类型]
|
||
]
|
||
核对规则(规则, {例证ID: 库["samples"][例证ID] for 例证ID in 引用})
|
||
assert 规则["status"] == "candidate"
|
||
assert 规则["default_disposition"] in {"advisory", "blocking", "candidate"}
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-2b1f41398f73",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_未提供样式时上下文不含声音节__2b1f41():
|
||
from muse.上下文.声音材料 import 冻结声音身份, 校验声音节
|
||
|
||
# 无已确认声音=合法空注入:B09 不把任何声音节放入上下文,writer 输入不含样式约束。
|
||
assert 校验声音节(None) is None
|
||
assert 冻结声音身份(None) is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-0ef41b9bcc12",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_确认声音约束冻结且投影只给写手__0ef41b():
|
||
import uuid
|
||
|
||
from muse.上下文.声音材料 import 冻结声音身份, 校验声音节, 身份键
|
||
|
||
版本 = str(uuid.uuid4())
|
||
材料 = {
|
||
"version_id": 版本,
|
||
"revision": 1,
|
||
"content_hash": "a" * 64,
|
||
"schema_hash": "b" * 64,
|
||
"projection_version": "c",
|
||
"材料键": "声音:" + 版本,
|
||
"材料哈希": "d" * 64,
|
||
}
|
||
校验声音节(材料) # 冻结身份完整时可用
|
||
assert 冻结声音身份(材料) == {k: 材料[k] for k in 身份键}
|
||
for 缺 in ("材料哈希", "version_id", "revision", "schema_hash"):
|
||
损坏 = {k: v for k, v in 材料.items() if k != 缺}
|
||
with pytest.raises(Muse错误):
|
||
校验声音节(损坏)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-0b66c8424583",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_空白样式约束被丢弃__0b66c8():
|
||
from muse.审校修订.接口 import 声音提案请求
|
||
|
||
for 关键字 in ({"verbal_tics": (" ",)}, {"protected_expressions": ("\n",)}):
|
||
with pytest.raises(Muse错误, match="空白"):
|
||
声音提案请求("synthetic:demo", 0, "voice", 1, {"叙事": "冷峻"}, (_样张(),), **关键字)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-e83f562f0ff7",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音来源依据不混入写手输入__e83f56():
|
||
from pydantic import ValidationError
|
||
|
||
from muse.上下文.声音材料 import 身份键
|
||
|
||
# 写手只看到声音内容三键;来源/版本身份只留在冻结节,不进入内容投影。
|
||
内容键 = {"narrator", "verbal_tics", "protected_expressions"}
|
||
assert not (set(身份键) & 内容键)
|
||
from muse.审校修订.接口 import 声音提案请求
|
||
|
||
# 旧“角色→口痒归属”字段不再成为写手内容的第二通道。
|
||
with pytest.raises(ValidationError):
|
||
声音提案请求(
|
||
"synthetic:demo",
|
||
0,
|
||
"voice",
|
||
1,
|
||
{"叙事": "冷峻"},
|
||
(_样张(),),
|
||
characters={"甲": ["嗯"]},
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c4cde790f809",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_声音草案产物可直接进入确认定位__c4cde7():
|
||
from uuid import uuid4
|
||
|
||
from muse.审校修订.接口 import 声音定位, 声音提案请求
|
||
|
||
# draft(候选请求)→ confirm(审阅定位)共享作品与基线;提案身份可被审阅定位引用。
|
||
请求 = 声音提案请求("synthetic:demo", 0, "voice", 1, {"句式画像": "短句叙述"}, (_样张(),))
|
||
定位 = 声音定位("synthetic:demo", str(uuid4()), 1, "a" * 64, 0)
|
||
assert 定位.work_id == 请求.work_id
|
||
assert 定位.expected_voice_revision == 请求.expected_voice_revision
|
||
with pytest.raises(Muse错误):
|
||
声音定位("synthetic:demo", "not-uuid", 1, "a" * 64, 0)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-f1f4ced3f7e6",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_完整声音候选经合同通过可提供确认__f1f4ce():
|
||
from muse.审校修订.接口 import 声音提案请求
|
||
|
||
# grounding 输入齐全(叙述者、样张、口癖与保护均有字段)时合同不误拒。
|
||
请求 = 声音提案请求(
|
||
"synthetic:demo",
|
||
0,
|
||
"voice",
|
||
1,
|
||
{"句式画像": "短句叙述", "达标样张": "甲走进门。"},
|
||
(_样张(),),
|
||
verbal_tics=("没有再说话",),
|
||
protected_expressions=("雨落下来。",),
|
||
)
|
||
assert 请求.narrator["达标样张"] and 请求.verbal_tics and 请求.protected_expressions
|
||
assert len(请求.samples) == 1
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-6111a82b27ea",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则草案拒绝不足四类或单边__6111a8():
|
||
from muse.审校修订.接口 import 案例规则请求
|
||
|
||
引用 = (
|
||
_案例("00000000-0000-4000-8000-000000000001", "a" * 64),
|
||
_案例("00000000-0000-4000-8000-000000000002", "b" * 64),
|
||
)
|
||
for 数量 in range(0, 4):
|
||
with pytest.raises(Muse错误, match="4至64"):
|
||
案例规则请求({"id": "candidate-x"}, 引用[:数量])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-6b7751add3c6",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_额外样例只从已确认案例投影__6b7751():
|
||
from muse.审校修订.接口 import 案例规则请求
|
||
|
||
引用 = tuple(_案例(f"00000000-0000-4000-8000-{i:012d}", chr(97 + i) * 64) for i in range(4))
|
||
for 字段 in ("samples", "status", "case_card_ids", "evidence"):
|
||
with pytest.raises(Muse错误, match="样例、状态和依据由服务生成"):
|
||
案例规则请求({"id": "candidate-x", 字段: {}}, 引用)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-cb044029fc57",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_已确认声音内容作为写手唯一通道__cb0440():
|
||
from pydantic import ValidationError
|
||
|
||
from muse.审校修订.接口 import 声音提案请求
|
||
|
||
# 声音内容三键是写手 guidance 的唯一通道;角色级“同时归属”输入被合同拒绝。
|
||
with pytest.raises(ValidationError):
|
||
声音提案请求(
|
||
"synthetic:demo",
|
||
0,
|
||
"voice",
|
||
1,
|
||
{"叙事": "冷峻"},
|
||
(_样张(),),
|
||
角色归属={"甲": ["嗯"]},
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-0587dbd22c64",
|
||
environment="离线纯规则",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_规则身份重复被公开合同拒绝__0587db():
|
||
from muse.审校修订.接口 import 核对规则库, 读取内置规则
|
||
|
||
库 = 读取内置规则()
|
||
规则 = list(库["rules"].values())
|
||
with pytest.raises(Muse错误, match="重复"):
|
||
核对规则库([*规则, 规则[0]], list(库["samples"].values()))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c45d9e768e76",
|
||
environment="离线纯规则",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置规则与所有四类例证经同一合同校验__c45d9e():
|
||
from muse.审校修订.接口 import 核对规则库, 读取内置规则
|
||
|
||
库 = 读取内置规则()
|
||
重验 = 核对规则库(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="离线纯规则",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_内置发布规则全部仅为候选__02fcb3():
|
||
from muse.审校修订.接口 import 读取内置规则
|
||
|
||
库 = 读取内置规则()
|
||
assert 库["rules"] and all(rule["status"] == "candidate" for rule in 库["rules"].values())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-9efb708e26f6",
|
||
environment="离线纯合同",
|
||
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-477b4a572369",
|
||
environment="离线纯合同",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI离线是诊断和修订回放的显式逃生口__477b4a(tmp_path):
|
||
import json
|
||
|
||
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-10cdc16e5ad7",
|
||
environment="离线纯合同",
|
||
when="持久化、重验或查询诊断、声音、规则与案例。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_离线诊断不写数据库且明示未持久化__10cdc1(tmp_path):
|
||
import json
|
||
|
||
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-5cd6b80a3e5e",
|
||
environment="离线,固定样张、规则和四类反例",
|
||
when="生成声音草案、投影保护项或验证规则候选。",
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_CLI离线诊断不触数据库且明示未持久化__5cd6b8(tmp_path):
|
||
import json
|
||
|
||
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(
|
||
"NC-w26-26d103",
|
||
environment="离线纯合同",
|
||
given="固定请求、来源与作者;按用例环境准备独立测试输入",
|
||
when="通过实际公开入口执行并核对拒绝、状态及持久结果",
|
||
then=["旧方法改进请求固定字节不增加规则默认字段"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察",
|
||
)
|
||
def test_旧方法改进请求固定字节不增加规则默认字段__26d103():
|
||
from dataclasses import asdict
|
||
|
||
from muse.作者经验.接口 import 改进请求
|
||
from muse.正式变更.接口 import 固定哈希
|
||
|
||
request = 改进请求(
|
||
"11111111-1111-1111-1111-111111111111",
|
||
0,
|
||
"旧方法标题",
|
||
"旧方法理由",
|
||
"22222222-2222-2222-2222-222222222222",
|
||
("33333333-3333-3333-3333-333333333333",),
|
||
)
|
||
assert set(asdict(request)) == {
|
||
"improvement_id",
|
||
"expected_revision",
|
||
"title",
|
||
"rationale",
|
||
"method_version_id",
|
||
"feedback_ids",
|
||
}
|
||
assert (
|
||
固定哈希(asdict(request))
|
||
== "8c3b0f01e086c99906c9a5e239ea82c26a9b612badb05c234a2465f7c645d955"
|
||
)
|