open-swe/tests/test_github_checks.py
Adam Moussa 62d9945df4
refactor: consolidate reviewer modules into agent/review/
Part of the domain-reorg adoption (build plan step C2): fork content,
upstream layout. Nine 1:1 module moves (reviewer_diff/eval_store/
findings/groups/publish/reconcile/trace_context + review_style_
collector/guidance) into agent/review/, with internal relative
imports re-wired to the new package depth. agent/review/__init__.py
mirrors upstream's thin re-export shim (one of the 21 verified "A"
structural adds).

Rewrote the 38 grep hits across importer files (agent/{analyzer,
ci_autofix,reviewer,webapp}.py, agent/dashboard/*, agent/middleware/
settle_review_check.py, agent/tools/*, agent/utils/github_feedback.py,
agent/webhooks/github.py, evals/reviewer/*, and the reviewer test
suite) to point at agent.review.*; 4 of the 38 hits were name
collisions (list_reviewer_findings, reviewer_outcomes,
_reviewer_thread_id, reviewer_thread_id — not the moved modules) and
were left untouched. tests/test_github_checks.py's module-alias
import (`from agent import reviewer_publish`) follows upstream's own
`from agent.review import publish as reviewer_publish` pattern so
downstream `reviewer_publish.*` call sites needed no changes.
agent/reviewer.py and agent/webapp.py stay in place per the hard
rule (fork content, import-only rewire) and are not part of this
package.

Gates: ruff check + ruff format --check, pytest --co -q (1637
collected), full unit suite (1637 passed), and the reviewer/findings
suite in isolation (pytest -k "review or finding", 421 passed).
2026-07-17 13:52:03 -04:00

271 lines
8.5 KiB
Python

from __future__ import annotations
from typing import Any
import httpx
import pytest
from agent.review import publish as reviewer_publish
from agent.utils import github_checks
class _FakeResponse:
def __init__(
self,
payload: dict[str, Any] | None = None,
error: bool = False,
status_code: int = 200,
) -> None:
self._payload = payload or {}
self._error = error
self.status_code = status_code
self.headers: dict[str, str] = {}
def raise_for_status(self) -> None:
if self._error:
raise httpx.HTTPStatusError("forbidden", request=None, response=None) # type: ignore[arg-type]
def json(self) -> dict[str, Any]:
return self._payload
class _FakeAsyncClient:
last_post: dict[str, Any] | None = None
last_patch: dict[str, Any] | None = None
post_response: _FakeResponse = _FakeResponse({"id": 42})
patch_response: _FakeResponse = _FakeResponse({})
def __init__(self, **kwargs: Any) -> None:
pass
async def __aenter__(self) -> _FakeAsyncClient:
return self
async def __aexit__(self, exc_type: object, exc: object, tb: object) -> None:
return None
async def post(self, url: str, **kwargs: Any) -> _FakeResponse:
type(self).last_post = {"url": url, **kwargs}
return type(self).post_response
async def patch(self, url: str, **kwargs: Any) -> _FakeResponse:
type(self).last_patch = {"url": url, **kwargs}
return type(self).patch_response
@pytest.fixture(autouse=True)
def _reset_fake_client() -> None:
_FakeAsyncClient.last_post = None
_FakeAsyncClient.last_patch = None
_FakeAsyncClient.post_response = _FakeResponse({"id": 42})
_FakeAsyncClient.patch_response = _FakeResponse({})
async def test_create_review_check_run_posts_in_progress(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(github_checks.httpx, "AsyncClient", _FakeAsyncClient)
check_run_id = await github_checks.create_review_check_run(
owner="acme",
repo="widgets",
head_sha="abc123",
token="tok",
details_url="https://example.com/thread",
)
assert check_run_id == 42
assert _FakeAsyncClient.last_post is not None
assert _FakeAsyncClient.last_post["url"].endswith("/repos/acme/widgets/check-runs")
body = _FakeAsyncClient.last_post["json"]
assert body["name"] == github_checks.REVIEW_CHECK_RUN_NAME
assert body["head_sha"] == "abc123"
assert body["status"] == "in_progress"
assert body["details_url"] == "https://example.com/thread"
async def test_create_review_check_run_returns_none_on_http_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(github_checks.httpx, "AsyncClient", _FakeAsyncClient)
_FakeAsyncClient.post_response = _FakeResponse(error=True)
check_run_id = await github_checks.create_review_check_run(
owner="acme", repo="widgets", head_sha="abc123", token="tok"
)
assert check_run_id is None
async def test_complete_review_check_run_patches_completed(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(github_checks.httpx, "AsyncClient", _FakeAsyncClient)
ok = await github_checks.complete_review_check_run(
owner="acme",
repo="widgets",
check_run_id=42,
token="tok",
conclusion="neutral",
title="Found 2 potential issues",
summary="…",
)
assert ok is True
assert _FakeAsyncClient.last_patch is not None
assert _FakeAsyncClient.last_patch["url"].endswith("/repos/acme/widgets/check-runs/42")
body = _FakeAsyncClient.last_patch["json"]
assert body["status"] == "completed"
assert body["conclusion"] == "neutral"
assert body["output"]["title"] == "Found 2 potential issues"
async def test_post_autofix_status_check_completes_neutral(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(github_checks.httpx, "AsyncClient", _FakeAsyncClient)
ok = await github_checks.post_autofix_status_check(
owner="acme",
repo="widgets",
head_sha="abc123",
token="tok",
title="Auto-fixing 1 failing check(s)",
summary="working on it",
details_url="https://example.com/thread",
)
assert ok is True
assert _FakeAsyncClient.last_post is not None
assert _FakeAsyncClient.last_post["url"].endswith("/repos/acme/widgets/check-runs")
body = _FakeAsyncClient.last_post["json"]
assert body["name"] == github_checks.AUTOFIX_CHECK_RUN_NAME
assert body["status"] == "completed"
assert body["conclusion"] == "neutral"
assert body["details_url"] == "https://example.com/thread"
def test_review_check_conclusion_mapping() -> None:
conclusion, title, _ = github_checks.review_check_conclusion(0)
assert conclusion == "success"
assert title == "No issues found"
conclusion, title, _ = github_checks.review_check_conclusion(1)
assert conclusion == "success"
assert "1 potential issue" in title
conclusion, title, _ = github_checks.review_check_conclusion(3)
assert conclusion == "success"
assert "3 potential issues" in title
async def test_settle_review_check_run_noop_without_tracked_id(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def fake_get_thread_metadata(thread_id: str) -> dict[str, Any]:
return {}
completed: list[dict[str, Any]] = []
async def fake_complete(**kwargs: Any) -> bool:
completed.append(kwargs)
return True
monkeypatch.setattr(reviewer_publish, "get_thread_metadata", fake_get_thread_metadata)
monkeypatch.setattr(reviewer_publish, "complete_review_check_run", fake_complete)
await reviewer_publish.settle_review_check_run(
thread_id="t1",
owner="acme",
repo="widgets",
token="tok",
conclusion="success",
title="t",
summary="s",
)
assert completed == []
async def test_settle_review_check_run_completes_and_clears(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def fake_get_thread_metadata(thread_id: str) -> dict[str, Any]:
return {"review_check_run_id": 42}
completed: list[dict[str, Any]] = []
metadata_writes: list[dict[str, Any]] = []
async def fake_complete(**kwargs: Any) -> bool:
completed.append(kwargs)
return True
async def fake_set_metadata(thread_id: str, **kwargs: Any) -> None:
metadata_writes.append({"thread_id": thread_id, **kwargs})
monkeypatch.setattr(reviewer_publish, "get_thread_metadata", fake_get_thread_metadata)
monkeypatch.setattr(reviewer_publish, "complete_review_check_run", fake_complete)
monkeypatch.setattr(reviewer_publish, "set_reviewer_thread_metadata", fake_set_metadata)
await reviewer_publish.settle_review_check_run(
thread_id="t1",
owner="acme",
repo="widgets",
token="tok",
conclusion="neutral",
title="t",
summary="s",
)
assert len(completed) == 1
assert completed[0]["check_run_id"] == 42
assert completed[0]["conclusion"] == "neutral"
assert metadata_writes == [
{
"thread_id": "t1",
"extra": {"review_check_run_id": None, "review_check_pending_result": None},
}
]
async def test_settle_review_check_run_keeps_id_on_patch_failure(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def fake_get_thread_metadata(thread_id: str) -> dict[str, Any]:
return {"review_check_run_id": 42}
metadata_writes: list[dict[str, Any]] = []
async def fake_complete(**kwargs: Any) -> bool:
return False
async def fake_set_metadata(thread_id: str, **kwargs: Any) -> None:
metadata_writes.append({"thread_id": thread_id, **kwargs})
monkeypatch.setattr(reviewer_publish, "get_thread_metadata", fake_get_thread_metadata)
monkeypatch.setattr(reviewer_publish, "complete_review_check_run", fake_complete)
monkeypatch.setattr(reviewer_publish, "set_reviewer_thread_metadata", fake_set_metadata)
await reviewer_publish.settle_review_check_run(
thread_id="t1",
owner="acme",
repo="widgets",
token="tok",
conclusion="success",
title="t",
summary="s",
)
assert metadata_writes == [
{
"thread_id": "t1",
"extra": {
"review_check_pending_result": {
"conclusion": "success",
"title": "t",
"summary": "s",
}
},
}
]