test(#134): mail.py Coverage 88.2% → 100%
- TestSendSync.test_raises_when_smtp_not_configured: leerer host/user fuehrt zu RuntimeError - TestSendSync.test_calls_smtp_ssl_with_settings: smtplib.SMTP_SSL wird mit host/port instanziiert, login + send_message aufgerufen - TestSendMailAsync.test_runs_send_sync_in_executor: send_mail() delegiert per loop.run_in_executor an _send_sync
This commit is contained in:
parent
9af74b1a05
commit
e69ca1c29d
@ -352,3 +352,63 @@ class TestRunDailyDigest:
|
|||||||
|
|
||||||
assert result["failed"] == 1
|
assert result["failed"] == 1
|
||||||
assert result["sent"] == 0
|
assert result["sent"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
# ─── SMTP-Send-Path Coverage (#134 Backfill) ─────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
class TestSendSync:
|
||||||
|
def test_raises_when_smtp_not_configured(self, monkeypatch):
|
||||||
|
"""Wenn settings.smtp_host oder smtp_user leer ist, RuntimeError."""
|
||||||
|
from app import mail as mail_mod
|
||||||
|
from app.config import settings
|
||||||
|
monkeypatch.setattr(settings, "smtp_host", "")
|
||||||
|
monkeypatch.setattr(settings, "smtp_user", "user@example.com")
|
||||||
|
with pytest.raises(RuntimeError, match="SMTP nicht konfiguriert"):
|
||||||
|
mail_mod._send_sync("to@example.com", "subj", "text", "<p>html</p>")
|
||||||
|
|
||||||
|
def test_calls_smtp_ssl_with_settings(self, monkeypatch):
|
||||||
|
"""Bei vollstaendiger Konfig wird smtplib.SMTP_SSL aufgerufen, login
|
||||||
|
und send_message getriggert."""
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from app import mail as mail_mod
|
||||||
|
from app.config import settings
|
||||||
|
monkeypatch.setattr(settings, "smtp_host", "smtp.test")
|
||||||
|
monkeypatch.setattr(settings, "smtp_port", 465)
|
||||||
|
monkeypatch.setattr(settings, "smtp_user", "user@test")
|
||||||
|
monkeypatch.setattr(settings, "smtp_password", "pw")
|
||||||
|
monkeypatch.setattr(settings, "smtp_from_email", "noreply@test")
|
||||||
|
monkeypatch.setattr(settings, "smtp_from_name", "Test")
|
||||||
|
|
||||||
|
ssl_mock = MagicMock()
|
||||||
|
server_mock = MagicMock()
|
||||||
|
ssl_mock.return_value.__enter__.return_value = server_mock
|
||||||
|
ssl_mock.return_value.__exit__.return_value = False
|
||||||
|
monkeypatch.setattr(mail_mod.smtplib, "SMTP_SSL", ssl_mock)
|
||||||
|
|
||||||
|
mail_mod._send_sync("to@test", "subj", "Plain", "<p>HTML</p>")
|
||||||
|
|
||||||
|
# SMTP_SSL wurde aufgerufen mit host + port
|
||||||
|
ssl_mock.assert_called_once()
|
||||||
|
args, kwargs = ssl_mock.call_args
|
||||||
|
assert args[0] == "smtp.test"
|
||||||
|
assert args[1] == 465
|
||||||
|
# Login + send wurden aufgerufen
|
||||||
|
server_mock.login.assert_called_once_with("user@test", "pw")
|
||||||
|
server_mock.send_message.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSendMailAsync:
|
||||||
|
def test_runs_send_sync_in_executor(self, monkeypatch):
|
||||||
|
"""send_mail (async) delegiert an _send_sync via Thread-Executor."""
|
||||||
|
import asyncio
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from app import mail as mail_mod
|
||||||
|
|
||||||
|
called: list[tuple] = []
|
||||||
|
def fake_sync(to, subj, text, html):
|
||||||
|
called.append((to, subj, text, html))
|
||||||
|
|
||||||
|
monkeypatch.setattr(mail_mod, "_send_sync", fake_sync)
|
||||||
|
asyncio.run(mail_mod.send_mail("to@test", "subj", "text", "<p>html</p>"))
|
||||||
|
assert called == [("to@test", "subj", "text", "<p>html</p>")]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user