From a6dfe1aaed6d5544f4bd827085aa40e12792df7e Mon Sep 17 00:00:00 2001 From: Oleksii Dolhov Date: Mon, 18 May 2026 14:51:00 +0300 Subject: [PATCH 1/2] fix(config): forward SMTP + SendGrid env to backend container (#771) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit config.py reads SMTP_HOST/PORT/USER/PASSWORD (lines 50-53) and SENDGRID_API_KEY (line 55), but both docker-compose.yml and docker-compose.prod.yml forwarded only SMTP_FROM. Result: EMAIL_PROVIDER=smtp or =sendgrid silently fails with no error — the vars never reach the container. Forward all five in both files. Scope note: #771 listed 5 findings; verified against current dev, only 2 were still legit (this fix). The other 3 are stale (report dated 2026-05-11): - GOOGLE_API_KEY: now documented at .env.example:130 - FRONTEND_URL: single definition (:193); :147 is a deliberate cross-reference comment, not a contradictory duplicate - TRINITY_PASSWORD "changeme": gone — both compose files now use ${ADMIN_PASSWORD} consistently (prod fail-fast :?, local :-) Validated: `docker compose config` passes for both files. Related to #771 Co-Authored-By: Claude Opus 4.7 (1M context) --- docker-compose.prod.yml | 7 +++++++ docker-compose.yml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index bc467382e..addfef2d2 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -33,6 +33,13 @@ services: - EMAIL_PROVIDER=${EMAIL_PROVIDER:-resend} - RESEND_API_KEY=${RESEND_API_KEY:-} - SMTP_FROM=${SMTP_FROM:-noreply@trinity.example.com} + # SMTP transport (EMAIL_PROVIDER=smtp) — read by src/backend/config.py (#771) + - SMTP_HOST=${SMTP_HOST:-} + - SMTP_PORT=${SMTP_PORT:-587} + - SMTP_USER=${SMTP_USER:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + # SendGrid transport (EMAIL_PROVIDER=sendgrid) — read by src/backend/config.py (#771) + - SENDGRID_API_KEY=${SENDGRID_API_KEY:-} - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} - NOTION_CLIENT_ID=${NOTION_CLIENT_ID} diff --git a/docker-compose.yml b/docker-compose.yml index fbdb9b189..e4717772e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,13 @@ services: - EMAIL_PROVIDER=${EMAIL_PROVIDER:-resend} - RESEND_API_KEY=${RESEND_API_KEY:-} - SMTP_FROM=${SMTP_FROM:-noreply@trinity.example.com} + # SMTP transport (EMAIL_PROVIDER=smtp) — read by src/backend/config.py (#771) + - SMTP_HOST=${SMTP_HOST:-} + - SMTP_PORT=${SMTP_PORT:-587} + - SMTP_USER=${SMTP_USER:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + # SendGrid transport (EMAIL_PROVIDER=sendgrid) — read by src/backend/config.py (#771) + - SENDGRID_API_KEY=${SENDGRID_API_KEY:-} # Log Retention & Archival Configuration - LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-90} - LOG_ARCHIVE_ENABLED=${LOG_ARCHIVE_ENABLED:-true} From 560d3c6ceff9fc5d6f9d43e675d73329a3854b5f Mon Sep 17 00:00:00 2001 From: Oleksii Dolhov Date: Mon, 18 May 2026 13:26:15 +0300 Subject: [PATCH 2/2] test(slots): adopt sanctioned _STUBBED_MODULE_NAMES pattern (#871 lint regression) PR #871 added tests/unit/test_slot_per_slot_ttl.py with 6 sys.modules mutations (a local restore fixture + importlib stub injections) but didn't register them with tests/lint_sys_modules.py, turning the `lint (sys.modules pollution check)` gate red on dev and on every branch cut from it. Fix: promote the fixture's local `names` list to a module-level `_STUBBED_MODULE_NAMES` constant (completing the set to also cover the database/models/utils.credential_sanitizer/services.capacity_manager/ cleanup_service_direct stubs the importlib helpers inject). The lint recognises the top-level `_STUBBED_MODULE_NAMES` + `_restore_sys_modules` fixture pair as the sanctioned self-contained snapshot/restore pattern (precedent: tests/unit/test_telegram_webhook_backfill.py) and exempts the file. Bonus: the restore fixture now actually restores every stubbed module, so it no longer leaks into sibling test files. No behavior change to the #869 test logic itself. Related to #871 Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/unit/test_slot_per_slot_ttl.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_slot_per_slot_ttl.py b/tests/unit/test_slot_per_slot_ttl.py index ccaa8494f..f1fdf2900 100644 --- a/tests/unit/test_slot_per_slot_ttl.py +++ b/tests/unit/test_slot_per_slot_ttl.py @@ -47,10 +47,29 @@ sys.path.insert(0, str(_BACKEND)) +# Modules this test stubs into sys.modules (directly or via the +# importlib-loaded slot_service_direct / cleanup_service_direct helpers) +# — restored after each test so they don't leak into other test files in +# the same pytest session. Declaring this at module level (paired with +# the `_restore_sys_modules` fixture below) is the sanctioned +# snapshot/restore pattern recognised by tests/lint_sys_modules.py +# (precedent: tests/unit/test_telegram_webhook_backfill.py). +_STUBBED_MODULE_NAMES = [ + "config", + "redis", + "utils.helpers", + "utils.credential_sanitizer", + "database", + "models", + "services.capacity_manager", + "slot_service_direct", + "cleanup_service_direct", +] + + @pytest.fixture(autouse=True) def _restore_sys_modules(): - names = ["config", "slot_service_direct", "utils.helpers", "redis"] - saved = {n: sys.modules.get(n) for n in names} + saved = {n: sys.modules.get(n) for n in _STUBBED_MODULE_NAMES} try: yield finally: