feat(admin): add a Test connection button to SMTP settings (BUNYIP-433) #427
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/BUNYIP-433-smtp-test-connection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds a Test connection button to the admin email settings page. One click opens a real SMTP connection to the configured relay, negotiates TLS, and authenticates, without sending any mail, then reports back what succeeded or the specific stage that failed.
Resolves BUNYIP-433.
Why
Until now the only way to verify SMTP config was to trigger a real email and wait to see whether it arrived. PMS-669 is the worked example of what that costs: a rotated password silently broke delivery while everything still looked configured, and it was only caught after verification emails stopped landing. This button is the control that would have caught it in seconds and named the cause.
How it works
POST /v1/admin/email/testloads the saved config and runsEmailService::test_connection. Returns200 { ok, stage, message }for a reached target (a failing relay is a diagnostic result, not an API error); only the rate-limit trip surfaces as 429.AsyncSmtpConnection. A plain TCP reach first, so an unreachable host reports asconnectdistinctly from a TLS error; then the implicit-TLS wrapper or STARTTLS upgrade (tls); then AUTH (auth, the stage that catches a rotated/rejected password). QUIT after AUTH, so no message is ever sent.RateLimitConfig::SMTP_TEST, 6 per 5 minutes per admin, so the button cannot be used to hammer the relay. Runs inside the existing per-route rate-limit floor.AdminEmailConnectionTestedwith host/port/tls/ok/stage. Never the password.Acceptance criteria
authenticate_succeeds_when_relay_accepts).authenticate_reports_auth_stage_when_relay_rejects).test_connection_reports_connect_stage_for_unreachable_host).RateLimitConfig::SMTP_TEST).Testing
just check-containergreen (fmt + clippy-D warnings+cargo test --workspace --all-targets), including 5 new tests. Verified end to end on the dev stack as an admin: the button renders, a POST returns the specific connect-stage diagnostic banner ("Could not connect to localhost:465: Connection refused"), and the audit row lands without the password.Follow-up (out of scope, noted in the ticket)
A periodic background health check that alerts on failure. Useful later; this button answers the immediate "did the settings I just entered work?" question first.
🤖 Generated with Claude Code
https://claude.ai/code/session_01US9AFL2ZTGrLH9TEPSmNfm