infrasynth-backend-kit/tests/test_notifications/test_channels.py
2026-08-28 14:38:47 -05:00

193 lines
7.9 KiB
Python

import pytest
from infrasynth.notifications.channels.base import Attachment, BaseChannel
from infrasynth.notifications.channels.email_sendgrid import SendGridChannel
from infrasynth.notifications.channels.email_smtp import SMTPChannel
from infrasynth.notifications.channels.sms_twilio import TwilioSMSChannel
from infrasynth.notifications.channels.telegram import TelegramChannel
class TestBaseChannel:
def test_is_abstract(self):
with pytest.raises(TypeError):
BaseChannel()
def test_attachment_fields(self):
attachment = Attachment("f.txt", b"data", "text/plain")
assert attachment.filename == "f.txt"
assert attachment.content == b"data"
assert attachment.mime_type == "text/plain"
assert attachment.cid is None
assert attachment.inline is False
class TestSMTPChannel:
def test_from_config_lowercases_keys(self):
channel = SMTPChannel.from_config({"HOST": "smtp.example.com", "PORT": 2525, "USERNAME": "u"})
assert channel.host == "smtp.example.com"
assert channel.port == 2525
assert channel.username == "u"
def test_send_without_host_returns_err(self):
channel = SMTPChannel()
result = channel.send("a@b.com", "s", "b")
assert result.is_err
assert "SMTP host" in result.error
def test_send_success_with_locmem_backend(self, settings):
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
channel = SMTPChannel.from_config(
{"host": "smtp.example.com", "backend": "django.core.mail.backends.locmem.EmailBackend"}
)
result = channel.send("a@b.com", "Hello", "<b>Hi</b>", is_html=True)
assert result.is_ok
def test_send_success_with_attachment(self, settings):
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
channel = SMTPChannel.from_config(
{"host": "smtp.example.com", "backend": "django.core.mail.backends.locmem.EmailBackend"}
)
attachment = Attachment("doc.pdf", b"%PDF", "application/pdf")
result = channel.send("a@b.com", "Hello", "Hi", attachments=[attachment])
assert result.is_ok
def test_health_check(self):
assert SMTPChannel.from_config({"host": "x"}).health_check() is True
assert SMTPChannel().health_check() is False
class TestSendGridChannel:
def test_send_without_key_returns_err(self):
channel = SendGridChannel()
result = channel.send("a@b.com", "s", "b")
assert result.is_err
assert "API key" in result.error
def test_send_success(self):
channel = SendGridChannel(api_key="sg-key")
with __import__("unittest").mock.patch(
"infrasynth.notifications.channels.email_sendgrid.requests.post"
) as post:
post.return_value.status_code = 202
post.return_value.text = "accepted"
result = channel.send("a@b.com", "Hello", "World")
assert result.is_ok
payload = post.call_args.kwargs["json"]
assert payload["personalizations"][0]["to"][0]["email"] == "a@b.com"
assert payload["content"][0]["type"] == "text/html"
assert payload["from"]["email"] == "noreply@example.com"
def test_send_http_error_returns_err(self):
channel = SendGridChannel(api_key="sg-key")
with __import__("unittest").mock.patch(
"infrasynth.notifications.channels.email_sendgrid.requests.post"
) as post:
post.return_value.status_code = 401
post.return_value.text = "unauthorized"
result = channel.send("a@b.com", "Hello", "World")
assert result.is_err
assert "401" in result.error
def test_send_request_exception_returns_err(self):
import requests
channel = SendGridChannel(api_key="sg-key")
with __import__("unittest").mock.patch(
"infrasynth.notifications.channels.email_sendgrid.requests.post",
side_effect=requests.RequestException("timeout"),
):
result = channel.send("a@b.com", "Hello", "World")
assert result.is_err
assert "timeout" in result.error
def test_health_check(self):
assert SendGridChannel(api_key="k").health_check() is True
assert SendGridChannel().health_check() is False
class TestTwilioSMSChannel:
def test_send_without_credentials_returns_err(self):
channel = TwilioSMSChannel()
result = channel.send("+573001234567", "s", "b")
assert result.is_err
assert "credentials" in result.error
def test_send_success(self):
channel = TwilioSMSChannel(account_sid="sid", auth_token="token", from_number="+15005550006")
with __import__("unittest").mock.patch("infrasynth.notifications.channels.sms_twilio.Client") as client_cls:
client = client_cls.return_value
result = channel.send("+573001234567", "s", "Hello SMS")
assert result.is_ok
client.messages.create.assert_called_once_with(to="+573001234567", from_="+15005550006", body="Hello SMS")
def test_send_twilio_error_returns_err(self):
from twilio.base.exceptions import TwilioRestException
channel = TwilioSMSChannel(account_sid="sid", auth_token="token", from_number="+15005550006")
with __import__("unittest").mock.patch("infrasynth.notifications.channels.sms_twilio.Client") as client_cls:
client_cls.return_value.messages.create.side_effect = TwilioRestException(
400, "http://twilio", msg="Invalid number"
)
result = channel.send("+573001234567", "s", "Hello")
assert result.is_err
assert "400" in result.error
def test_health_check(self):
assert TwilioSMSChannel(account_sid="a", auth_token="b", from_number="c").health_check() is True
assert TwilioSMSChannel().health_check() is False
class TestTelegramChannel:
def test_send_without_token_returns_err(self):
channel = TelegramChannel()
result = channel.send("12345", "s", "b")
assert result.is_err
assert "bot token" in result.error
def test_send_without_chat_id_returns_err(self):
channel = TelegramChannel(bot_token="tok")
result = channel.send("", "s", "b")
assert result.is_err
assert "chat_id" in result.error
def test_send_success(self):
from unittest import mock
channel = TelegramChannel(bot_token="tok", chat_id="12345")
with mock.patch("infrasynth.notifications.channels.telegram.requests.post") as post:
post.return_value.json.return_value = {"ok": True}
result = channel.send("", "Subject", "Hello")
assert result.is_ok
call_kwargs = post.call_args.kwargs
assert "bot" not in str(call_kwargs["json"])
def test_send_api_error_returns_err(self):
from unittest import mock
channel = TelegramChannel(bot_token="tok", chat_id="12345")
with mock.patch("infrasynth.notifications.channels.telegram.requests.post") as post:
post.return_value.json.return_value = {
"ok": False,
"description": "chat not found",
}
result = channel.send("", "Subject", "Hello")
assert result.is_err
assert "chat not found" in result.error
def test_send_request_exception_returns_err(self):
from unittest import mock
import requests
channel = TelegramChannel(bot_token="tok", chat_id="12345")
with mock.patch(
"infrasynth.notifications.channels.telegram.requests.post",
side_effect=requests.RequestException("network"),
):
result = channel.send("", "Subject", "Hello")
assert result.is_err
assert "network" in result.error
def test_health_check(self):
assert TelegramChannel(bot_token="k").health_check() is True
assert TelegramChannel().health_check() is False