bottleneckcrm/backend/common/tests/test_auth_email_send_failures.py
Rostyslav Kravchuk 8dc219da1a feat(email): configure outbound mail through MAILERS and allow SMTP from env
Move outbound email off Django's deprecated EMAIL_* settings onto Django
6.1's MAILERS setting, and make the SMTP backend configurable from env
vars (EMAIL_HOST/PORT/HOST_USER/HOST_PASSWORD/USE_TLS/USE_SSL/TIMEOUT).
Auth-email Celery tasks now catch OSError alongside ClientError so an
SMTP failure is logged instead of raising. Documents Mailtrap as the
example SMTP provider.
2026-10-16 20:04:24 +03:00

83 lines
2.6 KiB
Python

"""A failed SMTP send must be logged, not raised.
Regression for the ``MAILERS`` migration: before it, ``send_magic_link_email``
(and its siblings ``send_welcome_email`` / ``send_portal_login_email``) only
caught ``botocore.exceptions.ClientError``, an SES-specific exception, so an SMTP
failure propagated out of the Celery task instead of being logged.
"""
import secrets
import smtplib
from datetime import timedelta
from unittest.mock import patch
import pytest
from django.utils import timezone
from common.models import MagicLinkToken, PortalLoginToken, User
from common.tasks import (
send_magic_link_email,
send_portal_login_email,
send_welcome_email,
)
from contacts.models import Contact
SMTP_FAILURES = pytest.mark.parametrize(
"exc",
[
smtplib.SMTPAuthenticationError(535, b"Authentication failed"),
ConnectionRefusedError(111, "Connection refused"),
],
)
def _assert_logged(caplog, message):
assert any(message in r.getMessage() for r in caplog.records)
@pytest.mark.django_db
@SMTP_FAILURES
def test_magic_link_smtp_failure_is_logged_not_raised(exc, caplog):
token = MagicLinkToken.objects.create(
email="ada@example.com",
token=secrets.token_hex(32),
expires_at=timezone.now() + timedelta(minutes=10),
)
caplog.set_level("ERROR", logger="common.tasks")
with patch("common.tasks.EmailMessage.send", side_effect=exc):
send_magic_link_email(token.id)
_assert_logged(caplog, f"Failed to send email for magic link token {token.id}")
@pytest.mark.django_db
@SMTP_FAILURES
def test_welcome_smtp_failure_is_logged_not_raised(exc, caplog):
user = User.objects.create_user(email="ada@example.com", password="pw-not-used")
caplog.set_level("ERROR", logger="common.tasks")
with patch("common.tasks.EmailMessage.send", side_effect=exc):
send_welcome_email(user.id)
_assert_logged(caplog, f"Failed to send welcome email for user {user.id}")
@pytest.mark.django_db
@SMTP_FAILURES
def test_portal_login_smtp_failure_is_logged_not_raised(exc, caplog, org_a):
contact = Contact.objects.create(
org=org_a, first_name="Pat", last_name="Smith", email="pat@example.com"
)
token = PortalLoginToken.objects.create(
org=org_a,
contact=contact,
code_hash="not-a-real-hash",
expires_at=timezone.now() + timedelta(minutes=10),
)
caplog.set_level("ERROR", logger="common.tasks")
with patch("common.tasks.EmailMessage.send", side_effect=exc):
send_portal_login_email(token.id, "123456")
_assert_logged(caplog, f"Failed to send email for portal login token {token.id}")