Files
benevoles/backend/tests/test_email.py
T
AdamThomasandClaude Sonnet 5 fcc42c8d43 Email : ne plus journaliser l'adresse du destinataire en cas de refus SMTP
En cherchant pourquoi les logs d'envoi d'email semblaient invisibles, on a
plutôt trouvé un problème RGPD : `SMTPRecipientsRefused` embarque l'adresse
du destinataire dans ses `args` (`{adresse: (code, message)}`), et
`logger.error(..., exc)` la journalisait telle quelle via `str(exc)` — en
violation directe de la règle « pas de log des noms/emails/téléphones ».

`_smtp_error_summary()` n'en extrait que les codes/messages serveur.

Constaté au passage (pas un bug) : le mécanisme de logs lui-même fonctionne
bien — `docker compose logs -f backend` affiche bien les lignes `app.email`
dès qu'une vraie requête HTTP déclenche l'envoi (testé en direct sur
l'édition de démo).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-15 19:23:21 +02:00

109 lines
3.1 KiB
Python

from types import SimpleNamespace
from app.email import sender
def test_disabled_delivery_is_a_noop():
# email_enabled=False par défaut en test : aucune exception, pas de SMTP.
sender.send("a@b.fr", "Sujet", "Corps")
sender.send_many([("a@b.fr", "S", "B"), ("c@d.fr", "S", "B")])
def test_smtp_is_used_when_enabled(monkeypatch):
fake = SimpleNamespace(
email_enabled=True,
smtp_host="mail.test",
smtp_port=587,
smtp_user="u",
smtp_password="p",
smtp_starttls=True,
smtp_ssl=False,
smtp_from="Festival <no-reply@test>",
)
monkeypatch.setattr(sender, "get_settings", lambda: fake)
events: list = []
class FakeSMTP:
def __init__(self, host, port, timeout=None):
events.append(("connect", host, port))
def __enter__(self):
return self
def __exit__(self, *_a):
return False
def starttls(self, context=None):
events.append(("starttls",))
def login(self, user, _password):
events.append(("login", user))
def send_message(self, message):
events.append(("send", message["To"], message["Subject"]))
monkeypatch.setattr(sender.smtplib, "SMTP", FakeSMTP)
sender.send_many([("x@y.fr", "Sujet 1", "B"), ("z@y.fr", "Sujet 2", "B")])
assert ("connect", "mail.test", 587) in events
assert ("starttls",) in events
assert ("login", "u") in events
assert ("send", "x@y.fr", "Sujet 1") in events
assert ("send", "z@y.fr", "Sujet 2") in events
def test_recipient_refusal_does_not_log_the_email_address(monkeypatch, caplog):
# RGPD : `SMTPRecipientsRefused` embarque l'adresse dans ses `args` —
# elle ne doit jamais atterrir dans les logs, seulement le(s) code(s).
fake = SimpleNamespace(
email_enabled=True,
smtp_host="mail.test",
smtp_port=587,
smtp_user="u",
smtp_password="p",
smtp_starttls=True,
smtp_ssl=False,
smtp_from="Festival <no-reply@test>",
)
monkeypatch.setattr(sender, "get_settings", lambda: fake)
recipient = "jean.dupont@example.fr"
class FakeSMTP:
def __init__(self, host, port, timeout=None):
pass
def __enter__(self):
return self
def __exit__(self, *_a):
return False
def starttls(self, context=None):
pass
def login(self, user, _password):
pass
def send_message(self, message):
raise sender.smtplib.SMTPRecipientsRefused(
{recipient: (450, b"Recipient address rejected: Domain not found")}
)
monkeypatch.setattr(sender.smtplib, "SMTP", FakeSMTP)
with caplog.at_level("ERROR", logger="app.email"):
sender.send(recipient, "Sujet", "Corps")
assert recipient not in caplog.text
assert "450" in caplog.text
def test_render_magic_link_uses_translations():
from app.email import render_magic_link
subject, body = render_magic_link(link="https://x/connexion?token=abc", minutes=30)
assert subject
assert "https://x/connexion?token=abc" in body
assert "30" in body