Apply review polish to the digest send path (post-v0.1.0:76)

Non-blocking items from the v76 reviewer pass. No redeploy needed — the box runs
v76 and its happy path is unaffected; these ride the next build:

- digest_mailer.send_digest: when Gmail is enabled but no sender resolves
  (CRM_DIGEST_SENDER unset and no admin email), raise NoTransport so the caller
  returns a clear 400 instead of a generic 502.
- gmail_send.send_via_gmail: wrap OSError/URLError (timeout/DNS) as a RuntimeError
  ("Gmail API unreachable: ...") to match the HTTPError handling; include the
  sender in the HTTPError message for debuggability.
- credentials.py: correct the now-stale GMAIL_COMPOSE_SCOPE comment (the digest
  mailer sends with this scope; only outreach drafts never send).
- test_gmail_send.py: add the HTTPError->RuntimeError branch, default_sender DB
  fallback (+None case + env override), and the send_digest SMTP-tag path.

19/19 backend tests green.
This commit is contained in:
Keysat
2026-06-15 20:37:49 -05:00
parent 47dfd110a0
commit fee037a630
4 changed files with 78 additions and 3 deletions
+4 -2
View File
@@ -32,8 +32,10 @@ from . import errors
GMAIL_READONLY_SCOPE = "https://www.googleapis.com/auth/gmail.readonly"
# Drafts scope (authorized in Workspace DWD). We only ever CREATE drafts with it; the
# human sends from Gmail. (Google bundles send into this scope, but our code never sends.)
# Compose scope (authorized in Workspace DWD). Two consumers: outreach (compose.py)
# only CREATES drafts — the human sends from Gmail; the daily-digest mailer
# (gmail_send.py) uses this same scope to SEND, since gmail.compose authorizes
# users.messages.send. (The narrow gmail.send scope is NOT on the DWD grant.)
GMAIL_COMPOSE_SCOPE = "https://www.googleapis.com/auth/gmail.compose"
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"