"""Unit tests for mailer.py's SMTP call shape (#18). Calls _send_now directly rather than going through the background-thread queue, so assertions are synchronous -- the queue itself is just plumbing, already covered indirectly by app.py's identical Sheets-mirror pattern. """ from email.message import EmailMessage import pytest import mailer class _FakeSMTP: sent = [] login_calls = [] def __init__(self, host, port, timeout=None): self.host = host self.port = port def __enter__(self): return self def __exit__(self, *exc): return False def starttls(self): pass def login(self, username, password): _FakeSMTP.login_calls.append((username, password)) def send_message(self, msg): _FakeSMTP.sent.append(msg) @pytest.fixture(autouse=True) def _reset_fake_smtp(): _FakeSMTP.sent = [] _FakeSMTP.login_calls = [] yield def _msg(to="alice@example.com"): msg = EmailMessage() msg["Subject"] = "Terminbestätigung" msg["From"] = "noreply@example.com" msg["To"] = to msg.set_content("

hi

", subtype="html") return msg def test_send_now_skips_when_smtp_host_not_configured(monkeypatch): monkeypatch.setattr(mailer, "SMTP_HOST", "") monkeypatch.setattr(mailer, "smtplib", type("m", (), {"SMTP": _FakeSMTP})) mailer._send_now(_msg()) assert _FakeSMTP.sent == [] def test_send_now_sends_via_smtp_when_configured(monkeypatch): monkeypatch.setattr(mailer, "SMTP_HOST", "smtp.example.com") monkeypatch.setattr(mailer, "SMTP_PORT", 587) monkeypatch.setattr(mailer, "SMTP_USERNAME", "") monkeypatch.setattr(mailer, "smtplib", type("m", (), {"SMTP": _FakeSMTP})) msg = _msg() mailer._send_now(msg) assert _FakeSMTP.sent == [msg] assert _FakeSMTP.login_calls == [] def test_send_now_logs_in_when_username_configured(monkeypatch): monkeypatch.setattr(mailer, "SMTP_HOST", "smtp.example.com") monkeypatch.setattr(mailer, "SMTP_USERNAME", "mailer") monkeypatch.setattr(mailer, "SMTP_PASSWORD", "secret") monkeypatch.setattr(mailer, "smtplib", type("m", (), {"SMTP": _FakeSMTP})) mailer._send_now(_msg()) assert _FakeSMTP.login_calls == [("mailer", "secret")] def test_send_email_builds_html_message_and_enqueues(monkeypatch): captured = [] monkeypatch.setattr(mailer._queue, "put", lambda m: captured.append(m)) mailer.send_email("bob@example.com", "Subject line", "

body

", from_addr="noreply@custom.example") assert len(captured) == 1 msg = captured[0] assert msg["To"] == "bob@example.com" assert msg["Subject"] == "Subject line" assert msg["From"] == "noreply@custom.example" assert msg.get_content_type() == "text/html" def test_send_email_defaults_from_addr_to_fallback(monkeypatch): captured = [] monkeypatch.setattr(mailer._queue, "put", lambda m: captured.append(m)) monkeypatch.setattr(mailer, "MAIL_FALLBACK_FROM", "noreply@mivanchenko.de") mailer.send_email("bob@example.com", "Subject", "

body

") assert captured[0]["From"] == "noreply@mivanchenko.de"