fix(awooop): fallback AI alert card readback to direct DB
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 56s
CD Pipeline / build-and-deploy (push) Successful in 4m55s
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
ogt
2026-07-10 00:28:41 +08:00
parent fd605a71f7
commit e2cfbaabc8
2 changed files with 351 additions and 13 deletions

View File

@@ -1314,6 +1314,11 @@ async def test_list_ai_alert_card_delivery_readback_failsofts_db_unavailable(
"get_db_context",
lambda _project_id: FailingDbContext(),
)
monkeypatch.setattr(
platform_operator_service,
"_load_ai_alert_card_delivery_readback_direct",
_no_ai_alert_card_direct_readback,
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
@@ -1358,6 +1363,11 @@ async def test_list_ai_alert_card_delivery_readback_failsofts_slow_db(
"get_db_context",
lambda _project_id: SlowDbContext(),
)
monkeypatch.setattr(
platform_operator_service,
"_load_ai_alert_card_delivery_readback_direct",
_no_ai_alert_card_direct_readback,
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
@@ -1428,6 +1438,11 @@ async def test_list_ai_alert_card_delivery_readback_uses_cache_after_live_timeou
"get_db_context",
lambda _project_id: SlowDbContext(),
)
monkeypatch.setattr(
platform_operator_service,
"_load_ai_alert_card_delivery_readback_direct",
_no_ai_alert_card_direct_readback,
)
monkeypatch.setattr(
platform_operator_service,
"get_cached_operator_summary_async",
@@ -1451,6 +1466,113 @@ async def test_list_ai_alert_card_delivery_readback_uses_cache_after_live_timeou
assert payload["learning_registry"]["missing_target_count"] == 0
async def _no_ai_alert_card_direct_readback(**_kwargs):
return None
@pytest.mark.asyncio
async def test_list_ai_alert_card_delivery_readback_uses_direct_fallback(
monkeypatch: pytest.MonkeyPatch,
) -> None:
class SlowDb:
async def execute(self, *_args, **_kwargs):
await asyncio.sleep(1)
class SlowDbContext:
async def __aenter__(self):
return SlowDb()
async def __aexit__(self, exc_type, exc, tb):
return False
async def direct_readback(**kwargs):
assert kwargs["project_id"] == "awoooi"
return (
{
"total": 1,
"sent_total": 1,
"failed_total": 0,
"pending_total": 0,
"shadow_total": 0,
"delivery_receipt_required_total": 1,
"runtime_write_gate_open_count": 0,
"learning_writeback_ready_total": 1,
"latest_sent_at": None,
"latest_queued_at": None,
},
[
{
"message_id": "message-1",
"project_id": "awoooi",
"run_id": "run-1",
"channel_type": "telegram",
"message_type": "ai_alert_card",
"provider_message_id": "provider-1",
"send_status": "sent",
"send_error": None,
"queued_at": None,
"sent_at": None,
"triggered_by_state": "controlled_apply",
"alert_card": {
"event_type": "provider_freshness_signal",
"lane": "source_provider_freshness_triage",
"target": "provider_freshness_signal",
"delivery_receipt_readback_required": True,
"runtime_write_gate_count": 0,
},
"source_refs": {
"fingerprints": ["fp-1"],
"alert_ids": ["alert-1"],
},
"agent_id": "agent",
"run_state": "completed",
"run_created_at": None,
}
],
)
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_QUERY_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_DB_CONTEXT_EXIT_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"get_db_context",
lambda _project_id: SlowDbContext(),
)
monkeypatch.setattr(
platform_operator_service,
"_load_ai_alert_card_delivery_readback_direct",
direct_readback,
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
page=1,
per_page=6,
refresh=True,
)
assert payload["total"] == 1
assert payload["summary"]["status"] == "observed"
assert payload["summary"]["db_read_status"] == (
"direct_connection_after_session_timeout"
)
assert payload["summary"]["live_refresh_status"] == (
"live_refresh_served_by_direct_connection"
)
assert payload["summary"]["controlled_repair_queue_present"] is False
assert payload["learning_registry"]["status"] == "learning_registry_ready"
assert payload["learning_registry"]["ready_target_count"] == 6
assert payload["items"][0]["learning_writeback_ready"] is True
def test_list_callback_replies_keeps_audit_summary_separate_from_km_summary() -> None:
source = inspect.getsource(platform_operator_service.list_callback_replies)