5.6 KiB
Campaign SMTP/IMAP Test Bed
This test bed provides dedicated non-production SMTP/IMAP infrastructure for campaign delivery checks. It uses GreenMail and must never be configured with production recipients or credentials.
Start
cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed
cp .env.example .env
docker compose --env-file .env up -d
Default endpoints:
- SMTP:
127.0.0.1:3025, plain transport - IMAP:
127.0.0.1:3143, plain transport - GreenMail API/UI endpoint:
127.0.0.1:38080
Smoke Test
Run the transport smoke from the core virtual environment after installing the campaign and mail modules:
cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed
set -a
. ./.env
set +a
/mnt/DATA/git/govoplan-core/.venv/bin/python run_transport_smoke.py
The smoke sends and appends three messages:
- no attachment
- one normal attachment
- one password-protected AES ZIP attachment
It verifies SMTP authentication, IMAP authentication, folder listing, delivery
to INBOX, and append-to-Sent behavior.
If the smoke is started immediately after docker compose up -d, GreenMail may
bind the SMTP/IMAP ports before the services are fully ready. The smoke retries
login and folder setup for GOVOPLAN_MAIL_TEST_READY_TIMEOUT_SECONDS.
Campaign Acceptance
The transport smoke proves the Mail adapters. The Campaign acceptance runner
proves the public composition: it creates an isolated temporary Core database,
creates a Mail-owned encrypted profile through the API, materializes the
credential-free greenmail-delivery
fixture with only that profile reference, validates/builds it, sends the exact
generated EML through Campaign once, appends it once, and cross-checks Campaign
report/audit state with one unique-subject message in the GreenMail INBOX and
Sent folders. Provider mailbox verification is not a byte-for-byte comparison
after provider-side header or storage transformations.
cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed
set -a
. ./.env
set +a
/mnt/DATA/git/govoplan/.venv/bin/python run_campaign_acceptance.py \
--evidence /tmp/govoplan-campaign-greenmail-evidence.json
The default run also uses controlled loopback protocol endpoints to prove that
an SMTP connection loss before transmission is temporary, an explicit SMTP
authentication rejection is permanent, a final 451 response after DATA is
temporary, one accepted and one refused RCPT command is retained as partial
envelope acceptance, a connection loss after complete DATA is frozen as
outcome_unknown, and an IMAP authentication rejection after SMTP acceptance
leaves the send accepted while the append fails. A
second ordinary send must be rejected before another provider effect.
The worker drill queues one job, starts the registered
govoplan.campaigns.send_email task body in a dedicated OS process, waits until
the controlled endpoint has received complete DATA, terminates that process,
and starts the same task body in a fresh process. It proves the durable
sending/unfinished-attempt boundary is recovered as outcome_unknown
without a second SMTP connection or DATA transaction. This is a real process
and task-boundary interruption, but it does not start a Celery daemon, Redis
broker, or broker redelivery; celery_broker_redelivery therefore remains
false in the evidence.
The bounded JSON contains no endpoint, account, address, credential, profile,
campaign, version, or job identifiers. It records module versions, the fixture
hash, normalized classifications/counts, the Mail-profile boundary, required
audit actions, provider mailbox increments, and coverage flags. Runtime version
declarations identify the exercised composition; they do not claim that the
sources are clean, tagged, signed, or release-provenanced. The evidence names
them declared_module_versions and keeps source_artifact_provenance false;
exact commit/artifact provenance belongs to the package and release gate.
This runner is restricted to literal loopback IP addresses and the synchronous
Campaign delivery mode. Hostnames such as localhost and every non-loopback
address fail before profile creation, avoiding a DNS change between validation
and connection. It is local target-like evidence, not approval of an
institution's SMTP/IMAP service. The controlled post-DATA, temporary-response,
partial-refusal, and task-process interruption drills are local effect-level
proof, not proof of a target provider's behavior or Redis/Celery broker
redelivery. Use --success-only only when testing the success journey without
the local failure endpoints.
Starting the maintained test bed requires a working Docker CLI, Compose plugin,
daemon/socket access, and permission to pull greenmail/standalone:2.1.9. The
Campaign runner needs only the already-running loopback endpoints; it neither
starts Docker nor claims that it did.
Use With A Campaign
Use the same settings in a campaign mail profile:
- SMTP host
127.0.0.1, port3025, securityplain - IMAP host
127.0.0.1, port3143, securityplain - username and password from
.env - append folder from
GOVOPLAN_MAIL_TEST_SENT_FOLDER
When changing the mailbox, keep GOVOPLAN_MAIL_TEST_USER equal to
${GOVOPLAN_MAIL_TEST_LOCAL_PART}@${GOVOPLAN_MAIL_TEST_DOMAIN} because the
compose file creates the GreenMail account from local part, password, and
domain while login uses the full email address.
Then run the controlled sending checklist from
docs/CAMPAIGN_DELIVERY_RUNBOOK.md for no attachment, one attachment, and
password-protected ZIP campaign variants.
Stop
docker compose --env-file .env down -v