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["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