"""ResultAggregator(质量驱动协作聚合)测试 — 取代旧 test-queen(best-of-N 选优)。 覆盖文章模型 + ①②③闭环:收集共享池互补产出、AST 函数级整合(无 LLM 兜底不靠"最长版本")、 质量门决策(达标 accept / 不达标 bounce 打回 / 无法评分诚实不打回)、终态聚合 best-effort。 """ import asyncio import json import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.environ.setdefault("REDIS_FAKE", "1") os.environ.setdefault("ALLOW_MEMORY_STORE", "1") for k in ("OPENAI_API_KEY", "MODEL_API_KEY"): # 无 LLM,走 AST 兜底(确定性,不依赖外部模型) os.environ.pop(k, None) from orchestrator.result_aggregator import ( _strip_fence, _ast_merge_python, _merge_fallback, _decide, _collect_per_path, merge_artifacts, finalize_run, _emit_patch_mode, ) _failures = [] def check(name, cond): print(("PASS" if cond else "FAIL") + " - " + name) if not cond: _failures.append(name) class FakeTask: def __init__(self, task_id, files): self.task_id = task_id self.assigned_agent_id = "agent-" + task_id self.agent_role = "impl" self.result = json.dumps({"files": files}) class FakeRun: def __init__(self, metadata=None): self.metadata = metadata or {} self.swarm_id = "swarm-test" # --- _strip_fence --- check("strip fence python", _strip_fence("```python\nx = 1\n```") == "x = 1") check("strip fence passthrough", _strip_fence("z = 3") == "z = 3") # --- ① AST 函数级合并:各 agent 写不同函数 → 合并成含全部函数的一份(不靠最长版本) --- v1 = "def is_palindrome(s):\n return s == s[::-1]\n" v2 = "def count_vowels(s):\n return sum(c in 'aeiou' for c in s)\ndef reverse_words(s):\n return ' '.join(s.split()[::-1])\n" merged_py = _ast_merge_python([v1, v2]) check("AST 合并含 is_palindrome", merged_py and "def is_palindrome" in merged_py) check("AST 合并含 count_vowels", merged_py and "def count_vowels" in merged_py) check("AST 合并含 reverse_words", merged_py and "def reverse_words" in merged_py) check("AST 合并去重(同名一次)", merged_py and merged_py.count("def is_palindrome") == 1 if merged_py else False) # 语法垃圾 → None(回退最长) check("AST 解析全失败 → None", _ast_merge_python(["@#$%", "!!!"]) is None) check("merge_fallback 非py取最长", _merge_fallback("a.txt", ["short", "longer text"]) == "longer text") # --- _decide 决策门 --- check("decide: 无阈值 → accept", _decide({"pass_rate": 10.0}, None, 0, 2) == "accept") check("decide: 无法评分(None) → accept(诚实)", _decide({"pass_rate": None}, 80.0, 0, 2) == "accept") check("decide: 达标 → accept", _decide({"pass_rate": 90.0}, 80.0, 0, 2) == "accept") check("decide: 不达标且未到上限 → bounce", _decide({"pass_rate": 40.0}, 80.0, 0, 2) == "bounce") check("decide: 不达标但达上限 → accept", _decide({"pass_rate": 40.0}, 80.0, 2, 2) == "accept") # --- 收集共享池:同文件多版本都保留 --- t1 = FakeTask("t1", [{"path": "stringutils.py", "content": v1, "action": "write"}]) t2 = FakeTask("t2", [{"path": "stringutils.py", "content": v2, "action": "write"}]) t3 = FakeTask("t3", [{"path": "test_stringutils.py", "content": "def test_x():\n assert True\n", "action": "write"}]) by_path = _collect_per_path([t1, t2, t3]) check("collect: 同文件两版本都保留", len(by_path.get("stringutils.py", [])) == 2) # --- merge_artifacts:无 LLM 用 AST 整合三函数(不丢互补) --- merged = asyncio.run(merge_artifacts([t1, t2, t3], "实现 stringutils")) su = next(f for f in merged if f.path == "stringutils.py") check("merge: 整合后含全部三函数", all(fn in su.content for fn in ("is_palindrome", "count_vowels", "reverse_words"))) # --- finalize_run:无阈值 → accept;无 git grant → 诚实不 promote --- res = asyncio.run(finalize_run(FakeRun(), [t1, t2, t3], "实现 stringutils")) check("finalize: decision=accept(无阈值)", res.get("decision") == "accept") check("finalize: finalized=True", res.get("finalized") is True) check("finalize: 无 git grant promoted=False", (res.get("promotion") or {}).get("reason") == "no_git_grant") # --- finalize_run:无产物 → finalized=False --- empty = asyncio.run(finalize_run(FakeRun(), [], "空")) check("finalize: 无产物 finalized=False", empty.get("finalized") is False and empty.get("reason") == "no_artifacts") # --- emit_patch 模式(SWE-bench):门控 + 无 grant 诚实失败 --- check("emit_patch_mode: 默认关", _emit_patch_mode(FakeRun()) is False) check("emit_patch_mode: per-run flag 开", _emit_patch_mode(FakeRun({"emit_patch": True})) is True) os.environ["SWARM_EMIT_PATCH"] = "1" check("emit_patch_mode: env 开", _emit_patch_mode(FakeRun()) is True) patch_run = asyncio.run(finalize_run(FakeRun(), [t1, t2, t3], "实现 stringutils")) check("emit_patch: mode=emit_patch", patch_run.get("mode") == "emit_patch") check("emit_patch: 无 git grant patch=None", patch_run.get("patch") is None) check("emit_patch: 无 grant reason=no_git_grant", (patch_run.get("patch_meta") or {}).get("reason") == "no_git_grant") os.environ.pop("SWARM_EMIT_PATCH", None) if _failures: print(f"\nFAILED: {len(_failures)} check(s): {_failures}") raise SystemExit(1) print("\nALL PASSED")