from __future__ import annotations import inspect from unittest.mock import AsyncMock import pytest from src.services.ai_providers.interfaces import AIResult from src.services.sentry_webhook_service import ( SentryWebhookService, _sentry_ai_identity, route_sentry_ai_analysis, ) def test_sentry_recurrence_gets_new_run_but_stable_work_item() -> None: first = _sentry_ai_identity( {"project": "awoooi", "issue_id": "42", "count": 1} ) recurrence = _sentry_ai_identity( {"project": "awoooi", "issue_id": "42", "count": 2} ) assert first["run_id"] != recurrence["run_id"] assert first["trace_id"] != recurrence["trace_id"] assert first["work_item_id"] == recurrence["work_item_id"] def test_sentry_service_has_no_direct_host188_openclaw_call() -> None: source = inspect.getsource(SentryWebhookService.analyze_with_openclaw) assert "get_openclaw_http_service" not in source assert "route_sentry_ai_analysis" in source @pytest.mark.asyncio async def test_sentry_analysis_uses_global_provider_order_and_correlation_ids( monkeypatch: pytest.MonkeyPatch, ) -> None: executor = type("Executor", (), {})() executor.execute = AsyncMock( return_value=AIResult( raw_response=( '{"root_cause":"database saturation","impact":"latency",' '"fix_suggestion":"inspect pool","prevention":"capacity guard",' '"confidence":0.8}' ), success=True, provider="ollama_gcp_a", ) ) monkeypatch.setattr("src.services.ai_router.get_ai_executor", lambda: executor) result = await route_sentry_ai_analysis( { "issue_id": "42", "project": "awoooi", "level": "error", "culprit": "api", } ) assert result is not None assert result["analyzed_by"] == "ollama_gcp_a" call = executor.execute.await_args.kwargs assert call["provider_order"] == [ "ollama_gcp_a", "ollama_gcp_b", "ollama_local", "gemini", ] assert call["require_local"] is False context = call["context"] assert all(context[key] for key in ("trace_id", "run_id", "work_item_id")) assert context["alert_requires_ollama_before_cloud"] is True @pytest.mark.asyncio async def test_sentry_analysis_fails_closed_on_non_json_provider_response( monkeypatch: pytest.MonkeyPatch, ) -> None: executor = type("Executor", (), {})() executor.execute = AsyncMock( return_value=AIResult( raw_response="not-json", success=True, provider="ollama_local", ) ) monkeypatch.setattr("src.services.ai_router.get_ai_executor", lambda: executor) assert await route_sentry_ai_analysis({"issue_id": "43"}) is None