Files
agentci/tests/test_code_review.py
T

129 lines
3.3 KiB
Python

from pathlib import Path
from types import SimpleNamespace
from agentci.domain.models import (
AgentResult,
Job,
JobKind,
ReviewFinding,
ReviewReport,
ReviewSeverity,
Workflow,
WorkflowKind,
)
from agentci.workflows.code_review import CodeReviewLoop
def serious_report() -> ReviewReport:
return ReviewReport(
summary="Needs work",
findings=[
ReviewFinding(
severity=ReviewSeverity.MAJOR,
title="Missing check",
detail="A check is absent.",
recommendation="Add it.",
)
],
)
class FakeOpenCode:
def __init__(self, reports: list[ReviewReport]) -> None:
self.reports = iter(reports)
self.reviews = 0
self.revisions = 0
async def create_session(self, *_args):
return "reviewer"
async def resume(self, **kwargs):
if kwargs["result_type"] is ReviewReport:
self.reviews += 1
return next(self.reports)
self.revisions += 1
return AgentResult(summary_markdown=f"revision {self.revisions}", tests=[])
class FakeStorage:
async def update_job(self, *_args, **_kwargs):
return None
async def update_workflow(self, *_args, **_kwargs):
return None
class FakePrompts:
def render(self, name, **_kwargs):
return name
def objects(rounds: int, reports: list[ReviewReport]):
opencode = FakeOpenCode(reports)
settings = SimpleNamespace(
implement_review_rounds=rounds,
implement_model="model",
implement_variant="high",
)
deps = SimpleNamespace(
settings=settings,
opencode=opencode,
storage=FakeStorage(),
prompts=FakePrompts(),
development=SimpleNamespace(description="python"),
)
workflow = Workflow(
id="flow",
kind=WorkflowKind.IMPLEMENT,
repo_owner="org",
repo_name="repo",
issue_number=1,
workspace_path=Path("."),
base_sha="abc",
primary_session_id="primary",
)
job = Job(
id="job",
kind=JobKind.IMPLEMENT,
target_key="target",
repo_owner="org",
repo_name="repo",
issue_number=1,
pr_number=None,
requester="alice",
message="",
comment_id=1,
)
return CodeReviewLoop(deps), opencode, workflow, job # type: ignore[arg-type]
async def test_stops_after_clean_second_review() -> None:
clean = ReviewReport(summary="Ready", findings=[])
loop, opencode, workflow, job = objects(4, [serious_report(), clean])
_, report = await loop.run(
job,
workflow,
"issue context",
"canonical plan",
AgentResult(summary_markdown="initial", tests=[]),
)
assert not report.has_serious_findings
assert opencode.reviews == 2
assert opencode.revisions == 1
async def test_does_not_make_unreviewed_final_revision() -> None:
loop, opencode, workflow, job = objects(
3, [serious_report(), serious_report(), serious_report()]
)
_, report = await loop.run(
job,
workflow,
"issue context",
"canonical plan",
AgentResult(summary_markdown="initial", tests=[]),
)
assert report.has_serious_findings
assert opencode.reviews == 3
assert opencode.revisions == 2