"""审计循环调用方接线测试:run_one 终局块 + 包投递 + 升级投递。 只用注入的假 runner 与 init_db 的本地库,不跑真实模型回合、 不联网、不登录任何服务;env 里的值只是占位用的假名。 覆盖(按任务书):开关关闭时完成路径不变;确定性门 fail → 推回(任务重排、packet 可读、下一轮 prompt 含 packet);修好 后 pass(packet 清理、任务完成);3 轮仍 fail → 升级(任务终态、 审计在、不再重排);主观 needs_review → 任务结局不变、不重排、 只记审计;计数损坏 → 直接升级、不循环。 """ import http.client import json import os import sqlite3 import sys import tempfile import threading import time import unittest from pathlib import Path from types import SimpleNamespace try: from cloud.auth import AuthStore from cloud.server import create_server, init_db from cloud.worker import Worker except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from cloud.auth import AuthStore from cloud.server import create_server, init_db from cloud.worker import Worker def _insert_task(db_path, task_id, prompt="do work", status="queued", project_id="p1", audit_loop=0, goal_mode=0): now = time.time() conn = sqlite3.connect(db_path) try: conn.execute( "INSERT INTO tasks(id, project_id, prompt, status," " idempotency_key, depends_on, priority, created_by," " created_at, updated_at, last_heartbeat, audit_loop," " goal_mode)" " VALUES(?, ?, ?, ?, NULL, '[]', 0, 'tester', ?, ?, NULL," " ?, ?)", (task_id, project_id, prompt, status, now, now, audit_loop, goal_mode), ) conn.commit() finally: conn.close() def _row(db_path, task_id): conn = sqlite3.connect(db_path) try: row = conn.execute( "SELECT status, error_text, audit_loop, audit_loop_packet" " FROM tasks WHERE id = ?", (task_id,)).fetchone() finally: conn.close() return row def _packet(db_path, task_id): row = _row(db_path, task_id) if row is None or row[3] is None: return None return json.loads(row[3]) def _failing_runner(cmd): name = cmd[cmd.index("--name") + 1] if "dispatch" in cmd else "t" return SimpleNamespace( returncode=0, stdout=json.dumps({"task": name, "status": "failed", "text": "boom"}), stderr="") def _completed_runner(cmd): name = cmd[cmd.index("--name") + 1] if "dispatch" in cmd else "t" return SimpleNamespace( returncode=0, stdout=json.dumps({"task": name, "status": "completed", "text": "done"}), stderr="") class WireTestCase(unittest.TestCase): def setUp(self): self._old_push_allow = os.environ.get( "CODEX_PUSH_ALLOW_PRIVATE_TARGETS") os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1" self.tmp = tempfile.TemporaryDirectory() self.db_path = os.path.join(self.tmp.name, "control.db") init_db(self.db_path) self.env = {"XCM_API_KEY": "test-placeholder"} self.calls = [] self.audits = [] def tearDown(self): if getattr(self, "_old_push_allow", None) is None: os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None) else: os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = ( self._old_push_allow) self.tmp.cleanup() def _worker(self, runner=None, **kwargs): kwargs.setdefault("env", self.env) kwargs.setdefault("max_retries", 0) kwargs.setdefault("retry_backoff_seconds", 0) worker = Worker(self.db_path, "/fake/host.py", runner=runner or _completed_runner, **kwargs) worker._audit = lambda p, a, d="": self.audits.append((p, a, d)) return worker def _recording(self, inner): def fake(cmd): self.calls.append(list(cmd)) return inner(cmd) return fake # -- 开关关闭:完成路径不变 -------------------------------------- def test_off_task_completes_unchanged(self): _insert_task(self.db_path, "T1", audit_loop=0) out = self._worker().run_one() self.assertEqual(out["status"], "completed") self.assertEqual(_row(self.db_path, "T1")[0], "completed") self.assertIsNone(_packet(self.db_path, "T1")) # -- fail -> 推回 -> 修好 -> pass(完整 run_one 路径) ------------ def test_fail_pushback_then_pass_via_run_one(self): _insert_task(self.db_path, "T2", audit_loop=1) failing = self._worker(runner=self._recording(_failing_runner)) out = failing.run_one() self.assertEqual(out["status"], "queued") self.assertEqual(_row(self.db_path, "T2")[0], "queued") packet = _packet(self.db_path, "T2") self.assertIsNotNone(packet) self.assertEqual(packet["round"], 1) self.assertEqual(packet["findings"][0]["id"], "execution_failed") self.assertTrue(any(a == "worker_audit_loop_pushback" for _, a, _ in self.audits)) # 下一轮:包以前言贴进 prompt(库 prompt 本身不动),修好后 pass。 self.calls.clear() self.audits.clear() passing = self._worker(runner=self._recording(_completed_runner)) out = passing.run_one() self.assertEqual(out["status"], "completed") self.assertEqual(_row(self.db_path, "T2")[0], "completed") self.assertIsNone(_packet(self.db_path, "T2")) prompted = " ".join(" ".join(c) for c in self.calls) self.assertIn("execution_failed", prompted) conn = sqlite3.connect(self.db_path) try: prompt = conn.execute( "SELECT prompt FROM tasks WHERE id = 'T2'").fetchone()[0] finally: conn.close() self.assertEqual(prompt, "do work") # -- 3 轮仍 fail -> 升级 ------------------------------------------- def test_three_rounds_then_escalate_via_run_one(self): _insert_task(self.db_path, "T3", audit_loop=1) worker = self._worker(runner=_failing_runner) for _ in range(3): out = worker.run_one() self.assertEqual(out["status"], "queued") out = worker.run_one() self.assertEqual(out["status"], "failed") self.assertEqual(_row(self.db_path, "T3")[0], "failed") self.assertIsNone(_packet(self.db_path, "T3")) self.assertIn("审计循环升级", _row(self.db_path, "T3")[1]) self.assertTrue(any(a == "worker_audit_loop_escalated" for _, a, _ in self.audits)) # 升级后不再自动重排:下一轮认领不到该任务。 self.assertEqual(worker.run_one()["dispatched"], False) # -- 计数损坏 -> 直接升级、不循环 ---------------------------------- def test_corrupted_count_escalates_via_wire(self): _insert_task(self.db_path, "T4", status="failed", audit_loop=1) worker = self._worker() worker.audit_loop_evaluate( {"id": "T4", "project_id": "p1", "audit_loop": True}, {"execution": False}, findings=[]) # 上面那行若因空 finding 报错也不影响:直接写坏状态行。 conn = sqlite3.connect(self.db_path) try: conn.execute( "INSERT OR REPLACE INTO task_audit_loop_state" "(task_id, round, passed_gates, criteria, last_decision," " updated_at) VALUES('T4', 'not-a-number', '[]', '{}'," " 'pushback', ?)", (time.time(),)) conn.commit() finally: conn.close() task = worker.get_task("T4") final, error, decision = worker._audit_loop_wire( task, "failed", {"stdout": ""}, "boom") self.assertEqual(decision, "escalate") self.assertEqual(final, "failed") self.assertIn("审计循环升级", error) self.assertIsNone(_packet(self.db_path, "T4")) # -- goal 门:goal_met False 推回,None 不列入门 ------------------- def test_goal_met_false_pushbacks_and_none_is_not_a_gate(self): worker = self._worker() # 终局已写回的前提:库状态须与传入的 final 一致(completed), # 否则接线的 CAS 会正确地拒绝改状态——那是测试搭错,不是 bug。 _insert_task(self.db_path, "T5", status="completed", audit_loop=1, goal_mode=1) task = worker.get_task("T5") gates, findings = worker._audit_loop_gate_results( task, "completed", {"goal_met": False, "goal_error": "nope"}) self.assertFalse(gates["goal_verify"]) self.assertEqual(findings[0].id, "goal_unmet") final, _, decision = worker._audit_loop_wire( task, "completed", {"goal_met": False, "goal_error": "nope"}, None) self.assertEqual(decision, "pushback") self.assertEqual(final, "queued") gates, _ = worker._audit_loop_gate_results( task, "completed", {"goal_met": None}) self.assertNotIn("goal_verify", gates) # -- 主观 needs_review:结局不变、不重排、只记审计 ----------------- def test_needs_review_leaves_outcome_untouched(self): _insert_task(self.db_path, "T6", status="failed", audit_loop=1) worker = self._worker() worker.audit_loop_evaluate = lambda t, g, findings=None, fixed=None: { "enabled": True, "decision": "needs_review", "packet": None, "new_state": None, "reason": "subjective"} task = worker.get_task("T6") final, _, decision = worker._audit_loop_wire( task, "failed", {"stdout": ""}, "boom") self.assertEqual(decision, "needs_review") self.assertEqual(final, "failed") self.assertEqual(_row(self.db_path, "T6")[0], "failed") self.assertTrue(any(a == "worker_audit_loop_needs_review" for _, a, _ in self.audits)) # -- 包注入:有效包贴前言、无效包不动、重复调用不叠加 -------------- def test_packet_injection_and_invalid_packet(self): worker = self._worker() packet = json.dumps({ "task_id": "T7", "round": 1, "findings": [{"id": "execution_failed", "severity": "high", "evidence": "boom", "reproduction": "re-run", "acceptance": "task completes", "kind": "deterministic", "gate": "execution"}], "criteria": {"execution_failed": "execution"}}) task = {"id": "T7", "project_id": "p1", "prompt": "do work", "audit_loop_packet": packet} out = worker._apply_audit_loop_packet(task) self.assertIn("execution_failed", out["prompt"]) self.assertTrue(out["prompt"].startswith("[审计推回包")) again = worker._apply_audit_loop_packet(out) self.assertEqual(again["prompt"].count("execution_failed"), 1) bad = worker._apply_audit_loop_packet( {"id": "T7", "project_id": "p1", "prompt": "do work", "audit_loop_packet": "{not json"}) self.assertEqual(bad["prompt"], "do work") # -- 列与回读:init_db 建列、get_task 读到开关 ---------------------- def test_columns_and_get_task_flag(self): _insert_task(self.db_path, "T8", audit_loop=1) worker = self._worker() task = worker.get_task("T8") self.assertEqual(task["audit_loop"], 1) self.assertIsNone(task["audit_loop_packet"]) class ServerCreateTests(unittest.TestCase): """POST /tasks 的 audit_loop 字段:接受、拒绝、协调员拒绝。 复用 test_server_goal_notify 的 create_server + 真实 HTTP 骨架。 """ def setUp(self): self._old_push_allow = os.environ.get( "CODEX_PUSH_ALLOW_PRIVATE_TARGETS") os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1" self.tmp = tempfile.TemporaryDirectory() self.db_path = os.path.join(self.tmp.name, "control.db") self.auth = AuthStore(self.db_path) self.owner_id, self.owner_key = self.auth.create_user("owner") self.server = create_server(self.db_path, "127.0.0.1", 0) self.port = self.server.server_address[1] self.thread = threading.Thread( target=self.server.serve_forever, daemon=True) self.thread.start() def tearDown(self): self.server.shutdown() self.server.server_close() self.thread.join(timeout=5) if getattr(self, "_old_push_allow", None) is None: os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None) else: os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = ( self._old_push_allow) try: self.auth.conn.close() except Exception: pass self.tmp.cleanup() def _req(self, method, path, body=None, key=None): conn = http.client.HTTPConnection( "127.0.0.1", self.port, timeout=10) hdrs = {} payload = None if body is not None: payload = json.dumps(body).encode("utf-8") hdrs["Content-Type"] = "application/json" if key is not None: hdrs["X-API-Key"] = key try: conn.request(method, path, body=payload, headers=hdrs) resp = conn.getresponse() data = resp.read() finally: conn.close() try: parsed = json.loads(data.decode("utf-8")) if data else None except ValueError: parsed = None return resp.status, parsed def test_create_task_audit_loop_flag(self): _, project = self._req( "POST", "/projects", {"name": "p"}, self.owner_key) pid = project["id"] status, task = self._req( "POST", "/tasks", {"project_id": pid, "prompt": "do it", "audit_loop": True}, self.owner_key) self.assertEqual(status, 201, task) self.assertEqual(task["audit_loop"], 1) status, _ = self._req( "POST", "/tasks", {"project_id": pid, "prompt": "bad", "audit_loop": "yes"}, self.owner_key) self.assertEqual(status, 400) # 协调员接线轨:父任务带 audit_loop 不再 400——此前拒绝 # 的理由是“父任务接线覆盖不到、接受即静默丢弃”,合流 # 接线(worker._coord_audit_loop_merge)落地后该理由已 # 消除;子任务仍不直接接受外部开关(worker 内部 INSERT # 不写该列)。非法值仍 400。 status, task = self._req( "POST", "/tasks", {"project_id": pid, "prompt": "coord", "coordinator_parts": [{"name": "a", "prompt": "do a"}], "audit_loop": True}, self.owner_key) self.assertEqual(status, 201, task) self.assertEqual(task["audit_loop"], 1) if __name__ == "__main__": unittest.main()