fix(gate-check-v3): defend against user=null in review JSON #1862
@@ -264,11 +264,18 @@ def signal_2_reviews(pr_number: int, repo: str) -> dict:
|
||||
|
||||
blocking = []
|
||||
for r in reviews:
|
||||
if r.get("state") == "REQUEST_CHANGES" and not r.get("dismissed", False):
|
||||
if (
|
||||
r.get("state") == "REQUEST_CHANGES"
|
||||
and not r.get("dismissed", False)
|
||||
and r.get("official") is not False
|
||||
):
|
||||
login = (r.get("user") or {}).get("login", "")
|
||||
if not login:
|
||||
continue
|
||||
blocking.append(
|
||||
{
|
||||
"review_id": r["id"],
|
||||
"user": r["user"]["login"],
|
||||
"user": login,
|
||||
"commit_id": r.get("commit_id", ""),
|
||||
"created_at": r.get("submitted_at") or r.get("created_at", ""),
|
||||
}
|
||||
|
||||
@@ -119,3 +119,55 @@ def test_signal_1_null_user_in_review_does_not_crash(monkeypatch):
|
||||
# Should not crash; the valid review from core-devops still satisfies engineers gate
|
||||
assert result["verdict"] == "CLEAR"
|
||||
assert result["results"]["core-devops"]["verdict"] == "APPROVED"
|
||||
|
||||
|
||||
def test_signal_2_draft_request_changes_does_not_block(monkeypatch):
|
||||
"""official=False REQUEST_CHANGES is a draft/pending review and must NOT
|
||||
block the gate (matching review-check.sh post-#1818 official-filter)."""
|
||||
mod = load_gate_check()
|
||||
|
||||
def fake_api_list(path):
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/902/reviews":
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"user": {"login": "agent-reviewer"},
|
||||
"state": "REQUEST_CHANGES",
|
||||
"official": False,
|
||||
"dismissed": False,
|
||||
"submitted_at": "2026-05-13T10:00:00Z",
|
||||
}
|
||||
]
|
||||
raise AssertionError(f"unexpected api_list: {path}")
|
||||
|
||||
monkeypatch.setattr(mod, "api_list", fake_api_list)
|
||||
|
||||
result = mod.signal_2_reviews(902, "molecule-ai/molecule-core")
|
||||
assert result["verdict"] == "CLEAR"
|
||||
assert result["blocking_reviews"] == []
|
||||
|
||||
|
||||
def test_signal_2_null_user_in_request_changes_does_not_crash(monkeypatch):
|
||||
"""Regression: Gitea may return user=null on a REQUEST_CHANGES review.
|
||||
signal_2_reviews must survive this without AttributeError."""
|
||||
mod = load_gate_check()
|
||||
|
||||
def fake_api_list(path):
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/903/reviews":
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"user": None,
|
||||
"state": "REQUEST_CHANGES",
|
||||
"official": True,
|
||||
"dismissed": False,
|
||||
"submitted_at": "2026-05-13T10:00:00Z",
|
||||
}
|
||||
]
|
||||
raise AssertionError(f"unexpected api_list: {path}")
|
||||
|
||||
monkeypatch.setattr(mod, "api_list", fake_api_list)
|
||||
|
||||
result = mod.signal_2_reviews(903, "molecule-ai/molecule-core")
|
||||
assert result["verdict"] == "CLEAR"
|
||||
assert result["blocking_reviews"] == []
|
||||
|
||||
Reference in New Issue
Block a user