93 lines
2.8 KiB
Python
93 lines
2.8 KiB
Python
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
|