gapfill2: worker/sub90 coverage tests, G7 rollback branch, G5 caller-contract docs

H1 worker.py 92%->94% (277->210 miss): 35 real-assertion tests
covering goal-signal, tenant/env, followup, poll/wait,
process/watchdog and injected defensive branches; prod code 0 changes.
H2 sub-90 sweep: approval_timeout 100%, merge_gate_hard 100%,
worker_sweep 98% (only __main__ line 174 left); 14 tests.
H3 G7 rollback except:pass (server.py:6615-6616) covered by an
injected rollback-raising connection; original error still propagates,
status stays completed, retry succeeds; no semantics change.
H4 caller contract documented only (docstrings in _audit_scorecard
endpoint, check_baseline_stale, build_scorecard, baseline store):
current_main_head caller-supplied, any string as-is, no format checks,
missing/empty treated as not passed with no stale determination at the
endpoint, always 200, overall never rewritten; no new validation or
rejection, no response-structure change. 3 doc tests pin the wording.
H5 design notes (no decisions, no code changes) live in
verification/code-to10-gapfill2-design-notes.md, referenced by the
batch summary.
No bug found by the new tests; no existing assertion touched.
This commit is contained in:
host committed 2026-11-04 16:20:10 +00:00
1 parent 5d0651e70a
commit aba06d9a24
7 files changed
+1062 -2

No files matched your search

+163
View File
@@ -0,0 +1,163 @@
"""gapfill2 H3:_merge_task 成功审计段里 rollback 自身的 except: pass。
上一批唯一未覆盖分支:G7 新段里 ``conn.rollback()`` 本身抛错时的
防御分支(基底行号 server.py:6615-6616)。rollback 从不失败,故
全量覆盖率里该分支一直缺失。本文件只用注入式测试覆盖它,绝不
改动 rollback 语义、绝不把该分支改成可观测行为:
- 注入一个 rollback 会抛错的连接代理(除 rollback 外全部透传
真实连接),同时让审计失败(沿用 G7 的 RuntimeError 注入),
使执行流必然走进 ``except Exception: pass``;
- 断言:rollback 的异常被吞掉(原审计错误原样上抛,不被掩盖)、
任务状态仍是 completed、审计 0 行、原样重试后成功 200。
真实模型 0 轮,不联网;不跑真实 HTTP(handler 直调 +
真实 AuthStore/DB,审计行直接查库验证)。
"""
import os
import sqlite3
import sys
import tempfile
import time
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(REPO_ROOT))
import cloud.server as server_mod # noqa: E402
from cloud.auth import AuthStore # noqa: E402
from cloud.server import CloudControlPlaneHandler, init_db # noqa: E402
NO_BLOCK = {"merge_blocked": False, "release_blocked": False,
"blocked_by": None, "round": None, "failed_checks": []}
HARD_BODY = {
"files_changed": ["apps/web/a.py"],
"owned_dir": "apps/web",
"branch_protection": "protected",
"ci_status": "success",
"ci_checks": [{"name": "ci", "status": "success"}],
"required_checks": ["ci"],
"contract_version": "v2",
"contract_frozen": True,
"coordinator_approved": True,
"unresolved_review_comments": 0,
}
class _RollbackFailingConn:
"""代理真实连接:除 rollback 外全部透传,rollback 计数后抛错。
execute / commit / close / row_factory 等一律委托给真实连接,
只有 rollback 被替换为“计数 + 抛错”,精确模拟“rollback 自身
失败”这一防御分支的前提,不改动其它任何连接语义。
"""
def __init__(self, real, calls):
self._real = real
self._calls = calls
def rollback(self):
self._calls["n"] += 1
raise RuntimeError("rollback down")
def __getattr__(self, name):
return getattr(self._real, name)
class RollbackFailureSwallowedTestCase(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
self.auth = AuthStore(self.db_path)
self.owner_id, _key = self.auth.create_user("owner")
conn = sqlite3.connect(self.db_path)
try:
conn.execute(
"INSERT INTO projects(id, name, owner_id, created_at,"
" repo_url, auto_pr) VALUES(?, ?, ?, ?, ?, 0)",
("p1", "P", self.owner_id, time.time(), None))
conn.execute(
"INSERT INTO tasks(id, project_id, prompt, status,"
" created_by, created_at, updated_at)"
" VALUES(?, ?, ?, ?, ?, ?, ?)",
("t1", "p1", "do work", "completed", self.owner_id,
time.time(), time.time()))
conn.commit()
finally:
conn.close()
self.auth.create_project("p1", self.owner_id)
def tearDown(self):
try:
self.auth.conn.close()
except Exception:
pass
self.tmp.cleanup()
def _handler(self):
h = CloudControlPlaneHandler.__new__(CloudControlPlaneHandler)
h.server = SimpleNamespace(db_path=self.db_path)
sent = []
h._send_json = lambda status, obj, extra_headers=None: sent.append(
(status, obj))
h._read_json = lambda: (dict(HARD_BODY), None)
h.sent = sent
return h
def _merge(self, h):
with mock.patch.object(server_mod, "_pushback_block",
return_value=dict(NO_BLOCK)):
h._merge_task(self.owner_id, "t1")
def _status(self):
conn = sqlite3.connect(self.db_path)
try:
return conn.execute(
"SELECT status FROM tasks WHERE id = 't1'").fetchone()[0]
finally:
conn.close()
def _merge_audits(self):
return [a for a in self.auth.list_audit("p1")
if a["action"] == "merge_task"]
def test_rollback_failure_is_swallowed(self):
# 审计失败 + rollback 自身失败:rollback 的异常必须被吞掉,
# 原审计错误原样上抛(不被 rollback 错误掩盖),状态回滚
# 语义不变(completed、审计 0 行),原样重试后成功。
h = self._handler()
real_db = CloudControlPlaneHandler._db
calls = {"n": 0}
def failing_db(handler_self):
return _RollbackFailingConn(real_db(handler_self), calls)
with mock.patch.object(CloudControlPlaneHandler, "_db",
failing_db):
with mock.patch.object(
server_mod, "_audit_on_conn",
side_effect=RuntimeError("audit down")):
with self.assertRaises(RuntimeError) as ctx:
self._merge(h)
self.assertEqual(str(ctx.exception), "audit down")
self.assertEqual(calls["n"], 1)
self.assertEqual(h.sent, [])
self.assertEqual(self._status(), "completed")
self.assertEqual(self._merge_audits(), [])
# 原样重试(审计恢复、连接恢复后)必须成功:状态仍是
# completed,不是卡在 merged 导致重复合并 409。
h2 = self._handler()
self._merge(h2)
self.assertEqual(h2.sent[-1][0], 200)
self.assertEqual(self._status(), "merged")
self.assertEqual(len(self._merge_audits()), 1)
if __name__ == "__main__":
unittest.main()