- 资料研究路由:scene-slices/search 检索与 reindex 构建端点 - 参考挑选反馈请求增语料类型(method/slice,缺省method存量兼容);信号聚合与明细只计方法语料,片段挑选只落账留痕 - 选段改写页:语料源切换(方法卡/场景片段),片段卡显示书名+预览,挑选进 slice_references,落账按源分派 - 集成a10037:片段挑选HTTP落账不进方法信号轨;场景检索端点真实接缝
190 lines
7.4 KiB
Python
190 lines
7.4 KiB
Python
"""参考挑选反馈的真实 CLI/HTTP 接缝:落账、信号聚合读回;不经浏览器。"""
|
||
|
||
import json
|
||
import subprocess
|
||
import sys
|
||
|
||
import pytest
|
||
import test_参考改写 as 改写测试
|
||
from fastapi.testclient import TestClient
|
||
|
||
from muse.接入.http.应用 import 创建应用
|
||
from muse.资源加载 import 加载清单
|
||
from muse.配置 import 读取配置
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
生成环境 = 改写测试.生成环境
|
||
参考环境 = 改写测试.参考环境
|
||
片段环境 = 改写测试.片段环境
|
||
|
||
|
||
def _配置(env, tmp_path):
|
||
secret = tmp_path / "author-password"
|
||
secret.write_text("synthetic-evaluation-only")
|
||
secret.chmod(0o600)
|
||
pool = env["库"]
|
||
config = tmp_path / f"{pool.用途.value}.toml"
|
||
config.write_text(
|
||
'["数据库"]\n"取值方式"="受控存储"\n"位置"='
|
||
+ json.dumps(pool.引用.位置)
|
||
+ '\n["运行"]\n"用途"='
|
||
+ json.dumps(pool.用途.value)
|
||
+ '\n["资源"]\n"发布身份"="test"\n["HTTP"]\n"作者ID"="eval-author"'
|
||
+ '\n"口令文件"='
|
||
+ json.dumps(str(secret))
|
||
+ '\n"公开地址"="http://testserver"\n"允许来源"=["http://testserver"]\n'
|
||
)
|
||
path = config
|
||
path.write_text(
|
||
path.read_text()
|
||
.replace("eval-author", env["作者"].作者)
|
||
.replace(
|
||
'"发布身份"="test"',
|
||
'"发布身份"=' + json.dumps(加载清单()["构建身份"]),
|
||
)
|
||
)
|
||
return path
|
||
|
||
|
||
def _cli(tmp_path, config, 动作, *args):
|
||
response = subprocess.run(
|
||
[sys.executable, "-I", "-m", "muse", "经验", str(config), 动作, *map(str, args)],
|
||
cwd=tmp_path,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=30,
|
||
)
|
||
assert response.returncode == 0, response.stderr
|
||
return json.loads(response.stdout)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w32-a10013",
|
||
environment="隔离PG、实际CLI子进程与合成HTTP,不经浏览器",
|
||
given="真实作者、已确认方法版本与检索回执形状的候选清单",
|
||
when="经HTTP保存参考挑选反馈并按领域读回信号,再经CLI读同一信号",
|
||
then=["反馈只追加落账;HTTP与CLI读回一致的选用/拒绝计数"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
|
||
)
|
||
def test_HTTP与CLI读回一致的挑选信号__a10013(参考环境, tmp_path):
|
||
env = 参考环境
|
||
config = _配置(env, tmp_path)
|
||
版本 = env["参考版本"]
|
||
body = {
|
||
"command_id": "http-ref-sel-1",
|
||
"selection": {
|
||
"query": "机甲近身刀法",
|
||
"domain": "combat",
|
||
"work_id": "gen-work",
|
||
"quote": "",
|
||
"index_generation": 0,
|
||
"种类": "参考挑选",
|
||
"candidates": [{"method_version_id": 版本, "rank": 1, "score": 0.031}],
|
||
"selected": [版本],
|
||
"rejected": [],
|
||
},
|
||
}
|
||
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
|
||
assert (
|
||
client.post(
|
||
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
|
||
).status_code
|
||
== 200
|
||
)
|
||
saved = client.post("/api/v1/reference-selections", json=body)
|
||
assert saved.status_code == 201, saved.text
|
||
assert saved.json()["payload"]["种类"] == "参考挑选"
|
||
# 幂等重放:同命令同内容返回原记录
|
||
replay = client.post("/api/v1/reference-selections", json=body)
|
||
assert replay.status_code == 201
|
||
assert replay.json()["feedback_id"] == saved.json()["feedback_id"]
|
||
signals = client.get("/api/v1/reference-selection-signals", params={"domain": "combat"})
|
||
assert signals.status_code == 200
|
||
rows = {行["method_version_id"]: 行 for 行 in signals.json()}
|
||
assert rows[版本]["选用次数"] == 1
|
||
assert rows[版本]["拒绝次数"] == 0
|
||
assert rows[版本]["选中位次"] == [1]
|
||
|
||
cli = _cli(tmp_path, config, "挑选信号", "combat")
|
||
assert {行["method_version_id"]: 行["选用次数"] for 行 in cli} == {版本: 1}
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w32-a10014",
|
||
environment="隔离PG与合成HTTP,不经浏览器",
|
||
given="选中与拒绝重叠的非法请求",
|
||
when="经HTTP保存参考挑选反馈",
|
||
then=["边界校验拒绝,不落账"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
|
||
)
|
||
def test_非法挑选请求在边界拒绝__a10014(参考环境, tmp_path):
|
||
env = 参考环境
|
||
config = _配置(env, tmp_path)
|
||
版本 = env["参考版本"]
|
||
body = {
|
||
"command_id": "http-ref-sel-bad",
|
||
"selection": {
|
||
"query": "机甲近身刀法",
|
||
"domain": "combat",
|
||
"candidates": [{"method_version_id": 版本, "rank": 1, "score": 0.03}],
|
||
"selected": [版本],
|
||
"rejected": [版本],
|
||
},
|
||
}
|
||
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
|
||
assert (
|
||
client.post(
|
||
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
|
||
).status_code
|
||
== 200
|
||
)
|
||
response = client.post("/api/v1/reference-selections", json=body)
|
||
assert response.status_code >= 400
|
||
signals = client.get("/api/v1/reference-selection-signals")
|
||
assert all(行["method_version_id"] != 版本 for 行 in signals.json())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w33-a10037",
|
||
environment="隔离PG与合成HTTP,不经浏览器",
|
||
given="已构建的场景切片索引与片段语料挑选",
|
||
when="经HTTP落账片段挑选反馈再读方法信号",
|
||
then=["片段挑选落账成功;不进入方法信号轨(语料类型过滤)"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
|
||
)
|
||
def test_片段语料挑选不进方法信号__a10037(片段环境, tmp_path):
|
||
env = 片段环境
|
||
config = _配置(env, tmp_path)
|
||
片段 = env["片段"]
|
||
body = {
|
||
"command_id": "http-slice-sel-1",
|
||
"selection": {
|
||
"query": "刀锋划过装甲",
|
||
"domain": "other",
|
||
"work_id": "gen-work",
|
||
"quote": "",
|
||
"index_generation": 0,
|
||
"语料类型": "slice",
|
||
"种类": "参考挑选",
|
||
"candidates": [{"method_version_id": 片段["slice_id"], "rank": 1, "score": 0.0}],
|
||
"selected": [片段["slice_id"]],
|
||
"rejected": [],
|
||
},
|
||
}
|
||
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
|
||
assert (
|
||
client.post(
|
||
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
|
||
).status_code
|
||
== 200
|
||
)
|
||
saved = client.post("/api/v1/reference-selections", json=body)
|
||
assert saved.status_code == 201, saved.text
|
||
assert saved.json()["payload"]["语料类型"] == "slice"
|
||
检索 = client.get("/api/v1/scene-slices/search", params={"q": "刀锋", "limit": 3})
|
||
assert 检索.status_code == 200, 检索.text
|
||
信号 = client.get("/api/v1/reference-selection-signals", params={"domain": ""})
|
||
assert 信号.status_code == 200, 信号.text
|
||
行集 = 信号.json()
|
||
assert all(r["method_version_id"] != 片段["slice_id"] for r in 行集)
|