from __future__ import annotations import runpy from contextlib import asynccontextmanager from pathlib import Path from types import SimpleNamespace from unittest.mock import AsyncMock import pytest import src.db.base as db_base import src.services.approval_db as approval_db import src.services.telegram_gateway as gateway_module from src.services.telegram_gateway import TelegramGateway _REPO_ROOT = Path(__file__).resolve().parents[3] class _FakeResponse: def __init__(self, payload: dict) -> None: self._payload = payload def raise_for_status(self) -> None: return None def json(self) -> dict: return { "ok": True, "result": { "message_id": 11, "chat": {"id": self._payload.get("chat_id")}, }, } class _FakeHttpClient: def __init__(self) -> None: self.posts: list[tuple[str, dict]] = [] async def post(self, url: str, json: dict) -> _FakeResponse: self.posts.append((url, json)) return _FakeResponse(json) def _prepared_gateway( monkeypatch: pytest.MonkeyPatch, ) -> tuple[TelegramGateway, _FakeHttpClient]: gateway = TelegramGateway() client = _FakeHttpClient() gateway._initialized = True gateway._http_client = client # type: ignore[assignment] monkeypatch.setattr(gateway_module.settings, "SRE_GROUP_CHAT_ID", "test-room") monkeypatch.setattr(gateway, "_attach_incident_thread_reply", AsyncMock()) monkeypatch.setattr(gateway, "_mirror_outbound_message", AsyncMock()) monkeypatch.setattr( gateway._security, "generate_callback_nonce", lambda approval_id, action: f"{action}:test", ) return gateway, client def _notification_message(): from src.services.notifications.base import ExecutionStatus, NotificationMessage return NotificationMessage( execution_status=ExecutionStatus.SUCCESS, action_title="bounded test", action_description="no provider egress", approval_id="APR-TEST", ) @pytest.mark.asyncio async def test_notification_provider_does_not_report_success_for_gateway_no_send( monkeypatch: pytest.MonkeyPatch, ) -> None: from src.services.notifications import telegram as provider_module from src.services.notifications.base import NotificationStatus monkeypatch.setattr(provider_module.settings, "OPENCLAW_TG_BOT_TOKEN", "configured") monkeypatch.setattr(provider_module.settings, "SRE_GROUP_CHAT_ID", "test-room") gateway = SimpleNamespace( send_alert_notification=AsyncMock( return_value={ "ok": False, "_awooop_delivery_status": "blocked_no_egress", "_awooop_provider_send_performed": False, } ) ) monkeypatch.setattr(gateway_module, "get_telegram_gateway", lambda: gateway) result = await provider_module.TelegramWebhookProvider().send( _notification_message() ) assert result.status is NotificationStatus.SKIPPED assert result.message == "Telegram notification blocked before provider send" assert result.response_data == { "ok": False, "_awooop_delivery_status": "blocked_no_egress", "_awooop_provider_send_performed": False, } gateway.send_alert_notification.assert_awaited_once() @pytest.mark.asyncio async def test_notification_provider_requires_affirmative_delivery_ack( monkeypatch: pytest.MonkeyPatch, ) -> None: from src.services.notifications import telegram as provider_module from src.services.notifications.base import NotificationStatus monkeypatch.setattr(provider_module.settings, "OPENCLAW_TG_BOT_TOKEN", "configured") monkeypatch.setattr(provider_module.settings, "SRE_GROUP_CHAT_ID", "test-room") gateway = SimpleNamespace( send_alert_notification=AsyncMock( return_value={"ok": False, "description": "provider rejected"} ) ) monkeypatch.setattr(gateway_module, "get_telegram_gateway", lambda: gateway) result = await provider_module.TelegramWebhookProvider().send( _notification_message() ) assert result.status is NotificationStatus.FAILED assert result.message == "Telegram provider did not acknowledge delivery" assert result.error == "delivery_not_acknowledged" @pytest.mark.asyncio async def test_notification_connection_probe_rejects_structured_no_provider_send( monkeypatch: pytest.MonkeyPatch, ) -> None: from src.services.notifications import telegram as provider_module monkeypatch.setattr(provider_module.settings, "OPENCLAW_TG_BOT_TOKEN", "configured") monkeypatch.setattr(provider_module.settings, "SRE_GROUP_CHAT_ID", "test-room") gateway = SimpleNamespace( send_alert_notification=AsyncMock( return_value={ "ok": True, "_awooop_delivery_status": "blocked_no_egress", "_awooop_provider_send_performed": False, "_awoooi_canonical_route_receipt": { "decision": "blocked_no_egress", "provider_send_performed": False, }, } ) ) monkeypatch.setattr(gateway_module, "get_telegram_gateway", lambda: gateway) assert await provider_module.TelegramWebhookProvider().test_connection() is False gateway.send_alert_notification.assert_awaited_once() @pytest.mark.asyncio async def test_notification_connection_probe_accepts_complete_canonical_delivery( monkeypatch: pytest.MonkeyPatch, ) -> None: from src.services.notifications import telegram as provider_module monkeypatch.setattr(provider_module.settings, "OPENCLAW_TG_BOT_TOKEN", "configured") monkeypatch.setattr(provider_module.settings, "SRE_GROUP_CHAT_ID", "test-room") chat_id = -1001 destination_binding = gateway_module._telegram_destination_binding(chat_id) gateway = SimpleNamespace( send_alert_notification=AsyncMock( return_value={ "ok": True, "_awooop_delivery_status": "sent", "_awooop_provider_send_performed": True, "result": {"message_id": 42, "chat": {"id": chat_id}}, "_awoooi_canonical_route_receipt": { "schema_version": "telegram_canonical_egress_receipt_v1", "decision": "allowed", "destination_alias": "awoooi_sre_war_room", "destination_binding": destination_binding, "sender_bot_alias": "tsenyang_bot", "provider_send_performed": True, }, gateway_module._DELIVERY_CONTEXT_KEY: { "schema_version": "telegram_delivery_context_v1", "method": "sendMessage", "sender_bot_alias": "tsenyang_bot", "destination_alias": "awoooi_sre_war_room", "destination_binding": destination_binding, "payload_destination_binding": destination_binding, "provider_destination_binding": destination_binding, "destination_binding_verified": True, }, } ) ) monkeypatch.setattr(gateway_module, "get_telegram_gateway", lambda: gateway) assert await provider_module.TelegramWebhookProvider().test_connection() is True gateway.send_alert_notification.assert_awaited_once() @pytest.mark.asyncio async def test_unclassified_send_message_is_blocked_before_provider( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway._send_request( "sendMessage", {"chat_id": "test-room", "text": "unclassified"}, ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awooop_provider_send_performed"] is False assert client.posts == [] @pytest.mark.asyncio @pytest.mark.parametrize( "method", sorted(gateway_module._GUARDED_TELEGRAM_BOT_METHODS), ) async def test_every_guarded_method_without_context_is_blocked_before_provider( monkeypatch: pytest.MonkeyPatch, method: str, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway._send_request( method, { "chat_id": "test-room", "text": "unclassified", "caption": "unclassified", "photo": "file-placeholder", "document": "file-placeholder", "media": [], "message_id": 42, }, ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awooop_provider_send_performed"] is False assert client.posts == [] @pytest.mark.asyncio async def test_guarded_photo_accepts_only_a_valid_canonical_route( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway._send_request( "sendPhoto", { "chat_id": "test-room", "photo": "file-placeholder", gateway_module._CANONICAL_ROUTE_CONTEXT_KEY: { "product_id": "awoooi", "signal_family": "security_incident", "severity": "P1", }, }, ) assert result["_awooop_delivery_status"] == "sent" assert gateway_module._telegram_send_delivery_succeeded(result) is True assert len(client.posts) == 1 @pytest.mark.asyncio @pytest.mark.parametrize( ("method", "method_payload"), [ ("sendDocument", {"document": "file-placeholder", "reply_to_message_id": 42}), ("editMessageText", {"text": "updated", "message_id": 42}), ], ) async def test_guarded_interaction_method_requires_exact_inbound_binding( monkeypatch: pytest.MonkeyPatch, method: str, method_payload: dict, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway._send_request( method, { "chat_id": "operator-room", **method_payload, gateway_module._NON_MONITORING_INTERACTION_KEY: { "interaction_kind": "callback_reply", "inbound_chat_id": "operator-room", "inbound_message_id": 42, }, }, ) assert result["_awooop_delivery_status"] == "sent" assert gateway_module._telegram_send_delivery_succeeded(result) is True assert len(client.posts) == 1 @pytest.mark.asyncio async def test_info_and_business_methods_are_blocked_before_provider( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) info = await gateway.send_info_notification( incident_id="INC-TEST", title="FYI", message="healthy", ) business = await gateway.send_business_alert( incident_id="INC-TEST", alertname="BusinessMetric", business_domain="test", metric_name="rate", current_value="1", threshold="2", ) assert info["_awooop_delivery_status"] == "blocked_no_egress" assert business["_awooop_delivery_status"] == "blocked_no_egress" assert client.posts == [] @pytest.mark.asyncio async def test_wrong_group_override_is_blocked_before_provider( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway.send_escalation_card( incident_id="INC-TEST", original_alertname="Outage", duration_min=15, group_chat_id="wrong-room", ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awoooi_canonical_route_receipt"]["classifier"] == ( "caller_destination_mismatch" ) assert client.posts == [] @pytest.mark.asyncio @pytest.mark.parametrize("method_name", ["meta", "secops", "escalation"]) async def test_allowed_legacy_monitoring_methods_use_canonical_provider_once( monkeypatch: pytest.MonkeyPatch, method_name: str, ) -> None: gateway, client = _prepared_gateway(monkeypatch) if method_name == "meta": result = await gateway.send_meta_alert( incident_id="INC-TEST", approval_id="APR-TEST", alertname="AlertChainBroken", alert_category="alertchain_health", diagnosis="test", ) elif method_name == "secops": result = await gateway.send_secops_card( incident_id="INC-TEST", approval_id="APR-TEST", alertname="Threat", threat_level="critical", ) else: result = await gateway.send_escalation_card( incident_id="INC-TEST", original_alertname="Outage", duration_min=15, ) assert result["ok"] is True assert result["_awoooi_canonical_route_receipt"]["decision"] == "allowed" assert result["_awoooi_canonical_route_receipt"]["provider_send_performed"] is True assert gateway_module._telegram_send_delivery_succeeded(result) is True assert len(client.posts) == 1 assert client.posts[0][1]["chat_id"] == "test-room" @pytest.mark.asyncio async def test_provider_ok_without_message_id_is_non_terminal( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) monkeypatch.setattr( _FakeResponse, "json", lambda _self: {"ok": True, "result": {}}, ) result = await gateway.send_escalation_card( incident_id="INC-TEST", original_alertname="Outage", duration_min=15, ) assert result["_awooop_delivery_status"] == "provider_message_id_missing" assert result["_awooop_provider_send_performed"] is True assert result["_awoooi_canonical_route_receipt"]["provider_send_performed"] is True assert gateway_module._telegram_send_delivery_succeeded(result) is False assert len(client.posts) == 1 @pytest.mark.asyncio async def test_provider_chat_mismatch_is_non_terminal_delivery( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) monkeypatch.setattr( _FakeResponse, "json", lambda _self: { "ok": True, "result": {"message_id": 11, "chat": {"id": "wrong-room"}}, }, ) result = await gateway.send_escalation_card( incident_id="INC-TEST", original_alertname="Outage", duration_min=15, ) assert result["_awooop_delivery_status"] == "provider_destination_mismatch" assert result["_awooop_provider_send_performed"] is True assert ( result[gateway_module._DELIVERY_CONTEXT_KEY][ "destination_binding_verified" ] is False ) assert gateway_module._telegram_send_delivery_succeeded(result) is False assert len(client.posts) == 1 @pytest.mark.asyncio async def test_openclaw_cannot_own_tsenyang_monitoring_route( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) monkeypatch.setattr( gateway_module.settings, "OPENCLAW_BOT_TOKEN", "test-token-placeholder", ) result = await gateway._send_as_bot( token="test-token-placeholder", text="must not send", product_id="awoooi", signal_family="security_incident", severity="P1", actual_bot_alias="openclaw_bot", ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awoooi_canonical_route_receipt"]["classifier"] == ( "sender_bot_alias_not_authorized_for_route" ) assert client.posts == [] @pytest.mark.asyncio async def test_alternate_bot_uses_only_canonical_send_request( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway = TelegramGateway() canonical_send = AsyncMock( return_value={ "ok": True, "_awooop_delivery_status": "sent", "_awooop_provider_send_performed": True, "result": {"message_id": 11}, "_awoooi_canonical_route_receipt": { "schema_version": "telegram_canonical_egress_receipt_v1", "decision": "allowed", "provider_send_performed": True, }, } ) monkeypatch.setattr(gateway, "_send_request", canonical_send) result = await gateway._send_as_bot( token="test-token-placeholder", text="bounded reply", reply_to_message_id=11, actual_bot_alias="openclaw_bot", inbound_chat_id="verified-room", ) assert result["_awooop_delivery_status"] == "sent" canonical_send.assert_awaited_once() method, payload = canonical_send.await_args.args assert method == "sendMessage" assert payload[gateway_module._ACTUAL_BOT_ALIAS_KEY] == "openclaw_bot" assert payload[gateway_module._BOT_TOKEN_OVERRIDE_KEY] == ("test-token-placeholder") assert payload[gateway_module._NON_MONITORING_INTERACTION_KEY] == { "interaction_kind": "bot_discussion_reply", "inbound_chat_id": "verified-room", "inbound_message_id": 11, } @pytest.mark.asyncio async def test_alternate_bot_binding_mismatch_blocks_before_http( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) monkeypatch.setattr( gateway_module.settings, "OPENCLAW_BOT_TOKEN", "expected-placeholder", ) result = await gateway._send_as_bot( token="mismatched-placeholder", text="must not send", reply_to_message_id=11, actual_bot_alias="openclaw_bot", inbound_chat_id="test-room", ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awoooi_canonical_route_receipt"]["classifier"] == ( "sender_bot_binding_mismatch" ) assert client.posts == [] def test_readability_guard_uses_complete_ast_function_boundary() -> None: guard = runpy.run_path( str(_REPO_ROOT / "scripts" / "security" / "telegram-alert-readability-guard.py") ) long_body = "\n".join(f" value_{index} = {index}" for index in range(300)) source = ( 'DECOY = "async def _send_request"\n' "class Gateway:\n" " async def _send_request(self, payload):\n" f"{long_body}\n" " return normalize_telegram_send_message_payload(\n" ' "sendMessage", payload\n' " )\n\n" " async def unrelated(self):\n" " return False\n" ) segment = guard["function_segment"]( source, "async def _send_request", ) assert len(segment) > 2200 assert "normalize_telegram_send_message_payload" in segment assert "async def unrelated" not in segment @pytest.mark.asyncio async def test_operator_reply_lane_and_runtime_edit_binding_remain_available( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) assert gateway.chat_id == "test-room" assert gateway.alert_chat_id == "test-room" unverified = await gateway.send_notification( "must not infer an operator reply", chat_id="operator-room", ) assert unverified["_awooop_delivery_status"] == "blocked_no_egress" assert unverified["_awoooi_canonical_route_receipt"]["classifier"] == ( "missing_product_signal_severity_or_interaction_context" ) assert client.posts == [] result = await gateway.send_notification( "command reply", chat_id="operator-room", interaction_kind="operator_command_reply", inbound_chat_id="operator-room", inbound_message_id=42, ) assert result["ok"] is True receipt = result["_awoooi_canonical_route_receipt"] assert receipt["classifier"] == "non_monitoring_interaction_allowed" assert receipt["destination_alias"] == "inbound_interaction_reply_target" assert client.posts[0][1]["chat_id"] == "operator-room" assert client.posts[0][1]["reply_to_message_id"] == 42 @pytest.mark.asyncio async def test_interaction_reply_must_match_verified_inbound_context( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway._send_request( "sendMessage", { "chat_id": "operator-room", "text": "mismatched reply", "reply_to_message_id": 43, gateway_module._NON_MONITORING_INTERACTION_KEY: { "interaction_kind": "operator_command_reply", "inbound_chat_id": "operator-room", "inbound_message_id": 42, }, }, ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awoooi_canonical_route_receipt"]["classifier"] == ( "interaction_reply_context_missing_or_mismatched" ) assert client.posts == [] @pytest.mark.asyncio async def test_t1_through_t6_use_only_canonical_monitoring_routes( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway = TelegramGateway() canonical_send = AsyncMock(return_value={"ok": True}) monkeypatch.setattr(gateway, "send_canonical_message", canonical_send) await gateway.send_guardrail_blocked("api", "HighCpu", "protected") await gateway.send_preflight_failed("api", 25.0, 24.0, "backup-1") await gateway.send_backup_result("backup-1", True) await gateway.send_multisig_waiting("repair", "api", 1, 2, "APR-1") await gateway.send_multisig_approved("repair", "api") await gateway.send_change_applied("operator", "repair", "2026-07-14T00:00:00Z") assert canonical_send.await_count == 6 assert [ ( call.kwargs["product_id"], call.kwargs["signal_family"], call.kwargs["severity"], ) for call in canonical_send.await_args_list ] == [ ("awoooi", "security_incident", "P1"), ("awoooi", "disaster_recovery", "P1"), ("awoooi", "disaster_recovery", "P1"), ("awoooi", "incident_lifecycle", "P1"), ("awoooi", "incident_lifecycle", "P1"), ("awoooi", "incident_lifecycle", "P1"), ] @pytest.mark.asyncio async def test_image_analysis_reply_preserves_original_inbound_message_context( monkeypatch: pytest.MonkeyPatch, ) -> None: from src.services.image_analysis_service import ImageAnalysisService gateway = SimpleNamespace( initialize=AsyncMock(), send_as_openclaw=AsyncMock(return_value={"ok": True}), ) service = ImageAnalysisService() monkeypatch.setattr( gateway_module, "get_telegram_gateway", lambda: gateway, ) monkeypatch.setattr( service, "_download_telegram_file", AsyncMock(return_value=b"image"), ) monkeypatch.setattr( service, "analyze", AsyncMock(return_value="analysis"), ) await service.download_and_analyze( chat_id="test-room", file_id="file-1", original_message_id=77, ) gateway.initialize.assert_awaited_once() gateway.send_as_openclaw.assert_awaited_once_with( "🖼️ 圖片分析\nanalysis", reply_to_message_id=77, inbound_chat_id="test-room", ) @pytest.mark.asyncio async def test_manual_repair_callback_replies_to_verified_original_message( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) monkeypatch.setattr( gateway._security, "parse_callback_data", lambda _data: { "action": "log_manual_fix", "approval_id": "APR-1", "is_info_action": False, "nonce": "nonce-1", }, ) monkeypatch.setattr( gateway._security, "verify_callback", AsyncMock(return_value={"id": 123, "username": "operator"}), ) monkeypatch.setattr( gateway, "_check_incident_state_guard", AsyncMock(return_value=None), ) monkeypatch.setattr(gateway, "_answer_callback", AsyncMock()) monkeypatch.setattr(gateway_module, "get_redis", lambda: _FakeRedis()) result = await gateway.handle_callback( callback_query_id="callback-1", callback_data="log_manual_fix:APR-1:nonce-1", user_id=123, message_id=77, chat_id="operator-room", username="operator", ) assert result["success"] is True assert result["waiting_for_manual_fix"] is True assert len(client.posts) == 1 payload = client.posts[0][1] assert payload["chat_id"] == "operator-room" assert payload["reply_to_message_id"] == 77 @pytest.mark.asyncio async def test_manual_repair_flow_blocks_before_side_effect_without_inbound_context( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) result = await gateway.handle_manual_fix_done( user_id=123, username="operator", fix_steps="restart", ) assert result == { "success": False, "reason": "missing_verified_interaction_context", } assert client.posts == [] class _FakeDb: def __init__(self) -> None: self.params: list[dict] = [] async def execute(self, statement: object, params: dict) -> None: del statement self.params.append(params) class _FakeRedis: async def setex(self, key: str, ttl: int, value: str) -> None: del key, ttl, value def _patch_approval_dependencies( monkeypatch: pytest.MonkeyPatch, gateway: TelegramGateway, ) -> _FakeDb: fake_db = _FakeDb() @asynccontextmanager async def fake_db_context(*args, **kwargs): del args, kwargs yield fake_db monkeypatch.setattr(db_base, "get_db_context", fake_db_context) monkeypatch.setattr( approval_db, "get_approval_service", lambda: SimpleNamespace(update_telegram_message=True), ) monkeypatch.setattr(gateway_module, "get_redis", lambda: _FakeRedis()) monkeypatch.setattr( gateway_module, "_fetch_remediation_summary_for_card", AsyncMock(return_value=None), ) monkeypatch.setattr(gateway, "_build_inline_keyboard", AsyncMock(return_value={})) return fake_db @pytest.mark.asyncio async def test_approval_missing_type_is_blocked_without_provider_call( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) _patch_approval_dependencies(monkeypatch, gateway) result = await gateway.send_approval_card( approval_id="APR-TEST", risk_level="critical", resource_name="resource", root_cause="cause", suggested_action="review", notification_type="", ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert client.posts == [] @pytest.mark.asyncio async def test_allowed_type3_approval_persists_runtime_chat_without_public_id( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) fake_db = _patch_approval_dependencies(monkeypatch, gateway) monkeypatch.setattr(gateway, "canonical_destination_chat_id", lambda **kwargs: "7") result = await gateway.send_approval_card( approval_id="APR-TEST", risk_level="critical", resource_name="resource", root_cause="cause", suggested_action="review", notification_type="TYPE-3", ) assert result["ok"] is True assert len(client.posts) == 1 assert any(params.get("cid") == 7 for params in fake_db.params) receipt = result["_awoooi_canonical_route_receipt"] assert receipt["destination_alias"] == "awoooi_sre_war_room" assert "chat_id" not in receipt @pytest.mark.asyncio async def test_explicit_blocked_product_route_supersedes_legacy_type3( monkeypatch: pytest.MonkeyPatch, ) -> None: gateway, client = _prepared_gateway(monkeypatch) _patch_approval_dependencies(monkeypatch, gateway) result = await gateway.send_approval_card( approval_id="APR-PRODUCT", risk_level="medium", resource_name="momo-crawler", root_cause="freshness degraded", suggested_action="inspect evidence", notification_type="TYPE-3", product_id="momo-pro", signal_family="crawler_and_data_freshness", route_severity="P2", ) assert result["_awooop_delivery_status"] == "blocked_no_egress" assert result["_awooop_provider_send_performed"] is False assert client.posts == [] receipt = result["_awoooi_canonical_route_receipt"] assert receipt["product_id"] == "momo-pro" assert receipt["signal_family"] == "crawler_and_data_freshness"