"""Test 阶段2 benchmark data capture: terminal run → SwarmMetrics persisted to run.metadata. Hermetic (REDIS_FAKE), default NoopExporter (no storage dependency). Verifies: - capture_run_metrics persists run.metadata['benchmark'] with metrics + coverage. - real metrics are numbers; uncollected ones are null (NaN→null, never fabricated). - default export is a no-op and does not fail the capture. Run: python scripts/test-benchmark-capture.py """ import asyncio import json import os import sys from pathlib import Path os.environ["REDIS_FAKE"] = "1" os.environ.pop("BENCHMARK_EXPORT_TARGET", None) # ensure default Noop sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from orchestrator.redis_client import redis_client from orchestrator import swarm_runtime as sr_mod from orchestrator.swarm_runtime import swarm_runtime from orchestrator.task_queue import task_queue, TaskStatus from benchmark.collectors.capture import capture_run_metrics failures = [] def check(name, cond): print(("PASS" if cond else "FAIL"), "-", name) if not cond: failures.append(name) async def _noop(self, *a, **k): return None async def add_task(run, task_id, *, status, agent, cost=0.0, retry=0, depends_on=None): t = await task_queue.create_task(task_id=task_id, description=task_id, agent_role=task_id.split("-")[-1], depends_on=depends_on or [], enqueue=False) t.status = status t.assigned_agent_id = agent t.retry_count = retry t.result = json.dumps({"usage": {"model_cost_usd": cost}}) await task_queue._save_task(t) await swarm_runtime.attach_task(run, t.task_id) return t async def main(): await redis_client.connect() sr_mod.SwarmRuntime._post_callback = _noop body = { "mode": "swarm", "requirement": {"objective": "capture test"}, "orchestration_plan": {"budget": {"max_cost_usd": 10}}, "callback": {"url": "http://127.0.0.1:9/cb", "subscribed_events": []}, "metadata": {"manager_deployment_id": "m-cap", "scenario": "coding"}, } run, _ = await swarm_runtime.get_or_create_run(body=body, idempotency_key=None, correlation_id="c") await add_task(run, "t-implementation", status=TaskStatus.COMPLETED, agent="A", cost=2.0) await add_task(run, "t-testing", status=TaskStatus.COMPLETED, agent="B", cost=3.0) payload = await capture_run_metrics(run) # persisted onto the run reloaded = await swarm_runtime.get_run(run.swarm_id) bench = (reloaded.metadata or {}).get("benchmark") check("run.metadata['benchmark'] persisted", isinstance(bench, dict)) check("payload scenario carried", bench.get("scenario") == "coding") check("metrics dict present", isinstance(bench.get("metrics"), dict)) check("coverage dict present", isinstance(bench.get("coverage"), dict)) # s_completion real (2/2*100 = 100), serialized as a number check("s_completion real (100)", bench["metrics"].get("s_completion") == 100.0 and bench["coverage"].get("s_completion") is True) # uncollected metric (gain) → null, coverage False (no fabrication) check("s_gain null + coverage False", bench["metrics"].get("s_gain") is None and bench["coverage"].get("s_gain") is False) # JSON-serializable (would raise on NaN-as-float being non-serializable only with allow_nan=False; # we converted NaN→None, so strict dumps must succeed) try: json.dumps(payload, allow_nan=False) check("payload strictly JSON-serializable (no NaN)", True) except ValueError: check("payload strictly JSON-serializable (no NaN)", False) print() if failures: print(f"{len(failures)} capture check(s) FAILED: {failures}") sys.exit(1) print("all benchmark capture checks passed") if __name__ == "__main__": asyncio.run(main())