from __future__ import annotations import json from pathlib import Path import tempfile import unittest from ai_triage.report import render_markdown, write_report def redact(value: object) -> object: if isinstance(value, dict): return {key: redact(item) for key, item in value.items()} if isinstance(value, list): return [redact(item) for item in value] if isinstance(value, str): return value.replace("super-secret", "[REDACTED]").replace("user@example.com", "[REDACTED_EMAIL]") return value REPORT = { "task_name": "daily", "run_id": 123, "run_url": "https://github.invalid/runs/123", "conclusion": "failure", "triage": { "category": "product_error", "confidence": 0.9, "owner": "", "summary": "unexpected response for user@example.com", "root_cause": "token super-secret was included", "recommendations": ["inspect trace"], "evidence": ["actual 500"], }, "notification": {"status": "failed", "error": "timeout"}, } class ReportTests(unittest.TestCase): def test_writes_redacted_stable_json_and_markdown(self) -> None: with tempfile.TemporaryDirectory() as directory: json_path, markdown_path = write_report(directory, REPORT, redactor=redact) json_text = json_path.read_text(encoding="utf-8") markdown = markdown_path.read_text(encoding="utf-8") self.assertEqual("triage-report.json", json_path.name) self.assertEqual("triage-summary.md", markdown_path.name) self.assertNotIn("super-secret", json_text + markdown) self.assertNotIn("user@example.com", json_text + markdown) self.assertEqual("[REDACTED]", json.loads(json_text)["triage"]["root_cause"].split()[1]) self.assertIn("unassigned", markdown) self.assertIn("Notification", markdown) self.assertIn("timeout", markdown) def test_markdown_escapes_table_control_characters(self) -> None: report = dict(REPORT, task_name="daily | nightly\nsecond") markdown = render_markdown(redact(report)) self.assertIn("daily \\| nightly second", markdown) def test_markdown_renders_each_orchestrator_incident(self) -> None: report = { "event_id": "dedicated:123:2:456", "target_id": "dedicated", "workflow": "scenario.yaml", "run_id": 123, "run_attempt": 2, "run_url": "https://github.invalid/runs/123", "job_id": 456, "job_name": "nonprod (scenario.yaml)", "conclusion": "failure", "incidents": [ { "job_name": "nonprod (scenario.yaml)", "failed_step": "Run scenario cases", "triage": REPORT["triage"], "evidence": { "request_url": "https://api.example.com/v1/clusters/1", "expectation": "body.state == ACTIVE", "actual_response": '{"state":"CREATING"}', }, } ], "notification": {"status": "sent", "errors": []}, } markdown = render_markdown(redact(report)) self.assertIn("## Incident 1", markdown) self.assertIn("dedicated:123:2:456", markdown) self.assertIn("scenario.yaml", markdown) self.assertIn("Run attempt", markdown) self.assertIn("product_error", markdown) self.assertIn("https://api.example.com/v1/clusters/1", markdown) self.assertIn("body.state == ACTIVE", markdown) self.assertIn("CREATING", markdown) if __name__ == "__main__": unittest.main()