Files

367 lines
14 KiB
Python

"""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()