Expose /audit and /api/audit for local validator previews using final_report_validator and review schema checks. Operators can paste reports, auto-detect task kind, and get structured findings with suggested next prompts and issue-comment drafts. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
"""Tests for web UI report audit paste (#431)."""
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from starlette.testclient import TestClient
|
|
|
|
from webui.app import create_app
|
|
from webui.audit_validator import audit_report, infer_task_kind
|
|
|
|
|
|
_MINIMAL_REVIEW_REPORT = """## Controller Handoff
|
|
|
|
- Task: review PR #1
|
|
- Repo: Scaled-Tech-Consulting/Gitea-Tools
|
|
- Role: reviewer
|
|
- Identity: sysadmin / prgs-reviewer
|
|
- Review decision: approve
|
|
"""
|
|
|
|
|
|
class TestAuditValidator(unittest.TestCase):
|
|
def test_infer_task_kind_from_review_report(self):
|
|
self.assertEqual(infer_task_kind(_MINIMAL_REVIEW_REPORT), "review_pr")
|
|
|
|
def test_infer_task_kind_explicit_override(self):
|
|
self.assertEqual(
|
|
infer_task_kind(_MINIMAL_REVIEW_REPORT, explicit="work_issue"),
|
|
"work_issue",
|
|
)
|
|
|
|
def test_empty_report_blocked(self):
|
|
result = audit_report("")
|
|
self.assertTrue(result["blocked"])
|
|
self.assertIn("empty", result["reasons"][0].lower())
|
|
|
|
def test_minimal_report_produces_findings(self):
|
|
result = audit_report(_MINIMAL_REVIEW_REPORT)
|
|
self.assertIn(result["grade"], {"blocked", "downgraded", "warning", "A"})
|
|
self.assertTrue(result["findings"] or result["blocked"] or result["downgraded"])
|
|
|
|
|
|
class TestAuditRoutes(unittest.TestCase):
|
|
def setUp(self):
|
|
self.client = TestClient(create_app())
|
|
|
|
def test_audit_page_renders_form(self):
|
|
response = self.client.get("/audit")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("Report audit", response.text)
|
|
self.assertIn('name="report_text"', response.text)
|
|
self.assertIn("Run validator preview", response.text)
|
|
self.assertNotIn("child issue", response.text.lower())
|
|
|
|
def test_audit_post_runs_validator(self):
|
|
response = self.client.post(
|
|
"/audit",
|
|
data={"report_text": _MINIMAL_REVIEW_REPORT, "task_kind": "review_pr"},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("Validator preview", response.text)
|
|
self.assertIn("Safe next action", response.text)
|
|
|
|
def test_api_audit_post_json(self):
|
|
response = self.client.post(
|
|
"/api/audit",
|
|
json={"report_text": _MINIMAL_REVIEW_REPORT, "task_kind": "review_pr"},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertEqual(data["task_kind"], "review_pr")
|
|
self.assertIn("grade", data)
|
|
self.assertIn("findings", data)
|
|
self.assertIn("suggested_next_prompt", data)
|
|
|
|
def test_api_audit_rejects_empty_body(self):
|
|
response = self.client.post("/api/audit", json={"report_text": ""})
|
|
self.assertEqual(response.status_code, 400)
|
|
|
|
def test_audit_post_allowed_other_post_still_blocked(self):
|
|
self.assertEqual(self.client.post("/health").status_code, 405)
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/audit",
|
|
data={"report_text": _MINIMAL_REVIEW_REPORT},
|
|
).status_code,
|
|
200,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() |