139 lines
4.4 KiB
Python
139 lines
4.4 KiB
Python
import json
|
|
|
|
import pytest
|
|
|
|
from agentci.workflows.model import (
|
|
AgentResult,
|
|
ReviewFinding,
|
|
ReviewReport,
|
|
ReviewSeverity,
|
|
)
|
|
from agentci.workflows.render import (
|
|
agent_comment,
|
|
commit_title,
|
|
final_comment,
|
|
pull_request_body,
|
|
report_for_prompt,
|
|
required_session,
|
|
result_comment,
|
|
review_markdown,
|
|
)
|
|
|
|
|
|
def report() -> ReviewReport:
|
|
return ReviewReport(
|
|
summary="One issue remains.",
|
|
findings=[
|
|
ReviewFinding(
|
|
severity=ReviewSeverity.MAJOR,
|
|
title="Missing validation",
|
|
detail="The empty input is not checked.",
|
|
location="src/widget.py:12",
|
|
recommendation="Reject empty input.",
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
def test_review_markdown_renders_user_visible_finding() -> None:
|
|
assert review_markdown(report()) == (
|
|
"## Remaining review findings\n\n"
|
|
"One issue remains.\n\n"
|
|
"### MAJOR: Missing validation \u2014 `src/widget.py:12`\n"
|
|
"The empty input is not checked.\n\n"
|
|
"Recommendation: Reject empty input."
|
|
)
|
|
assert review_markdown(ReviewReport(summary="Ready", findings=[])) == ""
|
|
|
|
|
|
def test_agent_and_final_comments_preserve_protocol_marker() -> None:
|
|
assert agent_comment("plan", "flow-1", "Plan body") == (
|
|
"<!-- agentci:plan workflow=flow-1 -->\nPlan body"
|
|
)
|
|
assert final_comment("plan", "flow-1", "Plan body", report()) == (
|
|
f"<!-- agentci:plan workflow=flow-1 -->\nPlan body\n\n{review_markdown(report())}"
|
|
)
|
|
assert (
|
|
final_comment("plan", "flow-1", "Plan body", ReviewReport(summary="Ready", findings=[]))
|
|
== "<!-- agentci:plan workflow=flow-1 -->\nPlan body"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("issue_number", "result", "sha", "expected_body", "expected_comment"),
|
|
[
|
|
pytest.param(
|
|
17,
|
|
AgentResult(
|
|
summary_markdown="Implemented the widget fix.",
|
|
tests=["pytest: passed", "ruff: passed"],
|
|
),
|
|
"abc123",
|
|
(
|
|
"Closes #17\n\n"
|
|
"## Implementation\n\nImplemented the widget fix.\n\n"
|
|
"## Validation\n\n- pytest: passed\n- ruff: passed\n\n"
|
|
"_Created by Agent CI._"
|
|
),
|
|
(
|
|
"## Agent result\n\nImplemented the widget fix.\n\n"
|
|
"## Validation\n\n- pytest: passed\n- ruff: passed\n\n"
|
|
"Commit: `abc123`"
|
|
),
|
|
id="reported-validation",
|
|
),
|
|
pytest.param(
|
|
2,
|
|
AgentResult(summary_markdown="Applied the change.", tests=[]),
|
|
None,
|
|
(
|
|
"Closes #2\n\n"
|
|
"## Implementation\n\nApplied the change.\n\n"
|
|
"## Validation\n\n- Not reported\n\n"
|
|
"_Created by Agent CI._"
|
|
),
|
|
"## Agent result\n\nApplied the change.\n\n## Validation\n\n- Not reported",
|
|
id="missing-validation",
|
|
),
|
|
],
|
|
)
|
|
def test_result_renderers_render_reported_and_missing_validation(
|
|
issue_number: int,
|
|
result: AgentResult,
|
|
sha: str | None,
|
|
expected_body: str,
|
|
expected_comment: str,
|
|
) -> None:
|
|
assert pull_request_body(issue_number, result) == expected_body
|
|
assert result_comment(result, sha=sha) == expected_comment
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("markdown", "expected"),
|
|
[
|
|
("# Fix widget\n\nDetails", "Fix widget"),
|
|
("\n## Trim heading \n", "Trim heading"),
|
|
("\n\t\n", "apply requested changes"),
|
|
("x" * 80, "x" * 72),
|
|
],
|
|
)
|
|
def test_commit_title_uses_first_content_line(markdown: str, expected: str) -> None:
|
|
assert commit_title(markdown) == expected
|
|
|
|
|
|
def test_report_for_prompt_formats_json_and_preserves_unstructured_text() -> None:
|
|
stored = '{"summary":"Ready","findings":[]}'
|
|
|
|
formatted = report_for_prompt(stored)
|
|
|
|
assert json.loads(formatted) == {"summary": "Ready", "findings": []}
|
|
assert formatted == '{\n "summary": "Ready",\n "findings": []\n}'
|
|
assert report_for_prompt("plain text review") == "plain text review"
|
|
assert report_for_prompt(None) == "(none)"
|
|
|
|
|
|
def test_required_session_returns_value_or_fails_fast() -> None:
|
|
assert required_session("session-1") == "session-1"
|
|
with pytest.raises(RuntimeError, match="Expected a persisted OpenCode session ID"):
|
|
required_session(None)
|