Files
awoooi/apps/api/tests/test_channel_event_dossier_service.py
Your Name b50614528e
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m20s
CD Pipeline / build-and-deploy (push) Successful in 3m31s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s
feat(awooop): surface recurrence repair work items
2026-05-18 20:30:43 +08:00

576 lines
22 KiB
Python

from __future__ import annotations
from uuid import UUID
import pytest
from fastapi import HTTPException
from src.api.v1.platform.events import ChannelEventRecurrenceResponse
from src.services import channel_event_dossier_service
from src.services.channel_event_dossier_service import (
build_dossier_coverage,
build_dossier_event,
build_dossier_recurrence,
fetch_channel_event_dossier,
fetch_channel_event_dossier_coverage,
fetch_channel_event_dossier_recurrence,
)
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
def test_build_dossier_coverage_summarizes_recent_sources() -> None:
coverage = build_dossier_coverage(
[
{
"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_refs": {
"sentry_issue_ids": ["issue-1"],
"alert_ids": ["sentry:received:issue-1"],
"fingerprints": ["fingerprint-1"],
},
},
"is_duplicate": False,
"provider_ts": None,
"received_at": "2026-05-13T13:46:00",
},
{
"event_id": "event-2",
"project_id": "awoooi",
"channel_type": "internal",
"provider_event_id": "signoz:received:alert-1",
"content_hash": "i" * 64,
"content_preview": "SignOz alert",
"content_redacted": None,
"redaction_version": "audit_sink_v1",
"source_envelope": {
"provider": "signoz",
"stage": "received",
"source_refs": {
"signoz_alerts": ["alert-1"],
"alert_ids": ["signoz:received:alert-1"],
},
},
"is_duplicate": True,
"provider_ts": None,
"received_at": "2026-05-13T13:45:00",
},
{
"event_id": "event-3",
"project_id": "awoooi",
"channel_type": "telegram",
"provider_event_id": "telegram:callback:1",
"content_hash": None,
"content_preview": "Callback",
"content_redacted": None,
"redaction_version": "audit_sink_v1",
"source_envelope": {},
"is_duplicate": False,
"provider_ts": None,
"received_at": "2026-05-13T13:44:00",
},
],
project_id="awoooi",
limit=100,
)
assert coverage["project_id"] == "awoooi"
assert coverage["summary"]["source_count"] == 3
assert coverage["summary"]["source_envelope_total"] == 2
assert coverage["summary"]["missing_source_envelope_total"] == 1
assert coverage["summary"]["with_source_refs_total"] == 2
assert coverage["summary"]["missing_source_refs_total"] == 1
assert coverage["summary"]["duplicate_total"] == 1
assert coverage["summary"]["redacted_total"] == 1
assert coverage["summary"]["sentry_ref_total"] == 1
assert coverage["summary"]["signoz_ref_total"] == 1
assert coverage["summary"]["alert_ref_total"] == 2
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"],
"incident_ids": ["INC-20260513-ABCD"],
"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"],
"incident_ids": ["INC-20260513-ABCD"],
"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,
repair_summaries_by_incident={
"INC-20260513-ABCD": {
"schema_version": "awooop_recurrence_repair_summary_v1",
"incident_id": "INC-20260513-ABCD",
"latest_auto_repair_id": "repair-1",
"latest_playbook_id": "playbook-1",
"latest_playbook_name": "Restart workload",
"latest_success": False,
"latest_error_message_preview": "verifier failed",
"latest_triggered_by": "auto_repair",
"latest_risk_level": "low",
"latest_execution_time_ms": 1200,
"latest_auto_repair_at": "2026-05-13T13:48:00",
"latest_verification_result": "failed",
"latest_verification_at": "2026-05-13T13:49:00",
"auto_repair_total": 1,
"success_total": 0,
"failed_total": 1,
},
},
)
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
assert recurrence["summary"]["auto_repair_linked_total"] == 1
assert recurrence["summary"]["open_work_item_group_total"] == 1
assert recurrence["summary"]["verified_repair_group_total"] == 0
assert recurrence["summary"]["automation_gap_group_total"] == 0
assert recurrence["summary"]["failed_repair_group_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["latest_incident_id"] == "INC-20260513-ABCD"
assert host_group["incident_ids"] == ["INC-20260513-ABCD"]
assert host_group["run_state_counts"] == {"waiting_approval": 1, "completed": 1}
assert host_group["alert_ref_total"] == 2
assert host_group["repair_summary"]["status"] == "auto_repair_failed"
assert host_group["repair_summary"]["latest_auto_repair_id"] == "repair-1"
assert host_group["work_item"] == {
"schema_version": "awooop_recurrence_work_item_link_v1",
"work_item_id": "verification:INC-20260513-ABCD:repair-1",
"incident_id": "INC-20260513-ABCD",
"auto_repair_id": "repair-1",
"status": "open",
"kind": "verification",
"next_step": "triage_failed_repair",
"reason": "auto_repair_failed",
"needs_human": True,
}
def test_build_dossier_recurrence_opens_work_item_for_completed_run_without_repair() -> None:
recurrence = build_dossier_recurrence(
[
{
"event_id": "event-1",
"project_id": "awoooi",
"channel_type": "internal",
"provider_event_id": "alertmanager:received:1",
"content_hash": "a" * 64,
"content_preview": "Docker container unhealthy",
"content_redacted": "Docker container unhealthy",
"redaction_version": "audit_sink_v1",
"source_envelope": {
"provider": "alertmanager",
"source_refs": {
"alert_ids": ["alert-1"],
"incident_ids": ["INC-20260517-F25B4A"],
"fingerprints": ["fp-container-unhealthy"],
},
"log_correlation": {
"alertname": "DockerContainerUnhealthy",
"severity": "warning",
"namespace": "momo",
"target_resource": "bitan-pharmacy-bitan-1",
"fingerprint": "fp-container-unhealthy",
},
},
"is_duplicate": True,
"provider_ts": None,
"received_at": "2026-05-17T23:47:00",
"run_id": UUID("33333333-3333-4333-8333-333333333333"),
"run_state": "completed",
"run_agent_id": "openclaw",
}
],
project_id="awoooi",
limit=20,
)
item = recurrence["items"][0]
assert recurrence["summary"]["open_work_item_group_total"] == 1
assert recurrence["summary"]["automation_gap_group_total"] == 1
assert item["repair_summary"]["status"] == "run_completed_no_repair"
assert item["work_item"] == {
"schema_version": "awooop_recurrence_work_item_link_v1",
"work_item_id": "incident:INC-20260517-F25B4A",
"incident_id": "INC-20260517-F25B4A",
"auto_repair_id": None,
"status": "open",
"kind": "automation_gap",
"next_step": "create_repair_ticket",
"reason": "completed_run_without_auto_repair",
"needs_human": True,
}
def test_recurrence_response_model_preserves_repair_work_item_fields() -> None:
response = ChannelEventRecurrenceResponse.model_validate(
{
"project_id": "awoooi",
"limit": 20,
"summary": {
"source_event_total": 1,
"recurrence_group_total": 1,
"recurrent_group_total": 1,
"duplicate_event_total": 0,
"linked_run_total": 1,
"unlinked_event_total": 0,
"auto_repair_linked_total": 1,
"verified_repair_group_total": 1,
"open_work_item_group_total": 0,
"manual_gate_group_total": 0,
"automation_gap_group_total": 0,
"failed_repair_group_total": 0,
"latest_received_at": "2026-05-13T13:47:00",
},
"items": [
{
"recurrence_key": "fingerprint:fp-host-disk",
"provider": "alertmanager",
"alertname": "HostDiskUsageHigh",
"severity": "warning",
"namespace": "node",
"target_resource": "host-110",
"fingerprint": "fp-host-disk",
"latest_event_id": "11111111-1111-4111-8111-111111111111",
"latest_provider_event_id": "alertmanager:received:1",
"latest_content_preview": "Host disk pressure",
"latest_run_id": "22222222-2222-4222-8222-222222222222",
"latest_run_state": "completed",
"latest_agent_id": "openclaw",
"latest_incident_id": "INC-20260513-ABCD",
"incident_ids": ["INC-20260513-ABCD"],
"repair_summary": {
"status": "auto_repair_verified",
"latest_auto_repair_id": "repair-1",
},
"work_item": {
"work_item_id": "verification:INC-20260513-ABCD:repair-1",
"status": "closed",
},
"occurrence_total": 1,
"duplicate_total": 0,
"linked_run_total": 1,
"source_ref_total": 3,
"missing_source_refs_total": 0,
"sentry_ref_total": 0,
"signoz_ref_total": 0,
"alert_ref_total": 1,
"run_state_counts": {"completed": 1},
"first_received_at": "2026-05-13T13:47:00",
"latest_received_at": "2026-05-13T13:47:00",
}
],
}
)
payload = response.model_dump()
assert payload["summary"]["auto_repair_linked_total"] == 1
assert payload["items"][0]["latest_incident_id"] == "INC-20260513-ABCD"
assert payload["items"][0]["repair_summary"]["status"] == "auto_repair_verified"
assert payload["items"][0]["work_item"]["status"] == "closed"
@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,
}
@pytest.mark.asyncio
async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_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_coverage(
project_id=None,
provider="sentry",
limit=500,
)
assert result["project_id"] == "awoooi"
assert result["limit"] == 200
assert "source_envelope->>'provider'" in str(captured["sql"])
assert captured["params"] == {
"project_id": "awoooi",
"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,
}