feat(awooop): show recurring alert links
This commit is contained in:
@@ -8,8 +8,10 @@ from src.services import channel_event_dossier_service
|
||||
from src.services.channel_event_dossier_service import (
|
||||
build_dossier_event,
|
||||
build_dossier_coverage,
|
||||
build_dossier_recurrence,
|
||||
fetch_channel_event_dossier,
|
||||
fetch_channel_event_dossier_coverage,
|
||||
fetch_channel_event_dossier_recurrence,
|
||||
)
|
||||
|
||||
|
||||
@@ -135,6 +137,112 @@ def test_build_dossier_coverage_summarizes_recent_sources() -> None:
|
||||
assert coverage["providers"][0]["provider"] == "sentry"
|
||||
|
||||
|
||||
def test_build_dossier_recurrence_groups_events_and_run_state() -> None:
|
||||
recurrence = build_dossier_recurrence(
|
||||
[
|
||||
{
|
||||
"event_id": "event-2",
|
||||
"project_id": "awoooi",
|
||||
"channel_type": "internal",
|
||||
"provider_event_id": "alertmanager:received:2",
|
||||
"content_hash": "b" * 64,
|
||||
"content_preview": "Host disk pressure",
|
||||
"content_redacted": "Host disk pressure",
|
||||
"redaction_version": "audit_sink_v1",
|
||||
"source_envelope": {
|
||||
"provider": "alertmanager",
|
||||
"source_refs": {
|
||||
"alert_ids": ["alert-2"],
|
||||
"fingerprints": ["fp-host-disk"],
|
||||
},
|
||||
"log_correlation": {
|
||||
"alertname": "HostDiskUsageHigh",
|
||||
"severity": "warning",
|
||||
"namespace": "node",
|
||||
"target_resource": "host-110",
|
||||
"fingerprint": "fp-host-disk",
|
||||
},
|
||||
},
|
||||
"is_duplicate": True,
|
||||
"provider_ts": None,
|
||||
"received_at": "2026-05-13T13:47:00",
|
||||
"run_id": UUID("11111111-1111-4111-8111-111111111111"),
|
||||
"run_state": "waiting_approval",
|
||||
"run_agent_id": "openclaw",
|
||||
},
|
||||
{
|
||||
"event_id": "event-1",
|
||||
"project_id": "awoooi",
|
||||
"channel_type": "internal",
|
||||
"provider_event_id": "alertmanager:received:1",
|
||||
"content_hash": "a" * 64,
|
||||
"content_preview": "Host disk pressure",
|
||||
"content_redacted": "Host disk pressure",
|
||||
"redaction_version": "audit_sink_v1",
|
||||
"source_envelope": {
|
||||
"provider": "alertmanager",
|
||||
"source_refs": {
|
||||
"alert_ids": ["alert-1"],
|
||||
"fingerprints": ["fp-host-disk"],
|
||||
},
|
||||
"log_correlation": {
|
||||
"alertname": "HostDiskUsageHigh",
|
||||
"severity": "warning",
|
||||
"namespace": "node",
|
||||
"target_resource": "host-110",
|
||||
"fingerprint": "fp-host-disk",
|
||||
},
|
||||
},
|
||||
"is_duplicate": False,
|
||||
"provider_ts": None,
|
||||
"received_at": "2026-05-13T13:46:00",
|
||||
"run_id": UUID("22222222-2222-4222-8222-222222222222"),
|
||||
"run_state": "completed",
|
||||
"run_agent_id": "openclaw",
|
||||
},
|
||||
{
|
||||
"event_id": "event-3",
|
||||
"project_id": "awoooi",
|
||||
"channel_type": "internal",
|
||||
"provider_event_id": "sentry:received:issue-1",
|
||||
"content_hash": "c" * 64,
|
||||
"content_preview": "Sentry issue",
|
||||
"content_redacted": "Sentry issue",
|
||||
"redaction_version": "audit_sink_v1",
|
||||
"source_envelope": {
|
||||
"provider": "sentry",
|
||||
"source_refs": {"sentry_issue_ids": ["issue-1"]},
|
||||
"log_correlation": {"alertname": "Sentry Issue"},
|
||||
},
|
||||
"is_duplicate": False,
|
||||
"provider_ts": None,
|
||||
"received_at": "2026-05-13T13:45:00",
|
||||
"run_id": None,
|
||||
"run_state": None,
|
||||
"run_agent_id": None,
|
||||
},
|
||||
],
|
||||
project_id="awoooi",
|
||||
limit=100,
|
||||
)
|
||||
|
||||
assert recurrence["summary"]["source_event_total"] == 3
|
||||
assert recurrence["summary"]["recurrence_group_total"] == 2
|
||||
assert recurrence["summary"]["recurrent_group_total"] == 1
|
||||
assert recurrence["summary"]["duplicate_event_total"] == 1
|
||||
assert recurrence["summary"]["linked_run_total"] == 2
|
||||
assert recurrence["summary"]["unlinked_event_total"] == 1
|
||||
|
||||
host_group = recurrence["items"][0]
|
||||
assert host_group["recurrence_key"] == "fingerprint:fp-host-disk"
|
||||
assert host_group["occurrence_total"] == 2
|
||||
assert host_group["duplicate_total"] == 1
|
||||
assert host_group["linked_run_total"] == 2
|
||||
assert host_group["latest_run_state"] == "waiting_approval"
|
||||
assert host_group["run_state_counts"] == {"waiting_approval": 1, "completed": 1}
|
||||
assert host_group["alert_ref_total"] == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fetch_channel_event_dossier_requires_source() -> None:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
@@ -242,3 +350,52 @@ async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter(m
|
||||
"provider": "sentry",
|
||||
"limit": 200,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fetch_channel_event_dossier_recurrence_uses_joined_typed_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(),
|
||||
)
|
||||
|
||||
result = await fetch_channel_event_dossier_recurrence(
|
||||
project_id="awoooi",
|
||||
provider="alertmanager",
|
||||
limit=500,
|
||||
)
|
||||
|
||||
assert result["project_id"] == "awoooi"
|
||||
assert result["limit"] == 300
|
||||
assert "LEFT JOIN awooop_run_state r" in str(captured["sql"])
|
||||
assert "e.source_envelope->>'provider'" in str(captured["sql"])
|
||||
assert ":provider IS NULL" not in str(captured["sql"])
|
||||
assert captured["params"] == {
|
||||
"project_id": "awoooi",
|
||||
"provider": "alertmanager",
|
||||
"limit": 300,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user