"""参考挑选反馈的真实 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 行集)