open-swe/tests/agent/test_agent_usage.py

219 lines
8 KiB
Python
Raw Normal View History

from __future__ import annotations
import pytest
from agent.dashboard import agent_usage
class FakeStore:
def __init__(self, values: dict[tuple[tuple[str, ...], str], dict] | None = None):
self.values = values or {}
self.puts: list[tuple[list[str], str, dict]] = []
async def get_item(self, namespace: list[str], key: str) -> dict | None:
value = self.values.get((tuple(namespace), key))
return {"value": value} if value is not None else None
async def put_item(self, namespace: list[str], key: str, value: dict) -> None:
self.puts.append((namespace, key, value))
self.values[(tuple(namespace), key)] = value
class FakeThreads:
def __init__(self, threads: list[dict]):
self.threads = threads
self.calls: list[dict] = []
async def search(self, **kwargs) -> list[dict]:
self.calls.append(kwargs)
offset = kwargs.get("offset") or 0
limit = kwargs.get("limit") or len(self.threads)
return self.threads[offset : offset + limit]
class FakeClient:
def __init__(self, *, store: FakeStore | None = None, threads: FakeThreads | None = None):
self.store = store or FakeStore()
self.threads = threads or FakeThreads([])
@pytest.mark.asyncio
async def test_cached_usage_payload_returns_stale_snapshot_and_schedules_refresh(monkeypatch):
usage_snapshot = {
"period": "30d",
"total_members": 2,
"users": [
{
"rank": 1,
"key": "github:octo",
"name": "octo",
"github_login": "octo",
"email": "octo@example.com",
"favorite_model": "claude",
"agent_runs": 3,
"prs_opened": 2,
"merged_prs": 1,
"agent_loc": 10,
"additions": 8,
"deletions": 2,
},
{
"rank": 2,
"key": "email:private@example.com",
"name": "private",
"github_login": None,
"email": "private@example.com",
"favorite_model": "default",
"agent_runs": 1,
"prs_opened": 0,
"merged_prs": 0,
"agent_loc": 0,
"additions": 0,
"deletions": 0,
},
],
}
reviewer_snapshot = {
"period": "30d",
"reviewed_prs": 1,
"prs_with_findings": 1,
"findings_recorded": 1,
"surfaced_findings": 1,
"addressed_findings": 0,
"resolved_after_update": 0,
"dismissed_findings": 0,
"unresolved_surfaced_findings": 1,
"resolution_rate": 0.0,
"human_replies": 0,
"severity_counts": {"medium": 1},
"top_categories": [{"name": "correctness", "count": 1}],
}
store = FakeStore(
{
(tuple(agent_usage.USAGE_LEADERBOARD_CACHE_NAMESPACE), "30d"): {
"generated_at_ms": 1,
"snapshot": usage_snapshot,
},
(tuple(agent_usage.REVIEWER_STATS_CACHE_NAMESPACE), "30d"): {
"generated_at_ms": 1,
"snapshot": reviewer_snapshot,
},
}
)
monkeypatch.setattr(agent_usage, "_client", lambda: FakeClient(store=store))
monkeypatch.setattr(agent_usage, "_now_ms", lambda: agent_usage._CACHE_TTL_MS + 2)
usage_refreshes: list[str] = []
reviewer_refreshes: list[str] = []
payload = await agent_usage.list_agent_usage_leaderboard(
period="30d",
limit=1,
current_login="octo",
current_email="octo@example.com",
schedule_usage_refresh=usage_refreshes.append,
schedule_reviewer_refresh=reviewer_refreshes.append,
)
assert usage_refreshes == ["30d"]
assert reviewer_refreshes == ["30d"]
assert payload["rows"][0]["user"]["email"] == "octo@example.com"
assert payload["total_members"] == 2
assert payload["reviewer_stats"]["surfaced_findings"] == 1
assert store.puts == []
@pytest.mark.asyncio
async def test_reviewer_stats_snapshot_counts_surfaced_and_resolved_findings(monkeypatch):
threads = [
{
"created_at": "2025-01-01T00:00:00Z",
"metadata": {
"kind": "reviewer",
"head_sha": "fixed-sha",
"pr": {"owner": "langchain-ai", "name": "open-swe", "number": 1},
"findings": [
{
"id": "f_1",
"status": "resolved",
"severity": "high",
"category": "correctness",
"first_seen_sha": "buggy-sha",
"github_thread_resolved": True,
"github_review_comment_id": 10,
"resolution_note": "Fixed in a follow-up commit.",
"interactions": [{"kind": "human_reply"}],
},
{
"id": "f_2",
"status": "open",
"severity": "medium",
"category": "performance",
"github_review_comment_id": 11,
},
{
"id": "f_3",
"status": "dismissed",
"severity": "low",
"category": "style",
"github_review_id": 12,
},
],
},
}
]
monkeypatch.setattr(agent_usage, "_client", lambda: FakeClient(threads=FakeThreads(threads)))
snapshot = await agent_usage._build_reviewer_stats_snapshot("all")
assert snapshot["reviewed_prs"] == 1
assert snapshot["prs_with_findings"] == 1
assert snapshot["findings_recorded"] == 3
assert snapshot["surfaced_findings"] == 3
assert snapshot["addressed_findings"] == 1
assert snapshot["resolved_after_update"] == 1
assert snapshot["dismissed_findings"] == 1
assert snapshot["unresolved_surfaced_findings"] == 1
assert snapshot["human_replies"] == 1
assert snapshot["severity_counts"] == {"high": 1, "medium": 1, "low": 1}
assert snapshot["top_categories"] == [
{"name": "correctness", "count": 1},
{"name": "performance", "count": 1},
{"name": "style", "count": 1},
]
@pytest.mark.asyncio
async def test_reviewer_stats_paginates_reviewer_threads(monkeypatch):
threads = [
{"created_at": "2025-01-03T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
{"created_at": "2025-01-02T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
{"created_at": "2025-01-01T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
]
fake_threads = FakeThreads(threads)
monkeypatch.setattr(agent_usage, "_CACHE_SEARCH_LIMIT", 2)
monkeypatch.setattr(agent_usage, "_client", lambda: FakeClient(threads=fake_threads))
snapshot = await agent_usage._build_reviewer_stats_snapshot("all")
assert snapshot["reviewed_prs"] == 3
assert [call["offset"] for call in fake_threads.calls] == [0, 2]
assert all(call["sort_by"] == "created_at" for call in fake_threads.calls)
@pytest.mark.asyncio
async def test_reviewer_stats_stops_after_page_older_than_cutoff(monkeypatch):
threads = [
{"created_at": "2025-01-03T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
{"created_at": "2025-01-01T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
{"created_at": "2024-12-31T00:00:00Z", "metadata": {"kind": "reviewer", "findings": []}},
]
fake_threads = FakeThreads(threads)
monkeypatch.setattr(agent_usage, "_CACHE_SEARCH_LIMIT", 2)
monkeypatch.setattr(agent_usage, "_client", lambda: FakeClient(threads=fake_threads))
cutoff_ms = agent_usage._timestamp_ms("2025-01-02T00:00:00Z")
pages = [page async for page in agent_usage._iter_reviewer_thread_pages(cutoff_ms)]
assert len(pages) == 1
assert [call["offset"] for call in fake_threads.calls] == [0]