Files
stack/tests/mail/conftest.py
kert c0493a0b8f
Some checks failed
Deploy / docs (push) Has been skipped
Infra CI / notebooks (push) Failing after 16s
Infra CI / api (push) Successful in 32s
Deploy / report (push) Successful in 14s
CI / lint (push) Successful in 28s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Infra CI / zotero (push) Successful in 14s
Infra CI / docs (push) Successful in 15s
Infra CI / mc (push) Successful in 12s
CI / test (push) Successful in 12m27s
Harden / build-scan-report (push) Failing after 42s
Renovate / renovate (push) Successful in 16s
Package Supply Chain / pkg-supply-chain (push) Failing after 49s
fix(mail): IMAP-acct invariant, test isolation, mail-poller healthcheck
Three pre-existing bugs surfaced by the postmaster@mail.fhirworx.io
DMARC setup; all fixed together because they interact.

1. `seed_mailboxes` and `rotate_creds` only created the IMAP mailbox
   when the SMTP-creds `password` path failed (i.e. first-time
   provision). If creds existed but imap-acct didn't — observed for
   postmaster@mail.fhirworx.io after a partial earlier run — auth
   would succeed and delivered mail would have nowhere to land,
   silently dropping e.g. DMARC aggregate reports.

   Both functions now list `maddy imap-acct` once per droplet visit
   and ensure each address has its IMAP store, independently of the
   SMTP store. Added `_maddy_imap_accts` + `_ensure_imap_acct`
   helpers.

2. `tests/mail/test_droplet_exercise.py::TestDown::test_noop_when_none`
   called real `down(confirm=True)` while mocking only `_do_client`.
   `down()` then ran `(DROPLET_JSON, CREDS_JSON).unlink(missing_ok=
   True)` on the live state files. The pre-commit pytest hook
   destroyed production credentials.json + droplet.json this way.

   Added `tests/mail/conftest.py` with an autouse fixture that
   monkeypatches STATE_DIR / CREDS_JSON / DROPLET_JSON / GIT_MAILER_ENV
   to a tmp_path for every test in `tests/mail/`. Belt-and-suspenders:
   future tests in this dir can't reach the real filesystem even if
   they forget to patch.

3. `mail-poller` inherited a `HEALTHCHECK` from the `api` image
   probing `http://localhost:8000/health`, but the container runs
   only a polling loop with no HTTP server, so it stayed
   permanently `unhealthy`.

   Compose now overrides the inherited healthcheck. The poll loop
   touches `/tmp/heartbeat` after each iteration; the healthcheck
   verifies it's been touched within `2 * MAIL_POLL_INTERVAL`
   seconds. `start_period: 120s` covers the first cold poll.

Existing seed/rotate tests updated to expect the extra ssh calls
introduced by the imap-acct check; added one new test covering the
"imap-acct already present" path.
2026-05-21 08:58:18 -04:00

24 lines
893 B
Python

"""Isolate every test in tests/mail/ from real mail state on disk.
Without this, a test that exercises real code paths (e.g. `down()` with only
the DigitalOcean client mocked) will run `unlink(missing_ok=True)` on the
production `CREDS_JSON` and `DROPLET_JSON`. The pre-commit pytest hook
silently destroyed live mail state this way in May 2026.
"""
from __future__ import annotations
import pytest
@pytest.fixture(autouse=True)
def _isolate_mail_state(tmp_path, monkeypatch):
state_dir = tmp_path / "mail"
state_dir.mkdir()
git_dir = tmp_path / "git"
git_dir.mkdir()
monkeypatch.setattr("mail.droplet.STATE_DIR", state_dir)
monkeypatch.setattr("mail.droplet.CREDS_JSON", state_dir / "credentials.json")
monkeypatch.setattr("mail.droplet.DROPLET_JSON", state_dir / "droplet.json")
monkeypatch.setattr("mail.droplet.GIT_MAILER_ENV", git_dir / "mailer.env")