Files
pragent/tests/pilot/test_ai_review.py
T
Marcos 6f012e9b66 feat(pilot): minimal AI review bot for Gitea Actions
Ships a working pragent pilot ahead of the framework build (design doc
deferred). Single stdlib-only reviewer script fetched at runtime by a per-repo
Gitea Action; reviews fire only on PRs with the AI-REVIEW label; model is
glm-5.2:cloud via the on-network headroom proxy; fail-open.

- pilot/ai_review.py: fetch PR diff, call model, post review as pragent-bot
- pilot/workflow-template.yml: per-repo Gitea Action gated on AI-REVIEW label
- pilot/README.md: onboarding (bot collaborator + secret + workflow + label)
- tests/pilot/test_ai_review.py: 15 unit tests for pure helpers (no network)
- README/design doc: note pilot is the bootstrap; framework build deferred

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-17 18:18:06 +00:00

133 lines
4.1 KiB
Python

"""Unit tests for pragent pilot pure helpers. No network."""
import os
import sys
# Allow running without install: add repo root to path.
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
sys.path.insert(0, os.path.join(ROOT, "pilot"))
from ai_review import ( # noqa: E402
build_user_prompt,
format_review_body,
parse_text_blocks,
truncate_diff,
)
# ---------------------------------------------------------------------------
# truncate_diff
# ---------------------------------------------------------------------------
def test_truncate_diff_short():
text, truncated, n = truncate_diff("abc", 100)
assert text == "abc"
assert truncated is False
assert n == 3
def test_truncate_diff_exact_boundary():
text, truncated, n = truncate_diff("x" * 100, 100)
assert truncated is False
assert n == 100
assert text == "x" * 100
def test_truncate_diff_over_cap():
text, truncated, n = truncate_diff("x" * 250, 100)
assert truncated is True
assert n == 250
assert text.startswith("x" * 100)
assert "[diff truncated at 100 characters]" in text
def test_truncate_diff_none():
text, truncated, n = truncate_diff(None, 100) # type: ignore[arg-type]
assert text == ""
assert truncated is False
assert n == 0
# ---------------------------------------------------------------------------
# parse_text_blocks
# ---------------------------------------------------------------------------
def test_parse_text_blocks_text_only():
content = [{"type": "text", "text": "hello"}, {"type": "text", "text": "world"}]
assert parse_text_blocks(content) == "hello\nworld"
def test_parse_text_blocks_drops_thinking():
content = [
{"type": "thinking", "thinking": "reasoning here"},
{"type": "text", "text": "- [high] a.go:3 — bug. fix."},
]
assert parse_text_blocks(content) == "- [high] a.go:3 — bug. fix."
def test_parse_text_blocks_empty_and_malformed():
assert parse_text_blocks([]) == ""
assert parse_text_blocks(None) == "" # type: ignore[arg-type]
assert parse_text_blocks([{"type": "text"}, "garbage", 5]) == ""
def test_parse_text_blocks_real_glm_shape():
# Captured from glm-5.2:cloud via headroom 8789.
content = [
{"type": "thinking", "thinking": "Analyze the request..."},
{"type": "text", "text": "- [critical] auth.py:12 — token compared with `==`. Use hmac.compare_digest."},
]
assert "compare_digest" in parse_text_blocks(content)
# ---------------------------------------------------------------------------
# format_review_body
# ---------------------------------------------------------------------------
def test_format_review_body_findings():
body = format_review_body("- [high] x:1 — bug. fix.", "glm-5.2:cloud", "abcdef1234567890")
assert "pragent pilot" in body
assert "glm-5.2:cloud" in body
assert "`abcdef12`" in body # 8-char sha
assert "- [high] x:1" in body
def test_format_review_body_empty_findings():
body = format_review_body("", "glm-5.2:cloud", "abcdef1234567890")
assert "No issues found." in body
def test_format_review_body_whitespace_findings():
body = format_review_body(" \n ", "glm-5.2:cloud", "abcdef1234567890")
assert "No issues found." in body
def test_format_review_body_no_sha():
body = format_review_body("- [low] y:2 — nit", "glm-5.2:cloud", "")
assert "`unknown`" in body
# ---------------------------------------------------------------------------
# build_user_prompt
# ---------------------------------------------------------------------------
def test_build_user_prompt_includes_title_and_diff():
p = build_user_prompt("Fix login", "Closes #1", "diff --git a/x b/x")
assert "Fix login" in p
assert "Closes #1" in p
assert "diff --git a/x b/x" in p
def test_build_user_prompt_truncates_long_body():
long_body = "B" * 6000
p = build_user_prompt("t", long_body, "d")
assert "[PR body truncated]" in p
assert p.count("B") < 6000
def test_build_user_prompt_no_body():
p = build_user_prompt("t", "", "d")
assert "Description:" not in p