| 1 | """Tests for SubmitReportRequest body-completeness validation. |
| 2 | |
| 3 | A report submitted without its human-readable body fields must be rejected, not |
| 4 | silently persisted as a blank row. These are pure Pydantic-model tests — no DB |
| 5 | or app wiring required. |
| 6 | |
| 7 | Run with: cd backend && pip install pytest && python -m pytest tests/ |
| 8 | """ |
| 9 | |
| 10 | import pytest |
| 11 | from pydantic import ValidationError |
| 12 | |
| 13 | from app.ai_analyst.schema.ai_analyst import SubmitReportRequest |
| 14 | |
| 15 | COMPLETE = dict( |
| 16 | job_id="copilot-inv-1-abc", |
| 17 | alert_id=1, |
| 18 | customer_code="acme", |
| 19 | severity_assessment="High", |
| 20 | summary="Short tl;dr.", |
| 21 | report_markdown="# Full report\n\nDetails.", |
| 22 | recommended_actions="Isolate host.", |
| 23 | ) |
| 24 | |
| 25 | |
| 26 | def test_complete_report_is_accepted(): |
| 27 | req = SubmitReportRequest(**COMPLETE) |
| 28 | assert req.report_markdown == "# Full report\n\nDetails." |
| 29 | |
| 30 | |
| 31 | @pytest.mark.parametrize("field", ["severity_assessment", "summary", "report_markdown", "recommended_actions"]) |
| 32 | def test_missing_body_field_is_rejected(field): |
| 33 | payload = {k: v for k, v in COMPLETE.items() if k != field} |
| 34 | with pytest.raises(ValidationError) as excinfo: |
| 35 | SubmitReportRequest(**payload) |
| 36 | assert field in str(excinfo.value) |
| 37 | |
| 38 | |
| 39 | @pytest.mark.parametrize("field", ["summary", "report_markdown", "recommended_actions"]) |
| 40 | def test_blank_or_whitespace_body_field_is_rejected(field): |
| 41 | payload = {**COMPLETE, field: " "} |
| 42 | with pytest.raises(ValidationError) as excinfo: |
| 43 | SubmitReportRequest(**payload) |
| 44 | assert field in str(excinfo.value) |
| 45 | |
| 46 | |
| 47 | def test_control_chars_only_body_is_rejected(): |
| 48 | # strip_control_characters collapses this to "" before require_report_body runs. |
| 49 | payload = {**COMPLETE, "report_markdown": "\x00\x01\x02"} |
| 50 | with pytest.raises(ValidationError) as excinfo: |
| 51 | SubmitReportRequest(**payload) |
| 52 | assert "report_markdown" in str(excinfo.value) |
| 53 | |
| 54 | |
| 55 | def test_minimal_call_missing_all_bodies_is_rejected(): |
| 56 | # The exact failure mode from production: agent sends only the identifiers. |
| 57 | with pytest.raises(ValidationError): |
| 58 | SubmitReportRequest(job_id="j", alert_id=1, customer_code="acme") |