""" test_studio_smoke.py — U5 端到端产物契约验证。 端到端真跑由 cheap_studio CLI 提供(落 evidence/run-summary.json + staged 工程 + 九门 verdict); 本 test 验证最近一次 run 的产物契约(真 src/ 多文件 + staged + 九门 verdict + run-summary 字段)。 真跑回归(~2.5min,真 M3 ReAct): cheap-worker/.venv/bin/python cheap-worker/cheap_studio.py --id cheap-smoke1 --brief "一个简单的点击得分小游戏:屏幕上随机冒出可点的圆,点中加分" 然后:cheap-worker/.venv/bin/python cheap-worker/tests/test_studio_smoke.py """ import json import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # → cheap-worker/ import cheap_run GID = "cheap-smoke1" RUN_SUMMARY = cheap_run.game_dir(GID) / "evidence" / "run-summary.json" # 默认 pytest 是离线单测入口;真模型 smoke 必须先由上方 CLI 显式产证。 # 直接执行本文件时 pytestmark 不生效,仍会由 _require_run 严格拒绝缺失证据。 pytestmark = pytest.mark.skipif( not RUN_SUMMARY.exists(), reason="需先显式运行 cheap_studio 真模型 smoke 生成 cheap-smoke1 证据", ) def _require_run() -> dict: p = RUN_SUMMARY if not p.exists(): raise AssertionError( f"先跑端到端单局:cheap-worker/.venv/bin/python cheap-worker/cheap_studio.py --id {GID} --brief ...(缺 {p})") return json.loads(p.read_text(encoding="utf-8")) def test_run_summary_contract(): """run-summary 含 finished/check/build/staged 字段且收敛。""" s = _require_run() assert s["finished"] is True, s assert s["check"]["ok"] and s["build"]["ok"], s assert s["staged"] is True, s def test_real_src_multifile(): """产真 src/ 多文件工程(非单文件 / JSON blob / new Function blob)——doc↔code 兑现门的便宜档侧。""" src = cheap_run.game_dir(GID) / "src" assert (src / "game-logic.js").exists(), "缺 src/game-logic.js" js = list(src.glob("*.js")) assert len(js) >= 2, f"应为真 src/ 多文件工程,实际 {len(js)} 个 .js" def test_staged_artifact_exists(): """stage 后 _wg1-gen// 有自包含 bundle + index + 自动产的 play-spec。""" dst = cheap_run.wg1_game_dir(GID) assert (dst / "bundle.iife.js").exists() assert (dst / "index.html").exists() assert (dst / "play-spec.json").exists(), "play-spec 缺失 → 九门会 driven=false 假绿" def test_nine_gates_verdict(): """九门 verdict.json 存在且含逐门 guards(不假绿)。""" v = cheap_run.wg1_game_dir(GID) / "evidence" / "verdict.json" assert v.exists(), "缺九门 verdict.json" d = json.loads(v.read_text(encoding="utf-8")) assert isinstance(d.get("guards"), dict), "verdict 应含九门 guards" if __name__ == "__main__": _fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)] _failed = 0 for _fn in _fns: try: _fn() print(f" PASS {_fn.__name__}") except Exception as e: # noqa: BLE001 _failed += 1 print(f" FAIL {_fn.__name__}: {type(e).__name__}: {e}") print(f"\n{len(_fns) - _failed}/{len(_fns)} passed") sys.exit(1 if _failed else 0)