- 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)
206 lines
7.7 KiB
Python
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
|