muse-agent-example/tests/集成/test_返修会话接入.py
zizi 6e619e187b 修复:web/node_modules 不再入库,测试导入顺序归位
- 第 3 批 `git add -- web` 把工作区里指向外层检出的 web/node_modules 符号链接一并提交;
  在别的检出里那个绝对路径就是它自己,形成自环,导致 make 生成 与 索引检查(前端用例收集)失败。
  改为不跟踪该路径,并把忽略规则从 web/node_modules/ 放宽为 web/node_modules,同时覆盖符号链接形态。
- tests/集成/test_返修会话接入.py 的导入顺序按 ruff 归位:上一批提交后未复跑 make 检查。

验证:make 检查 exit 0(ruff/格式/pyright/模块边界 22 passed/索引通过);离线层 1431 passed。
2026-09-18 01:42:28 +08:00

186 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""真实作者HTTP与CLI对同一返修会话读写,不替代运行装配或真实模型验收。"""
import json
import os
import socket
import subprocess
import time
from dataclasses import asdict, replace
from pathlib import Path
from threading import Thread
import httpx
import pytest
import test_持久返修会话 as 返修测试
import uvicorn
from fastapi.testclient import TestClient
from muse.接入.cli.审校命令 import 运行审校命令
from muse.接入.http.应用 import 创建应用
from muse.资源加载 import 当前资源提供器
from muse.配置 import 服务配置
pytestmark = pytest.mark.数据库
生成环境 = 返修测试.生成环境
修订环境 = 返修测试.修订环境
@pytest.mark.case_id(
"NC-w23-236011",
environment="隔离PG",
given="合成资料、真实S02配置与明确作者授权",
when="通过实际返修会话和作者入口执行并重新读取",
then=["作者HTTP创建返修与CLI读回选择重放同一会话"],
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
)
def test_作者HTTP创建返修与CLI读回选择重放同一会话__236011(修订环境, tmp_path):
env = 修订环境
secret = tmp_path / "password"
secret.write_text("synthetic-session-author")
secret.chmod(0o600)
config = replace(
env["装配"].配置,
HTTP=服务配置(
str(secret),
作者ID=env["作者"].作者,
公开地址="http://testserver",
允许来源=("http://testserver",),
),
)
env["装配"].配置 = config
payload = {
"command_id": "create-session-http",
"request": asdict(env["授权"]),
"config_id": "gen-config",
"max_rounds": 2,
}
path = tmp_path / "request.json"
path.write_text(json.dumps(payload))
app = 创建应用(config)
with TestClient(app, headers={"origin": "http://testserver"}) as client:
app.state.装配 = env["装配"]
assert client.post("/api/v1/revision-sessions", json=payload).status_code == 401
assert (
client.post("/api/v1/session", json={"password": secret.read_text()}).status_code == 200
)
response = client.post("/api/v1/revision-sessions", json=payload)
assert response.status_code == 201, response.text
face = response.json()
sid = face["session_id"]
assert 运行审校命令(env["装配"], "有限返修", str(path)) == face
tid = face["rounds"][0]["task_id"]
返修测试.批准轮预算(env, tid)
env["剧本"].append({"类型": "文本", "文本": 返修测试.候选输出(env)})
返修测试.修订测试.跑(env, tid)
face = client.get("/api/v1/revision-sessions/" + sid).json()
assert face["state"] == "awaiting_author"
assert face == 运行审校命令(env["装配"], "返修会话", sid)
choice = {
"command_id": "keep-original-http",
"session_id": sid,
"expected_round": 1,
"candidate_id": face["rounds"][0]["candidate_id"],
"choice": "original",
}
selected = client.post(f"/api/v1/revision-sessions/{sid}/decisions", json=choice)
assert selected.status_code == 200, selected.text
path.write_text(json.dumps(choice))
assert 运行审校命令(env["装配"], "返修选择", str(path)) == selected.json()
assert selected.json()["state"] == "closed"
assert selected.json()["stop_reason"] == "author_selected_original"
assert len(env["收到"]) == 1
assert env["正文"].读取正文(env["作者"], "ch-4") == env["原稿"]
@pytest.mark.case_id(
"NC-w23-236012",
environment="隔离PG/显式浏览器",
given="合成资料、真实S02配置与明确作者授权",
when="通过实际返修会话和作者入口执行并重新读取",
then=["返修原文选择经真实浏览器保存刷新及owner复核"],
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
)
@pytest.mark.浏览器
def test_返修原文选择经真实浏览器保存刷新及owner复核__236012(修订环境, tmp_path):
if os.environ.get("MUSE_RUN_BROWSER") != "1":
pytest.skip("需显式启用浏览器")
env = 修订环境
face = 返修测试.发起并产出候选(env, command="browser-revision", max_rounds=2)
sid = face["session_id"]
with socket.socket() as sock:
sock.bind(("127.0.0.1", 0))
port = sock.getsockname()[1]
base = f"http://127.0.0.1:{port}"
secret = tmp_path / "password"
secret.write_text("synthetic-browser-revision")
secret.chmod(0o600)
cfg = replace(
env["装配"].配置,
HTTP=服务配置(
str(secret),
作者ID=env["作者"].作者,
公开地址=base,
允许来源=(base,),
),
)
env["装配"].配置 = cfg
app = 创建应用(cfg)
app.state.装配 = env["装配"]
# lifespan 关掉后要自己补上请求中间件要用的资源提供器,否则 /ready 与业务路由都拿不到。
app.state.资源提供器 = 当前资源提供器()
# 本例注入合成HTTP/计价的真实业务装配;默认安装装配由独立安装用例验证。
server = uvicorn.Server(
uvicorn.Config(app, host="127.0.0.1", port=port, lifespan="off", log_level="warning")
)
thread = Thread(target=server.run, daemon=True)
thread.start()
output = Path(os.environ.get("MUSE_BROWSER_ARTIFACTS", str(tmp_path / "browser")))
output.mkdir(parents=True, exist_ok=True)
try:
for _ in range(100):
if server.started:
break
time.sleep(0.05)
assert server.started
assert httpx.get(base + "/ready", trust_env=False).status_code == 200
target = (
f"/works/gen-work/review?chapter=ch-4&diagnosis={env['授权'].diagnosis_id}"
f"&revision_session={sid}"
)
environment = {
**os.environ,
"MUSE_WORKBENCH_URL": base,
"MUSE_AUTHOR_PASSWORD_FILE": str(secret),
"MUSE_REVISION_SESSION_URL": target,
"MUSE_BROWSER_EXECUTABLE": (
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
),
}
with (output / "浏览器.json").open("w") as report:
result = subprocess.run(
[
"pnpm",
"exec",
"playwright",
"test",
"有限返修.spec.ts",
"--reporter=json",
"--output",
str(output / "产物"),
],
cwd=Path(__file__).resolve().parents[2] / "web",
env=environment,
stdout=report,
stderr=subprocess.STDOUT,
timeout=90,
)
assert result.returncode == 0, "浏览器未通过,见浏览器.json"
current = env["装配"].要求审校().读取返修会话(env["作者"], env["装配"].任务运行, sid)
assert current["state"] == "closed" and current["author_choice"] == "original"
assert current["current_round"] == 1 and len(env["收到"]) == 1
assert env["正文"].读取正文(env["作者"], "ch-4") == env["原稿"]
finally:
server.should_exit = True
thread.join(10)
assert not thread.is_alive()