"""真实任务尝试经CLI/HTTP记录观察;不向请求方披露原始输入或派发模型。""" import json import os import socket import subprocess import sys import time from dataclasses import replace from pathlib import Path from threading import Thread import httpx import pytest import test_生产评测权限隔离 as 接入测试 import test_运行观察与来源 as 观察测试 import uvicorn from fastapi.testclient import TestClient from muse.接入.http.应用 import 创建应用 from muse.资源加载 import 加载清单 from muse.配置 import 读取配置 pytestmark = pytest.mark.数据库 观察环境 = 观察测试.观察环境 def _配置(env, tmp_path): path = 接入测试._配置文件(env["pool"], tmp_path) path.write_text( path.read_text() .replace("eval-author", env["author"].作者) .replace( '"发布身份"="test"', '"发布身份"=' + json.dumps(加载清单()["构建身份"]), ) ) return path def _cli(tmp_path, config, action, *args): response = subprocess.run( [sys.executable, "-I", "-m", "muse", "经验", str(config), action, *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-w26-26e101", environment="隔离PG/合成HTTP及浏览器", given="固定请求、来源与作者;按用例环境准备独立测试输入", when="通过实际公开入口执行并核对拒绝、状态及持久结果", then=["真实CLI及HTTP按原任务用途保存观察且读取不重发"], contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察", ) def test_真实CLI及HTTP按原任务用途保存观察且读取不重发__26e101(观察环境, tmp_path): env = 观察环境 task_id, claim, _ = 观察测试._完成(env) before = env["run"].读取任务(task_id) config = _配置(env, tmp_path) source_request = {"task_id": task_id, "attempt_id": claim.尝试ID} source_file = tmp_path / "source.json" source_file.write_text(json.dumps(source_request)) source = _cli(tmp_path, config, "运行来源", source_file) assert source["runtime"]["content_purpose"] == "generation" assert "不应进入运行观察的正文" not in json.dumps(source, ensure_ascii=False) body = { "command_id": "entry-observation", "observation": { "sources": [source["locator"]], "phenomenon": "实际尝试有结果", "quote": "保留这次的观察。", "background": "固定合成任务。", "issue_key": "同类结果", }, } with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client: assert ( client.post( "/api/v1/session", json={"password": "synthetic-evaluation-only"} ).status_code == 200 ) task = client.get("/api/v1/tasks/" + task_id) assert task.status_code == 200 and task.json()["steps"][0]["attempt_id"] == claim.尝试ID reread = client.post("/api/v1/run-observation-sources", json=source_request) assert reread.status_code == 200 and reread.json() == source assert client.get("/api/v1/run-observations", params={"task_id": task_id}).json() == [] bad = {**body, "observation": {**body["observation"], "state": "promoted"}} assert client.post("/api/v1/run-observations", json=bad).status_code == 422 saved = client.post("/api/v1/run-observations", json=body) assert saved.status_code == 201, saved.text oid = saved.json()["observation_id"] assert ( client.get("/api/v1/run-observations/" + oid).json()["payload"]["quote"] == "保留这次的观察。" ) repeats = client.get("/api/v1/recurring-issues", params={"issue_key": "同类结果"}) assert repeats.status_code == 200, repeats.text write_file = tmp_path / "observation.json" write_file.write_text(json.dumps(body)) replay = _cli(tmp_path, config, "记录运行观察", write_file) assert replay["deduped"] and replay["observation_id"] == oid assert ( _cli(tmp_path, config, "运行观察", oid)["payload"]["sources"][0]["runtime"]["attempt_id"] == claim.尝试ID ) assert len(_cli(tmp_path, config, "运行观察清单")) == 1 assert env["run"].读取任务(task_id) == before other = tmp_path / "other.toml" other.write_text(config.read_text().replace(env["author"].作者, "other-observer")) with TestClient(创建应用(读取配置(other)), headers={"Origin": "http://testserver"}) as client: client.post("/api/v1/session", json={"password": "synthetic-evaluation-only"}) assert ( client.post("/api/v1/run-observation-sources", json=source_request).status_code == 403 ) assert client.get("/api/v1/run-observations").json() == [] @pytest.mark.case_id( "NC-w26-26e102", environment="隔离PG/合成HTTP及浏览器", given="固定请求、来源与作者;按用例环境准备独立测试输入", when="通过实际公开入口执行并核对拒绝、状态及持久结果", then=["任务页真实执行观察保存刷新且运行本身不变"], contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察", ) @pytest.mark.浏览器 def test_任务页真实执行观察保存刷新且运行本身不变__26e102(观察环境, tmp_path): if os.environ.get("MUSE_RUN_BROWSER") != "1": pytest.skip("需显式启用浏览器") env = 观察环境 task_id, claim, _ = 观察测试._完成(env) before = env["run"].读取任务(task_id) config_file = _配置(env, tmp_path) with socket.socket() as sock: sock.bind(("127.0.0.1", 0)) port = sock.getsockname()[1] url = f"http://127.0.0.1:{port}" config = 读取配置(config_file) assert config.HTTP is not None config = replace(config, HTTP=replace(config.HTTP, 公开地址=url, 允许来源=(url,))) server = uvicorn.Server( uvicorn.Config(创建应用(config), host="127.0.0.1", port=port, log_level="warning") ) thread = Thread(target=server.run, daemon=True) thread.start() output = Path(os.environ.get("MUSE_BROWSER_ARTIFACTS", str(tmp_path / "browser"))).resolve() output.mkdir(parents=True) try: for _ in range(100): if server.started: break time.sleep(0.05) assert server.started and httpx.get(url + "/ready", trust_env=False).status_code == 200 environment = { **os.environ, "MUSE_WORKBENCH_URL": url, "MUSE_AUTHOR_PASSWORD_FILE": config.HTTP.口令文件, "MUSE_OBSERVATION_TASK": task_id, "MUSE_OBSERVATION_ATTEMPT": claim.尝试ID, "MUSE_BROWSER_EXECUTABLE": ( "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ), } with (output / "浏览器.json").open("w") as report: process = 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 process.returncode == 0, "浏览器未通过,见浏览器.json" rows = env["service"].列出运行观察(env["author"], 任务ID=task_id) assert len(rows) == 1 and rows[0]["payload"]["quote"] == "先把这次的现象记下来。" assert env["run"].读取任务(task_id) == before finally: server.should_exit = True thread.join(10) assert not thread.is_alive()