Files
microdao-daarion/services/sofiia-supervisor/tests/test_postmortem_graph.py
Apple 129e4ea1fc feat(platform): add new services, tools, tests and crews modules
New router intelligence modules (26 files): alert_ingest/store, audit_store,
architecture_pressure, backlog_generator/store, cost_analyzer, data_governance,
dependency_scanner, drift_analyzer, incident_* (5 files), llm_enrichment,
platform_priority_digest, provider_budget, release_check_runner, risk_* (6 files),
signature_state_store, sofiia_auto_router, tool_governance

New services:
- sofiia-console: Dockerfile, adapters/, monitor/nodes/ops/voice modules, launchd, react static
- memory-service: integration_endpoints, integrations, voice_endpoints, static UI
- aurora-service: full app suite (analysis, job_store, orchestrator, reporting, schemas, subagents)
- sofiia-supervisor: new supervisor service
- aistalk-bridge-lite: Telegram bridge lite
- calendar-service: CalDAV calendar service with reminders
- mlx-stt-service / mlx-tts-service: Apple Silicon speech services
- binance-bot-monitor: market monitor service
- node-worker: STT/TTS memory providers

New tools (9): agent_email, browser_tool, contract_tool, observability_tool,
oncall_tool, pr_reviewer_tool, repo_tool, safe_code_executor, secure_vault

New crews: agromatrix_crew (10 modules: depth_classifier, doc_facts, doc_focus,
farm_state, light_reply, llm_factory, memory_manager, proactivity, reflection_engine,
session_context, style_adapter, telemetry)

Tests: 85+ test files for all new modules
Made-with: Cursor
2026-03-03 07:14:14 -08:00

204 lines
7.9 KiB
Python

"""
Tests for postmortem_draft_graph.
Mocks the GatewayClient — no real network calls.
"""
import asyncio
import base64
import json
import sys
from pathlib import Path
from unittest.mock import patch
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent))
from tests.conftest import MockGatewayClient, _run
# ─── Mock data ────────────────────────────────────────────────────────────────
_INCIDENT_DATA = {
"id": "inc_20260223_1000_abc123",
"service": "router",
"env": "prod",
"severity": "P1",
"status": "open",
"title": "Router OOM",
"summary": "Router pods running out of memory under high load",
"started_at": "2026-02-23T10:00:00Z",
"ended_at": None,
"created_by": "sofiia",
"events": [
{"ts": "2026-02-23T10:01:00Z", "type": "note", "message": "Memory usage >90%"},
{"ts": "2026-02-23T10:10:00Z", "type": "action", "message": "Restarted pods"},
],
"artifacts": [],
}
_INCIDENT_WITH_TRIAGE = {
**_INCIDENT_DATA,
"artifacts": [
{"kind": "triage_report", "format": "json", "path": "ops/incidents/inc_test/triage_report.json"},
],
}
_OVERVIEW_DATA = {
"status": "degraded",
"alerts": [{"name": "OOMKilled", "severity": "critical"}],
}
_HEALTH_DATA = {"status": "unhealthy", "error": "OOM"}
_KB_DATA = {"results": [
{"path": "docs/runbooks/oom.md", "content": "## OOM Runbook\n- Check memory limits\n- Restart pods"}
]}
# ─── Tests ────────────────────────────────────────────────────────────────────
class TestPostmortemDraftGraph:
"""Happy path: incident exists, triage exists, postmortem generated."""
def test_happy_path_with_triage(self):
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
mock_gw.register("oncall_tool", "incident_get", _INCIDENT_WITH_TRIAGE)
mock_gw.register("oncall_tool", "incident_attach_artifact", {"artifact": {"path": "test", "sha256": "abc"}})
mock_gw.register("oncall_tool", "incident_append_event", {"event": {"ts": "now", "type": "followup"}})
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
result = _run(graph.ainvoke({
"run_id": "gr_test_01",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {
"incident_id": "inc_20260223_1000_abc123",
"service": "router",
},
}))
assert result["graph_status"] == "succeeded"
pm = result["result"]
assert pm["incident_id"] == "inc_20260223_1000_abc123"
assert pm["artifacts_count"] >= 2 # md + json
assert "postmortem" in result["postmortem_md"].lower()
def test_triage_missing_triggers_generation(self):
"""When incident has no triage artifact, the graph generates one."""
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
mock_gw.register("oncall_tool", "incident_get", _INCIDENT_DATA) # no triage artifact
mock_gw.register("observability_tool", "service_overview", _OVERVIEW_DATA)
mock_gw.register("oncall_tool", "service_health", _HEALTH_DATA)
mock_gw.register("kb_tool", "search", _KB_DATA)
mock_gw.register("oncall_tool", "incident_attach_artifact", {"artifact": {"path": "t", "sha256": "x"}})
mock_gw.register("oncall_tool", "incident_append_event", {"event": {}})
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
result = _run(graph.ainvoke({
"run_id": "gr_test_02",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {"incident_id": "inc_20260223_1000_abc123"},
}))
assert result["graph_status"] == "succeeded"
assert result.get("triage_was_generated") is True
# Should have triage + postmortem artifacts (3 total)
assert result["result"]["artifacts_count"] >= 2
def test_incident_not_found_fails_gracefully(self):
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
mock_gw.register("oncall_tool", "incident_get", None, error="Incident not found")
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
result = _run(graph.ainvoke({
"run_id": "gr_test_03",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {"incident_id": "inc_nonexistent"},
}))
assert result["graph_status"] == "failed"
assert "not found" in (result.get("error") or "").lower()
def test_missing_incident_id_fails(self):
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
result = _run(graph.ainvoke({
"run_id": "gr_test_04",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {},
}))
assert result["graph_status"] == "failed"
assert "incident_id" in (result.get("validation_error") or "").lower()
def test_gateway_error_on_followup_nonfatal(self):
"""If follow-up append fails, graph still succeeds."""
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
mock_gw.register("oncall_tool", "incident_get", _INCIDENT_WITH_TRIAGE)
mock_gw.register("oncall_tool", "incident_attach_artifact", {"artifact": {"path": "t", "sha256": "x"}})
mock_gw.register("oncall_tool", "incident_append_event", None, error="gateway timeout")
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
result = _run(graph.ainvoke({
"run_id": "gr_test_05",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {"incident_id": "inc_20260223_1000_abc123"},
}))
assert result["graph_status"] == "succeeded"
# followups may be 0 due to error, but graph still completed
assert result["result"]["followups_count"] == 0
def test_correlation_ids_present(self):
from app.graphs.postmortem_draft_graph import build_postmortem_draft_graph
mock_gw = MockGatewayClient()
mock_gw.register("oncall_tool", "incident_get", _INCIDENT_WITH_TRIAGE)
mock_gw.register("oncall_tool", "incident_attach_artifact", {"artifact": {}})
mock_gw.register("oncall_tool", "incident_append_event", {"event": {}})
graph = build_postmortem_draft_graph()
with patch("app.graphs.postmortem_draft_graph.GatewayClient", return_value=mock_gw):
_run(graph.ainvoke({
"run_id": "gr_corr_01",
"agent_id": "sofiia",
"workspace_id": "ws1",
"user_id": "u1",
"input": {"incident_id": "inc_20260223_1000_abc123"},
}))
# All calls should have graph_run_id
for call in mock_gw.calls:
assert call["graph_run_id"] == "gr_corr_01"