"""notify_dispatch respects global policy.""" from datetime import datetime, timezone from unittest.mock import patch from app.models import Event from app.services import notify_dispatch from app.services.notification_policy import NotificationPolicyConfig def test_notify_event_calls_selected_channels(): event = Event( event_id="00000000-0000-4000-8000-000000000401", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="auth", type="rdp.login.success", severity="info", title="ok", summary="skip", payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=False, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: notify_dispatch.notify_event(event) mock_tg.notify_event.assert_not_called() def test_notify_event_calls_selected_channels(): event = Event( event_id="00000000-0000-4000-8000-000000000402", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="auth", type="rdp.login.failed", severity="warning", title="fail", summary="send", payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: with patch.object(notify_dispatch, "email_notify") as mock_em: notify_dispatch.notify_event(event) mock_tg.notify_event.assert_called_once() mock_wh.notify_event.assert_called_once() mock_em.notify_event.assert_not_called() def test_notify_event_skips_agent_heartbeat(): event = Event( event_id="00000000-0000-4000-8000-000000000404", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="agent", type="agent.heartbeat", severity="info", title="heartbeat", summary="skip", payload={}, ) policy = NotificationPolicyConfig( min_severity="info", use_telegram=True, use_webhook=True, use_email=True, use_mobile=True, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: with patch.object(notify_dispatch, "email_notify") as mock_em: with patch.object(notify_dispatch, "mobile_notify") as mock_mob: notify_dispatch.notify_event(event) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_not_called() mock_em.notify_event.assert_not_called() mock_mob.notify_event.assert_not_called() def test_notify_event_skipped_by_cooldown(): event = Event( event_id="00000000-0000-4000-8000-000000000403", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="auth", type="rdp.login.failed", severity="warning", title="fail", summary="send", dedup_key="same-key", payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=False, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=False): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: notify_dispatch.notify_event(event) mock_tg.notify_event.assert_not_called() def test_notify_event_skips_hidden_event_type(db_session): from app.models.event_type_visibility import EventTypeVisibility db_session.add(EventTypeVisibility(event_type="agent.inventory", show_in_events=False)) db_session.commit() event = Event( event_id="00000000-0000-4000-8000-000000000701", host_id=1, occurred_at=datetime(2026, 6, 22, 12, 0, tzinfo=timezone.utc), category="agent", type="agent.inventory", severity="warning", title="Hardware changed", summary="memory", payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=True, use_mobile=True, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: with patch.object(notify_dispatch, "email_notify") as mock_em: with patch.object(notify_dispatch, "mobile_notify") as mock_mob: notify_dispatch.notify_event(event, db=db_session) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_not_called() mock_em.notify_event.assert_not_called() mock_mob.notify_event.assert_not_called() def test_notify_auth_login_bypasses_min_severity(): event = Event( event_id="00000000-0000-4000-8000-000000000601", host_id=1, occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc), category="auth", type="rdp.login.success", severity="info", title="RDP login event 4624", summary="papatramp from 192.168.160.3", details={"telegram_via": "sac"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=False, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: notify_dispatch.notify_auth_login(event) mock_tg.notify_event.assert_called_once() def test_notify_rdg_connection_bypasses_min_severity(): event = Event( event_id="00000000-0000-4000-8000-000000000603", host_id=1, occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc), category="auth", type="rdg.connection.success", severity="info", title="RD Gateway event 302", summary="RDG 302 papatramp -> 192.168.160.3", details={"telegram_via": "sac"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=False, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: notify_dispatch.notify_rdg_connection(event) mock_tg.notify_event.assert_called_once() def test_notify_auth_login_skips_telegram_when_via_agent(): event = Event( event_id="00000000-0000-4000-8000-000000000602", host_id=1, occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc), category="auth", type="ssh.login.success", severity="info", title="SSH login", summary="user from 10.0.0.1", details={"telegram_via": "agent"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: notify_dispatch.notify_auth_login(event) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_called_once() def test_notify_lifecycle_bypasses_min_severity(): event = Event( event_id="00000000-0000-4000-8000-000000000501", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="agent", type="agent.lifecycle", severity="info", title="started", summary="agent started", details={"lifecycle": "started", "telegram_via": "sac"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=False, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: notify_dispatch.notify_lifecycle(event) mock_tg.notify_event.assert_called_once() def test_notify_lifecycle_skips_telegram_when_via_agent(): event = Event( event_id="00000000-0000-4000-8000-000000000502", host_id=1, occurred_at=datetime(2026, 5, 29, 15, 0, tzinfo=timezone.utc), category="agent", type="agent.lifecycle", severity="info", title="started", summary="agent started", details={"lifecycle": "started", "telegram_via": "agent"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: notify_dispatch.notify_lifecycle(event) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_called_once() def test_notify_daily_report_skips_telegram_when_via_agent(): event = Event( event_id="00000000-0000-4000-8000-000000000604", host_id=1, occurred_at=datetime(2026, 6, 3, 9, 0, tzinfo=timezone.utc), category="report", type="report.daily.rdp", severity="info", title="Ежедневный отчёт Windows", summary="RDP 24ч: успех 0, неудач 0, банов 0", details={"telegram_via": "agent", "report_body": "📊 body"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: notify_dispatch.notify_daily_report(event) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_called_once() def test_notify_daily_report_skips_telegram_when_via_agent(): event = Event( event_id="00000000-0000-4000-8000-000000000604", host_id=1, occurred_at=datetime(2026, 6, 3, 9, 0, tzinfo=timezone.utc), category="report", type="report.daily.rdp", severity="info", title="Ежедневный отчёт Windows", summary="RDP 24ч: успех 0, неудач 0, банов 0", details={"telegram_via": "agent", "report_body": "📊 body"}, payload={}, ) policy = NotificationPolicyConfig( min_severity="warning", use_telegram=True, use_webhook=True, use_email=False, use_mobile=False, source="db", ) with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy): with patch.object(notify_dispatch, "should_notify_event", return_value=True): with patch.object(notify_dispatch, "telegram_notify") as mock_tg: with patch.object(notify_dispatch, "webhook_notify") as mock_wh: notify_dispatch.notify_daily_report(event) mock_tg.notify_event.assert_not_called() mock_wh.notify_event.assert_called_once()