"""覆盖率 78%->85% 第 4 步:cloud/worker.py run_one/_run_attempt 剩余缺口的真实行为测试。 只用临时 SQLite(server.init_db 建表)与同步假执行器: 假执行器是执行边界(真实 Local/Docker 执行器的替身), 绝不碰真实 sub2api/生产网关、不跑模型;超时与重试成本 用 max_retries=0、retry_backoff_seconds=0 控制。 """ import os import sqlite3 import tempfile import unittest from unittest import mock from cloud.sandbox import DockerExecutor from cloud.server import init_db from cloud.worker import Worker def _insert_task(db_path, task_id, status="queued", **kw): conn = sqlite3.connect(db_path) try: cols = {"id": task_id, "project_id": kw.pop("project_id", "proj"), "prompt": kw.pop("prompt", "do it"), "status": status, "created_by": kw.pop("created_by", "owner1"), "created_at": kw.pop("created_at", 1000.0), "updated_at": kw.pop("updated_at", 1000.0)} cols.update(kw) names = ", ".join(cols) placeholders = ", ".join("?" * len(cols)) conn.execute("INSERT INTO tasks(%s) VALUES(%s)" % (names, placeholders), tuple(cols.values())) conn.commit() finally: conn.close() def _row(db_path, sql, params=()): conn = sqlite3.connect(db_path) try: return conn.execute(sql, params).fetchall() finally: conn.close() def _set(db_path, sql, params=()): conn = sqlite3.connect(db_path) try: conn.execute(sql, params) conn.commit() finally: conn.close() def _result(status="completed", stdout="done", **kw): r = {"executor": "local", "status": status, "stdout": stdout, "stderr": "", "returncode": 0, "host_status": None} r.update(kw) return r class FakeSync: """同步假执行器:非 Local/Docker/Cloud,走 _run_attempt 的 else 分支(dispatch_task(task) / goal_task(task) 单参形)。 """ def __init__(self, dispatch=None, run=None, goal=None): self.dispatch_result = dispatch or _result() self.run_result = run or _result() self.goal_result = goal or _result() self.calls = [] def dispatch_task(self, task): self.calls.append(("dispatch", task["id"])) return dict(self.dispatch_result) def run_task(self, task): self.calls.append(("run", task["id"])) return dict(self.run_result) def goal_task(self, task): self.calls.append(("goal", task["id"])) return dict(self.goal_result) class Worker3Base(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.addCleanup(self.tmp.cleanup) self.db_path = os.path.join(self.tmp.name, "control.db") init_db(self.db_path) self.hb = os.path.join(self.tmp.name, "hb.json") def _worker(self, executor=None, **kw): kw.setdefault("env", {"XCM_API_KEY": "test-placeholder"}) kw.setdefault("heartbeat_file", self.hb) kw.setdefault("worker_id", "cov85-worker3") kw.setdefault("max_retries", 0) kw.setdefault("retry_backoff_seconds", 0) return Worker(self.db_path, "/fake/host.py", executor=executor, **kw) class RunOneGatesTest(Worker3Base): def test_missing_key_gate(self): _insert_task(self.db_path, "t-key", status="queued") fake = FakeSync() w = self._worker(executor=fake, env={}) result = w.run_one() self.assertTrue(result["dispatched"]) self.assertEqual(result["status"], "failed") self.assertIn("Missing environment variable", result["error"]) self.assertEqual(_row(self.db_path, "SELECT status FROM tasks WHERE id = 't-key'"), [("failed",)]) self.assertEqual(fake.calls, []) def test_already_claimed(self): _insert_task(self.db_path, "t-claim", status="running") w = self._worker(executor=FakeSync()) task = w.get_task("t-claim") result = w.run_one(task) self.assertFalse(result["dispatched"]) self.assertEqual(result["reason"], "already_claimed") def test_quota_gate(self): _insert_task(self.db_path, "t-quota", status="queued") _set(self.db_path, "INSERT INTO user_quotas(user_id, quota_usd_micros," " updated_at) VALUES('owner1', 0, 0)") w = self._worker(executor=FakeSync()) result = w.run_one() self.assertTrue(result["dispatched"]) self.assertEqual(result["status"], "interrupted") self.assertIn("quota_exceeded", result["error"]) self.assertEqual(_row( self.db_path, "SELECT interrupted_reason FROM tasks WHERE id = 't-quota'"), [("quota",)]) def test_goal_cap_over_and_zero_old_form(self): for rounds, want in [(101, "超过上限"), (0, "已禁用")]: with self.subTest(rounds=rounds): tid = "t-cap-%s" % rounds _insert_task(self.db_path, tid, status="queued", verify_command="make test", goal_max_rounds=rounds) w = self._worker(executor=FakeSync()) result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn(want, result["error"]) self.assertEqual(_row( self.db_path, "SELECT status FROM tasks WHERE id = ?", (tid,)), [("failed",)]) def test_goal_cap_bad_type_passes_gate(self): # goal_max_rounds 非整数:int() 抛错后 _goal_rounds_int # 为 None,门不拦,照常跑完(门只拦可解析的超限值)。 _insert_task(self.db_path, "t-cap-bad", status="queued", verify_command="make test", goal_max_rounds="abc") w = self._worker(executor=FakeSync()) result = w.run_one() self.assertEqual(result["status"], "completed") def test_gateway_failure_gate(self): _insert_task(self.db_path, "t-gw", status="queued") w = self._worker(executor=FakeSync()) with mock.patch.dict(os.environ, {"CLOUD_GATEWAY": "sub2api", "SUB2API_BASE_URL": ""}): result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn("网关准备失败", result["error"]) def test_plain_resume_message_swaps_prompt(self): _insert_task(self.db_path, "t-resume", status="queued", resume_message="继续说下去") fake = FakeSync() w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(fake.calls, [("dispatch", "t-resume")]) # 续跑消息已消费:库里 resume_message 清掉。 self.assertEqual(_row( self.db_path, "SELECT resume_message FROM tasks WHERE id = 't-resume'"), [(None,)]) def test_verify_resume_message_is_breakpoint_kind(self): # 旧验收形态 + 续跑消息:prompt 不换、_resume_kind 为 # breakpoint,假执行器走 goal_task 一次即完成。 _insert_task(self.db_path, "t-resume-v", status="queued", verify_command="make test", resume_message="继续原目标") fake = FakeSync() w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(fake.calls, [("goal", "t-resume-v")]) def test_goal_resume_pending_fresh_restart(self): _insert_task(self.db_path, "t-grp", status="queued", goal_mode=1, goal_resume_pending=1) fake = FakeSync() w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(fake.calls, [("goal", "t-grp")]) self.assertEqual(_row( self.db_path, "SELECT goal_resume_pending FROM tasks WHERE id = 't-grp'"), [(0,)]) class RunOneAttemptFlowTest(Worker3Base): def test_completed_simple(self): _insert_task(self.db_path, "t-ok", status="queued") w = self._worker(executor=FakeSync()) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(result["stdout"], "done") self.assertIsNotNone(result["cost_record_id"]) def test_failed_simple_error_text(self): _insert_task(self.db_path, "t-fail", status="queued") fake = FakeSync(dispatch=_result(status="failed", stdout="", stderr="boom-err")) w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn("boom-err", result["error"]) def test_timeout_converts_to_failed(self): _insert_task(self.db_path, "t-timeout", status="queued") fake = FakeSync(dispatch=_result(status="timeout", stdout="")) w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn("task timeout", result["error"]) def test_retry_same_model(self): _insert_task(self.db_path, "t-retry", status="queued") fake = FakeSync(dispatch=_result(status="failed", stdout="", stderr="first-fail")) w = self._worker(executor=fake, max_retries=1, retry_backoff_seconds=0) result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn("[retries=1]", result["error"]) self.assertEqual([c[0] for c in fake.calls], ["dispatch", "dispatch"]) def test_fallback_model_second_failure_combines_errors(self): _insert_task(self.db_path, "t-fb", status="queued") calls = {"n": 0} class _Fb(FakeSync): def dispatch_task(self, task): calls["n"] += 1 if calls["n"] == 1: return _result(status="failed", stdout="", stderr="primary-bad 429", host_status="failed") return _result(status="failed", stdout="", stderr="fallback-bad", host_status="failed") fake = _Fb() w = self._worker(executor=fake, fallback_models=["fb-model"]) result = w.run_one() self.assertEqual(result["status"], "failed") # 真 fallback 换模型再跑一次:两次原因都写进错误文本。 self.assertEqual(calls["n"], 2) self.assertIn("primary model", result["error"]) self.assertIn("fallback model", result["error"]) self.assertIn("fallback-bad", result["error"]) def test_fallback_via_token(self): _insert_task(self.db_path, "t-fb2", status="queued") seq = [("failed", "model overloaded 429"), ("failed", "second bad")] class _Fb2(FakeSync): def __init__(self): super().__init__() self.n = 0 def dispatch_task(self, task): self.n += 1 status, err = seq[min(self.n - 1, len(seq) - 1)] return _result(status=status, stdout="", stderr=err, host_status="failed") fake = _Fb2() w = self._worker(executor=fake, fallback_models=["fb-model"]) result = w.run_one() self.assertEqual(result["status"], "failed") # 真 fallback 走过时错误文本含两次原因(或至少第二次)。 self.assertEqual(fake.n, 2) self.assertIn("fallback model", result["error"]) def test_cancelled_via_result(self): _insert_task(self.db_path, "t-cancel", status="queued") fake = FakeSync(dispatch=_result(status="cancelled", stdout="")) w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "interrupted") self.assertEqual(result["error"], "cancelled") def test_goal_already_exists_falls_back_to_resume(self): # goal 首轮 stdout 含“已存在”:run_one 走一次断点续跑, # 续跑完成即最终完成(不直接 failed)。 _insert_task(self.db_path, "t-goal-exists", status="queued", goal_mode=1) calls = [] class _GoalEx(FakeSync): def goal_task(self, task): calls.append(task["id"]) if len(calls) == 1: return _result(status="failed", stdout="任务已存在") return _result(status="completed", stdout="resumed-ok") w = self._worker(executor=_GoalEx()) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(result["stdout"], "resumed-ok") self.assertEqual(len(calls), 2) def test_goal_met_false_notifies_goal_unmet(self): _insert_task(self.db_path, "t-goal-unmet", status="queued", goal_mode=1) fake = FakeSync(goal=_result(status="completed", stdout="", goal_met=False)) w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "completed") actions = _row(self.db_path, "SELECT action FROM audit_log ORDER BY id") self.assertIn(("worker_completed",), actions) def test_followup_goal_convert_queues_task(self): _insert_task(self.db_path, "t-fu", status="queued") _set(self.db_path, "INSERT INTO task_followups(id, task_id, project_id, body," " status, created_at) VALUES('fu1', 't-fu', 'proj'," " '/goal 收齐周报', 'pending', 1000)") w = self._worker(executor=FakeSync()) result = w.run_one() self.assertEqual(result["status"], "queued") self.assertEqual(_row( self.db_path, "SELECT status FROM tasks WHERE id = 't-fu'"), [("queued",)]) self.assertEqual(_row( self.db_path, "SELECT status FROM task_followups WHERE id = 'fu1'"), [("done",)]) def test_followup_plain_message_runs_second_round(self): _insert_task(self.db_path, "t-fu2", status="queued") _set(self.db_path, "INSERT INTO task_followups(id, task_id, project_id, body," " status, created_at) VALUES('fu2', 't-fu2', 'proj'," " '再补充一点', 'pending', 1000)") fake = FakeSync() w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "completed") self.assertEqual(len(fake.calls), 2) self.assertEqual(_row( self.db_path, "SELECT status FROM task_followups WHERE id = 'fu2'"), [("done",)]) def test_followup_failure_stops_drain(self): _insert_task(self.db_path, "t-fu3", status="queued") _set(self.db_path, "INSERT INTO task_followups(id, task_id, project_id, body," " status, created_at) VALUES('fu3', 't-fu3', 'proj'," " '再补充一点', 'pending', 1000)") class _FuFail(FakeSync): def dispatch_task(self, task): self.calls.append(("dispatch", task["id"])) if len(self.calls) == 1: return _result() return _result(status="failed", stdout="", stderr="fu-bad") w = self._worker(executor=_FuFail()) result = w.run_one() self.assertEqual(result["status"], "failed") self.assertIn("fu-bad", result["error"]) self.assertEqual(_row( self.db_path, "SELECT status FROM task_followups WHERE id = 'fu3'"), [("failed",)]) def test_quota_stop_after_attempt(self): # 首轮返回 quota_exceeded:run_one 走配额中断收尾, # 状态 interrupted、续跑消息保留(本例无消息,只断状态)。 _insert_task(self.db_path, "t-qstop", status="queued") fake = FakeSync(dispatch=_result(status="quota_exceeded", stdout="")) w = self._worker(executor=fake) result = w.run_one() self.assertEqual(result["status"], "interrupted") self.assertIn("quota", result["error"]) self.assertEqual(_row( self.db_path, "SELECT interrupted_reason FROM tasks WHERE id = 't-qstop'"), [("quota",)]) class RunAttemptDirectTest(Worker3Base): def _task_dict(self, tid, **kw): d = {"id": tid, "project_id": "proj", "prompt": "do it", "created_by": "owner1"} d.update(kw) return d def test_dispatch_already_exists_runs_again(self): w = self._worker(executor=FakeSync( dispatch=_result(status="failed", stdout="任务已存在"), run=_result(stdout="run-ok"))) result, error = w._run_attempt(self._task_dict("t-ae")) self.assertEqual(result["status"], "completed") self.assertEqual(result["stdout"], "run-ok") self.assertIsNone(error) def test_executor_raises_becomes_failed(self): class _Boom(FakeSync): def dispatch_task(self, task): raise RuntimeError("runner 炸了") w = self._worker(executor=_Boom()) result, error = w._run_attempt(self._task_dict("t-boom")) self.assertEqual(result["status"], "failed") self.assertEqual(error, "runner 炸了") def test_failed_stdout_tail_and_docker_timeout_marker(self): w = self._worker(executor=FakeSync( dispatch=_result(status="failed", stdout="docker run timeout"))) result, _ = w._run_attempt(self._task_dict("t-dto")) self.assertEqual(result["status"], "timeout") def test_quota_hit_rewrites_to_quota_exceeded(self): w = self._worker(executor=FakeSync()) w._mark_quota_hit("t-qh") result, error = w._run_attempt(self._task_dict("t-qh")) self.assertEqual(result["status"], "quota_exceeded") self.assertIn("配额看门狗", error) def test_goal_breakpoint_unsupported_for_fake_executor(self): w = self._worker(executor=FakeSync()) task = self._task_dict("t-bp", goal_mode=1) task["_goal_resume_breakpoint"] = True result, error = w._run_attempt(task) self.assertEqual(result["status"], "failed") self.assertIn("不支持 goal 断点续跑", error) def test_goal_task_via_fake_executor(self): w = self._worker(executor=FakeSync( goal=_result(stdout="goal-done"))) result, _ = w._run_attempt(self._task_dict("t-goal", goal_mode=1)) self.assertEqual(result["status"], "completed") self.assertEqual(result["stdout"], "goal-done") def test_no_time_limit_task(self): w = self._worker(executor=FakeSync()) result, _ = w._run_attempt(self._task_dict( "t-ntl", no_time_limit=1)) self.assertEqual(result["status"], "completed") class DockerAttemptTest(Worker3Base): def _docker_worker(self, **kw): class _Docker(DockerExecutor): def __init__(self): super().__init__(runner=lambda cmd, timeout=None: None, env={"XCM_API_KEY": "test"}) self.timeout = kw.pop("timeout", None) def container_name(self, task): return "codex-t-" + task["id"] def goal_task(self, task, **_kw): return _result(stdout="docker-goal-ok") def goal_resume_task(self, task, text, **_kw): return _result(stdout="docker-resume-ok") def goal_resume_breakpoint_task(self, task, **_kw): return _result(stdout="docker-bp-ok") def dispatch_task(self, task, **_kw): return _result(stdout="docker-dispatch-ok") def run_task(self, task, **_kw): return _result(stdout="docker-run-ok") def ensure_network(self, name): return True def network_gateway(self, network): return "10.0.0.1" w = self._worker(executor=_Docker(), **kw) return w def _task_dict(self, tid, **kw): d = {"id": tid, "project_id": "proj", "prompt": "do it", "created_by": "owner1"} d.update(kw) return d def test_docker_goal_and_resume_variants(self): w = self._docker_worker() result, _ = w._run_attempt(self._task_dict("t-dg", goal_mode=1)) self.assertEqual(result["stdout"], "docker-goal-ok") task = self._task_dict("t-dr", goal_mode=1) task["_resume"] = True task["_resume_kind"] = "new-goal" result, _ = w._run_attempt(task) self.assertEqual(result["stdout"], "docker-resume-ok") task = self._task_dict("t-dbp", goal_mode=1) task["_goal_resume_breakpoint"] = True result, _ = w._run_attempt(task) self.assertEqual(result["stdout"], "docker-bp-ok") result, _ = w._run_attempt(self._task_dict("t-dd")) self.assertEqual(result["stdout"], "docker-dispatch-ok") def test_docker_context_egress_gateway_failure(self): class _NoGw(DockerExecutor): def __init__(self): super().__init__(runner=lambda cmd, timeout=None: None, env={"XCM_API_KEY": "test"}) def network_gateway(self, network): return None w = self._worker(executor=_NoGw(), egress_proxy_port=8888) result, error = w._run_attempt(self._task_dict("t-dgw")) self.assertEqual(result["status"], "failed") self.assertIn("egress 代理网关解析失败", error) def test_docker_timeout_bad_type_falls_back(self): # executor.timeout 非数字:min() 抛错后回落 task_timeout, # 任务仍正常跑完(不炸 _run_attempt)。 w = self._docker_worker(timeout="bogus") result, _ = w._run_attempt(self._task_dict("t-dbt")) self.assertEqual(result["status"], "completed") class RunLoopPrJobsTest(Worker3Base): def test_run_loop_zero_and_run_all_empty(self): w = self._worker(executor=FakeSync()) self.assertEqual(w.run_loop(max_seconds=0), []) self.assertEqual(w.run_all(), []) def test_run_one_with_explicit_task_dict(self): _insert_task(self.db_path, "t-exp", status="queued") w = self._worker(executor=FakeSync()) task = w.get_task("t-exp") result = w.run_one(task) self.assertEqual(result["status"], "completed") if __name__ == "__main__": unittest.main()