#5508 "Every other email through the outbox: weekly report, invites, password resets, and the test buttons" (milestone 501). - outbox.send_each queues one email per reader (claimed, so the sender doesn't take it too) and tries them at once, for a page that answers with what happened; outbox.said words the result. attempt takes a cfg for a test of the form's unsaved settings and returns the settled rows. - Weekly report (scheduled and Send now) and invites: queued, tried at once; one the server couldn't take yet is retried for an hour. An invite that didn't go shows its link with the reason, as before. - Password reset: queued in the reset link's own transaction; the sender delivers it on its next tick. - Settings -> Notifications test email and a rule's Send test: queued and recorded with retry=False, tried once (System 156 ruling). - Removed mail.send, notifications._send_email and _smtp_send_sync; dispatch_notifications is webhook only. A guard asserts only the outbox (and smtp_open itself) opens an SMTP connection, with a self-test. Also fixes run 8941 (#5507's integration test): its Also-send-to addresses were "@test", which is_real_address rightly drops; they are example.com now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
30 lines
1.2 KiB
Python
30 lines
1.2 KiB
Python
"""Email in integration tests: every email is a queued row the outbox sends
|
|
(#5506), so a test catches what the mail server would have been handed by
|
|
replacing the one SMTP conversation, outbox.deliver_sync."""
|
|
from __future__ import annotations
|
|
|
|
from email.message import EmailMessage
|
|
|
|
|
|
def capture(app, monkeypatch, answer=None) -> list[EmailMessage]:
|
|
"""Set a mail server and collect every message the outbox delivers.
|
|
`answer(msg)` is the server's reply to each (None: accepted)."""
|
|
from steward.core import outbox
|
|
sent: list[EmailMessage] = []
|
|
|
|
def deliver(cfg, messages):
|
|
sent.extend(messages)
|
|
return [answer(m) if answer else None for m in messages]
|
|
|
|
app.config["SMTP"] = {**app.config.get("SMTP", {}), "host": "mx.example", "port": 587,
|
|
"from": "steward@example.com"}
|
|
monkeypatch.setattr(outbox, "deliver_sync", deliver)
|
|
return sent
|
|
|
|
|
|
def to(sent: list[EmailMessage], *addresses: str) -> list[EmailMessage]:
|
|
"""The messages among `sent` addressed to any of `addresses` (other
|
|
tests' queued email can go out in the same tick)."""
|
|
want = {a.lower() for a in addresses}
|
|
return [m for m in sent if m["To"].lower() in want]
|