from __future__ import annotations import pytest from fastapi import HTTPException from uuid import UUID from src.services import channel_event_dossier_service from src.services.channel_event_dossier_service import ( build_dossier_event, fetch_channel_event_dossier, ) def test_build_dossier_event_summarizes_source_envelope() -> None: event = build_dossier_event({ "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "h" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue redacted", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_url": "https://sentry.example.invalid/issues/issue-1", "content_sha256": "a" * 64, "content_length": 42, "source_refs": { "event_ids": ["issue-1"], "sentry_issue_ids": ["issue-1", "sentry:received:issue-1"], "fingerprints": ["sentry-issue-1"], }, "log_correlation": { "alertname": "Sentry Issue", "severity": "error", "namespace": "sentry", "target_resource": "frontend", "fingerprint": "sentry-issue-1", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:46:00", }) assert event["provider"] == "sentry" assert event["stage"] == "received" assert event["alertname"] == "Sentry Issue" assert event["severity"] == "error" assert event["source_ref_count"] == 4 assert event["has_redacted_content"] is True assert event["content_sha256"] == "a" * 64 @pytest.mark.asyncio async def test_fetch_channel_event_dossier_requires_source() -> None: with pytest.raises(HTTPException) as exc_info: await fetch_channel_event_dossier( project_id="awoooi", run_id=None, provider_event_id=None, limit=20, ) assert exc_info.value.status_code == 422 @pytest.mark.asyncio async def test_fetch_channel_event_dossier_uses_typed_run_filter(monkeypatch) -> None: captured: dict[str, object] = {} class FakeMappings: def all(self) -> list[dict[str, object]]: return [] class FakeResult: def mappings(self) -> FakeMappings: return FakeMappings() class FakeDb: async def execute(self, statement, params): # noqa: ANN001 captured["sql"] = str(statement) captured["params"] = params return FakeResult() class FakeContext: async def __aenter__(self) -> FakeDb: return FakeDb() async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001 return None monkeypatch.setattr( channel_event_dossier_service, "get_db_context", lambda _project_id: FakeContext(), ) run_id = UUID("0a4c365f-609e-5441-bc29-4c7ebc3603b6") result = await fetch_channel_event_dossier( project_id="awoooi", run_id=run_id, provider_event_id=None, limit=20, ) assert result["total"] == 0 assert "run_id = CAST(:run_id AS uuid)" in str(captured["sql"]) assert ":run_id IS NULL" not in str(captured["sql"]) assert captured["params"] == { "project_id": "awoooi", "run_id": str(run_id), "limit": 20, }