- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具) - 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺 - 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口 - 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影) - 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威) - R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
167 lines
6.4 KiB
Python
167 lines
6.4 KiB
Python
"""真实作者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 服务配置
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
生成环境 = 返修测试.生成环境
|
||
修订环境 = 返修测试.修订环境
|
||
|
||
|
||
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.浏览器
|
||
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["装配"]
|
||
# 本例注入合成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()
|