Files
ss-tools/backend/tests/services/test_notification_providers.py
busya c33792c8ee test(services): add unit tests for 7 service-layer modules
- profile_utils: 40 tests, 100% coverage (sanitize, normalize, mask, validate payload)
- security_badge_service: 17 tests, 100% coverage (role/permission extraction, security summary)
- services_mapping: 4 tests, 100% coverage (client resolution, get_suggestions)
- superset_lookup_service: 10 tests, 100% coverage (resolve environment, lookup success/degraded)
- notification_providers: 16 tests, 95% coverage (SMTP, Telegram, Slack providers)
- llm_provider: 25 tests, 91% coverage (mask_api_key, CRUD with encrypted API keys)
- rbac_permission_catalog: 12 tests, 79% coverage (route scanning, sync to DB)
2026-06-10 14:57:04 +03:00

206 lines
7.7 KiB
Python

# #region Test.NotificationProviders [C:3] [TYPE Module] [SEMANTICS test,notification,provider,smtp,telegram,slack]
# @BRIEF Tests for notifications/providers.py — SMTP, Telegram, Slack providers with mocked network.
# @RELATION BINDS_TO -> [providers]
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
class TestSMTPProvider:
"""SMTPProvider — delivers notifications via SMTP."""
@pytest.fixture
def config(self):
return {
"host": "smtp.example.com",
"port": 587,
"username": "user",
"password": "pass",
"from_email": "noreply@example.com",
"use_tls": True,
}
@pytest.fixture
def provider(self, config):
from src.services.notifications.providers import SMTPProvider
return SMTPProvider(config)
def test_init(self, provider):
assert provider.host == "smtp.example.com"
assert provider.port == 587
assert provider.from_email == "noreply@example.com"
assert provider.use_tls is True
def test_init_defaults(self):
from src.services.notifications.providers import SMTPProvider
p = SMTPProvider({"host": "smtp.x.com"})
assert p.port == 587
assert p.use_tls is True
assert p.username is None
assert p.password is None
@pytest.mark.asyncio
async def test_send_success(self, provider):
mock_smtp = AsyncMock()
mock_smtp.__aenter__.return_value = mock_smtp
with patch("src.services.notifications.providers.aiosmtplib.SMTP", return_value=mock_smtp):
result = await provider.send("admin@x.com", "Test", "Body")
assert result is True
mock_smtp.sendmail.assert_called_once()
@pytest.mark.asyncio
async def test_send_failure_returns_false(self, provider):
mock_smtp = AsyncMock()
mock_smtp.__aenter__.side_effect = ConnectionError("SMTP timeout")
with patch("src.services.notifications.providers.aiosmtplib.SMTP", return_value=mock_smtp):
result = await provider.send("admin@x.com", "Test", "Body")
assert result is False
@pytest.mark.asyncio
async def test_send_no_auth(self, config):
from src.services.notifications.providers import SMTPProvider
p = SMTPProvider({"host": "smtp.x.com", "username": None, "password": None})
mock_smtp = AsyncMock()
mock_smtp.__aenter__.return_value = mock_smtp
with patch("src.services.notifications.providers.aiosmtplib.SMTP", return_value=mock_smtp):
result = await p.send("admin@x.com", "Test", "Body")
assert result is True
mock_smtp.login.assert_not_called()
@pytest.mark.asyncio
async def test_send_no_tls(self, config):
from src.services.notifications.providers import SMTPProvider
p = SMTPProvider({"host": "smtp.x.com", "use_tls": False})
mock_smtp = AsyncMock()
mock_smtp.__aenter__.return_value = mock_smtp
with patch("src.services.notifications.providers.aiosmtplib.SMTP", return_value=mock_smtp):
result = await p.send("admin@x.com", "Test", "Body")
assert result is True
mock_smtp.starttls.assert_not_called()
class TestTelegramProvider:
"""TelegramProvider — delivers via Telegram Bot API."""
@pytest.fixture
def config(self):
return {"bot_token": "123:ABC"}
@pytest.fixture
def provider(self, config):
from src.services.notifications.providers import TelegramProvider
return TelegramProvider(config)
def test_init(self, provider):
assert provider.bot_token == "123:ABC"
@pytest.mark.asyncio
async def test_send_success(self, provider):
mock_client = AsyncMock()
mock_response = MagicMock()
mock_response.raise_for_status.return_value = None
mock_client.post.return_value = mock_response
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("@admin", "Alert", "DB issue")
assert result is True
mock_client.post.assert_called_once()
@pytest.mark.asyncio
async def test_send_no_token_returns_false(self):
from src.services.notifications.providers import TelegramProvider
p = TelegramProvider({"bot_token": None})
result = await p.send("@admin", "Alert", "Body")
assert result is False
@pytest.mark.asyncio
async def test_send_failure_returns_false(self, provider):
mock_client = AsyncMock()
mock_client.post.side_effect = ConnectionError("API timeout")
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("@admin", "Alert", "Body")
assert result is False
@pytest.mark.asyncio
async def test_send_http_error_returns_false(self, provider):
mock_client = AsyncMock()
mock_response = MagicMock()
mock_response.raise_for_status.side_effect = ConnectionError("HTTP 500")
mock_client.post.return_value = mock_response
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("@admin", "Alert", "Body")
assert result is False
class TestSlackProvider:
"""SlackProvider — delivers via Slack Webhooks."""
@pytest.fixture
def config(self):
return {"webhook_url": "https://hooks.slack.com/services/xxx"}
@pytest.fixture
def provider(self, config):
from src.services.notifications.providers import SlackProvider
return SlackProvider(config)
def test_init(self, provider):
assert provider.webhook_url == "https://hooks.slack.com/services/xxx"
@pytest.mark.asyncio
async def test_send_success(self, provider):
mock_client = AsyncMock()
mock_response = MagicMock()
mock_response.raise_for_status.return_value = None
mock_client.post.return_value = mock_response
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("#alerts", "Alert", "DB issue")
assert result is True
mock_client.post.assert_called_once()
@pytest.mark.asyncio
async def test_send_no_webhook_returns_false(self):
from src.services.notifications.providers import SlackProvider
p = SlackProvider({"webhook_url": None})
result = await p.send("#alerts", "Alert", "Body")
assert result is False
@pytest.mark.asyncio
async def test_send_failure_returns_false(self, provider):
mock_client = AsyncMock()
mock_client.post.side_effect = ConnectionError("Webhook timeout")
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("#alerts", "Alert", "Body")
assert result is False
@pytest.mark.asyncio
async def test_send_http_error_returns_false(self, provider):
mock_client = AsyncMock()
mock_response = MagicMock()
mock_response.raise_for_status.side_effect = ConnectionError("HTTP 500")
mock_client.post.return_value = mock_response
with patch("src.services.notifications.providers._get_http_client",
return_value=mock_client):
result = await provider.send("#alerts", "Alert", "Body")
assert result is False
# #endregion Test.NotificationProviders