- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具) - 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺 - 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口 - 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影) - 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威) - R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
178 lines
7.3 KiB
Python
178 lines
7.3 KiB
Python
"""B07真实版本反馈、作者审阅及撤回;无模型响应或私表代写业务结果。"""
|
||
|
||
from dataclasses import replace
|
||
from uuid import uuid4
|
||
|
||
import psycopg
|
||
import pytest
|
||
import test_正文候选与作者决定 as 正文测试
|
||
|
||
from muse.作者经验.接口 import (
|
||
作者经验服务,
|
||
偏好决定,
|
||
偏好范围,
|
||
偏好请求,
|
||
反馈版本,
|
||
反馈请求,
|
||
读取偏好材料,
|
||
)
|
||
from muse.共享.调用身份 import 用途
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.正文写作.接口 import 读取候选依据
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
候选环境 = 正文测试.候选环境
|
||
|
||
|
||
@pytest.fixture
|
||
def 环境(候选环境):
|
||
body, author, pools = 候选环境
|
||
service = 作者经验服务(pools[用途.生产], body.正式)
|
||
_, candidate, _ = 正文测试.新候选(body, author)
|
||
original = body.读取正文(author, "chapter-candidate")
|
||
with pools[用途.生产].连接(只读=True) as conn:
|
||
updated = 读取候选依据(conn, author.作者, candidate["candidate_id"], 1)
|
||
feedback = 反馈请求(
|
||
"work-candidate",
|
||
反馈版本("document", "chapter-candidate", 1, original["document_hash"]),
|
||
反馈版本("candidate", candidate["candidate_id"], 1, updated["document_hash"]),
|
||
"这一次末段不要解释人物感受。",
|
||
"revise",
|
||
)
|
||
return service, author, pools, feedback
|
||
|
||
|
||
def _请求(**changes):
|
||
return 偏好请求(
|
||
**{
|
||
"preference_id": str(uuid4()),
|
||
"topic": "心理描写",
|
||
"text": "不解释人物感受。",
|
||
"quote": "今后不解释人物感受。",
|
||
"scope": 偏好范围(),
|
||
**changes,
|
||
}
|
||
)
|
||
|
||
|
||
def _定位(result, replacements=()):
|
||
return 偏好决定(
|
||
result["preference_id"], result["revision"], result["content_hash"], replacements
|
||
)
|
||
|
||
|
||
def _消费(pools, author):
|
||
with pools[用途.生产].连接(只读=True) as conn:
|
||
return 读取偏好材料(conn, author.作者, "work-candidate")
|
||
|
||
|
||
def test_反馈保留确切原版本且不能冒充候选采纳__26a001(环境):
|
||
service, author, pools, request = 环境
|
||
saved = service.记录反馈(author, "feedback", request)
|
||
assert service.记录反馈(author, "feedback", request) == saved
|
||
read = service.读取反馈(author, str(saved["feedback_id"]))
|
||
assert "原句「保留」" in read["before_text"]
|
||
assert "改过的首句" in read["after_text"]
|
||
assert read["payload"]["quote"] == request.quote
|
||
with pytest.raises(Muse错误):
|
||
service.记录反馈(author, "feedback", replace(request, quote="改了原话"))
|
||
with pytest.raises(Muse错误):
|
||
service.记录反馈(
|
||
author,
|
||
"bad-hash",
|
||
replace(request, before=replace(request.before, content_hash="0" * 64)),
|
||
)
|
||
with pools[用途.生产].连接(只读=True) as conn:
|
||
assert conn.execute("SELECT count(*) FROM muse_candidate_decision").fetchone()[0] == 0
|
||
assert service.列出偏好(author) == []
|
||
|
||
|
||
def test_推断偏好待作者审阅确认且撤回保留历史__26a002(环境):
|
||
service, author, pools, feedback = 环境
|
||
fid = service.记录反馈(author, "feedback", feedback)["feedback_id"]
|
||
request = _请求(origin="inferred", feedback_id=str(fid), quote=feedback.quote)
|
||
saved = service.保存偏好(author, "infer", request)["results"][0]
|
||
assert saved["state"] == "pending" and _消费(pools, author) == []
|
||
point = _定位(saved)
|
||
review = service.打开偏好审阅(author, point)
|
||
confirmed = service.确认偏好(
|
||
author, "confirm", point, 审阅ID=review["review_id"], 审阅哈希=review["review_hash"]
|
||
)
|
||
active = confirmed["results"][0]
|
||
assert active["state"] == "active" and _消费(pools, author)[0]["text"] == request.text
|
||
service.撤回偏好(author, "revoke", _定位(active))
|
||
assert _消费(pools, author) == []
|
||
assert service.读取偏好(author, request.preference_id, 2)["state"] == "active"
|
||
assert service.读取偏好(author, request.preference_id, 1)["payload"]["quote"] == feedback.quote
|
||
assert (
|
||
service.确认偏好(
|
||
author, "confirm", point, 审阅ID=review["review_id"], 审阅哈希=review["review_hash"]
|
||
)
|
||
== confirmed
|
||
)
|
||
|
||
|
||
def test_显式范围冲突须点名旧版本而非自动覆盖__26a003(环境):
|
||
service, author, pools, _ = 环境
|
||
first = service.保存偏好(author, "first", _请求())["results"][0]
|
||
other = _请求(
|
||
text="允许解释人物感受。",
|
||
quote="这本书允许解释。",
|
||
scope=偏好范围("work", "work-candidate"),
|
||
)
|
||
second = service.保存偏好(author, "second", other)["results"][0]
|
||
assert first["state"] == "active" and second["state"] == "pending"
|
||
assert _消费(pools, author)[0]["preference_id"] == first["preference_id"]
|
||
point = _定位(second)
|
||
review = service.打开偏好审阅(author, point)
|
||
with pytest.raises(Muse错误, match="冲突"):
|
||
service.确认偏好(
|
||
author, "bad-confirm", point, 审阅ID=review["review_id"], 审阅哈希=review["review_hash"]
|
||
)
|
||
point = _定位(second, ((first["preference_id"], first["revision"]),))
|
||
review = service.打开偏好审阅(author, point)
|
||
service.确认偏好(
|
||
author, "confirm", point, 审阅ID=review["review_id"], 审阅哈希=review["review_hash"]
|
||
)
|
||
assert [x["text"] for x in _消费(pools, author)] == [other.text]
|
||
assert service.读取偏好(author, first["preference_id"])["state"] == "withdrawn"
|
||
|
||
|
||
def test_审阅后偏好变化拒绝旧批准和旧修改__26a004(环境):
|
||
service, author, _, feedback = 环境
|
||
fid = service.记录反馈(author, "feedback", feedback)["feedback_id"]
|
||
req = _请求(origin="inferred", feedback_id=str(fid), quote=feedback.quote)
|
||
first = service.保存偏好(author, "infer", req)["results"][0]
|
||
point = _定位(first)
|
||
review = service.打开偏好审阅(author, point)
|
||
service.保存偏好(author, "edit", replace(req, text="保留必要解释。", expected_revision=1))
|
||
with pytest.raises(Muse错误):
|
||
service.确认偏好(
|
||
author, "stale", point, 审阅ID=review["review_id"], 审阅哈希=review["review_hash"]
|
||
)
|
||
with pytest.raises(Muse错误):
|
||
service.保存偏好(author, "stale-edit", replace(req, expected_revision=1))
|
||
|
||
|
||
def test_偏好及反馈拒绝异作者及评测用途__26a005(环境):
|
||
service, author, pools, request = 环境
|
||
fid = str(service.记录反馈(author, "feedback", request)["feedback_id"])
|
||
pref = service.保存偏好(author, "pref", _请求())["results"][0]
|
||
stranger = replace(author, 作者="stranger")
|
||
for action in [
|
||
lambda: service.读取反馈(stranger, fid),
|
||
lambda: service.读取偏好(stranger, pref["preference_id"]),
|
||
lambda: service.保存偏好(replace(author, 用途=用途.评测), "x", _请求()),
|
||
]:
|
||
with pytest.raises(Muse错误):
|
||
action()
|
||
with pools[用途.评测].连接() as conn, pytest.raises(psycopg.errors.InsufficientPrivilege):
|
||
conn.execute("SELECT * FROM muse_author_feedback")
|
||
|
||
|
||
def test_单次要求不被登记为作者长期偏好__26a006(环境):
|
||
with pytest.raises(Muse错误, match="一次性"):
|
||
_请求(duration="request")
|
||
with pytest.raises(Muse错误, match="反馈"):
|
||
_请求(origin="inferred")
|