from src.services.incident_timeline_service import ( STAGE_DEFS, _approval_execution_truth, _approval_status_to_timeline_status, _reconciliation_event, format_ascii_timeline, ) def _stages(status_by_stage: dict[str, str]) -> list[dict]: return [ {"stage": stage, "status": status_by_stage.get(stage, "skipped")} for stage, _label in STAGE_DEFS ] def test_format_ascii_timeline_skips_unrecorded_stages() -> None: stages = _stages({ "webhook": "completed", "ai_router": "success", "executor": "error", "km": "pending", }) assert format_ascii_timeline(stages) == ( "webhook:ok > ai_router:ok > executor:fail > km:wait" ) def test_format_ascii_timeline_has_empty_fallback() -> None: assert format_ascii_timeline(_stages({})) == "webhook:skip > ai:skip > executor:skip" def test_reconciliation_event_marks_safe_stage_failed() -> None: event = _reconciliation_event({ "applicable": True, "consistency_status": "blocked", "operator_next_state": "manual_required", "mismatches": [ {"code": "approval_approved_without_execution_record"}, {"code": "evidence_all_sensors_failed"}, ], }) assert event is not None assert event["stage"] == "safe" assert event["status"] == "error" assert event["title"] == "Lifecycle reconciliation: blocked" assert "approval_approved_without_execution_record" in event["description"] assert event["data"]["operator_next_state"] == "manual_required" def test_reconciliation_event_omits_consistent_state() -> None: assert _reconciliation_event({ "applicable": True, "consistency_status": "consistent", "mismatches": [], }) is None def test_approval_execution_success_without_repair_is_timeline_info() -> None: approval = type("Approval", (), { "action": "OBSERVE", "extra_metadata": {"execution_kind": "no_action", "repair_executed": False}, })() truth = _approval_execution_truth(approval) assert truth == {"execution_kind": "no_action", "repair_executed": False} assert _approval_status_to_timeline_status( "execution_success", repair_executed=truth["repair_executed"], ) == "info" def test_approval_execution_success_with_repair_stays_success() -> None: approval = type("Approval", (), { "action": "kubectl rollout restart deployment/awoooi-api", "extra_metadata": {"execution_kind": "kubectl", "repair_executed": True}, })() truth = _approval_execution_truth(approval) assert truth == {"execution_kind": "kubectl", "repair_executed": True} assert _approval_status_to_timeline_status( "execution_success", repair_executed=truth["repair_executed"], ) == "success"