4.2 KiB
mailu — recipe-maintainer → cc-ci parity (Phase 2 P2)
P2 is VACUOUS for mailu: there is no recipe-info/mailu/tests/ corpus in the recipe-maintainer
workspace (/srv/recipe-maintainer), so there are no recipe-maintainer tests to port. Coverage is
therefore health + recipe-specific functional tests (P3), authored from what mailu is (a full
email stack: nginx front + admin + postfix/smtp + dovecot/imap + rspamd/antispam + webmail + redis).
cc-ci deployment notes
COMPOSE_FILE=compose.yml(base). recipe_meta.EXTRA_ENV(domain) pinsMAIL_DOMAIN/HOSTNAMESto the per-run domain,TRAEFIK_STACK_NAME=traefik_ci_commoninternet_net(so the external*_letsencryptvolume the certdumper mounts resolves), andTLS_FLAVOR=notls— mailu's mail-port TLS normally comes fromcertdumperdumping traefik's ACMEacme.json, but cc-ci uses a file-provider wildcard cert (no ACME), so there is no acme.json;notlsremoves that dependency. (certdumper still runs idle; harmless — it converges 1/1.) Web/admin is served over the real wildcard TLS via Traefik. Mail ports 25/465/587/110/143/993/995 are published mode:host → on-host (cc-ci-run) tests reach SMTP/IMAP at 127.0.0.1.
Recipe-specific functional tests (P3 — ≥2)
custom/test_mailbox.py— §4.3 create-an-object + read-back: create a mailbox via the admin container'sflask mailu userCLI, then read it back fromflask mailu config-export --jsonand assert the address is present (admin-DB provisioning round-trip).custom/test_mail_flow.py— the characteristic end-to-end mail flow: INJECT a uniquely-marked message to the mailbox via the postfix container's localsendmail(locally-originated → not greylisted), then VERIFY delivery+storage via dovecot'sdoveadm searchin the imap container — a real postfix → rspamd → dovecot deliver/store/fetch round-trip. We use the in-container mail tools (not the host network ports) because TLS_FLAVOR=notls makes dovecot refuse plaintext auth over the network (143); the in-container path exercises the same delivery/storage stack. (A network IMAP-auth test was dropped: under notls dovecot disallows plaintext network auth, so a host-side login is not a meaningful signal here.)
Backup data-integrity (P4) — COVERED (phase-mailu, 2026-06-11)
P4 is now earned via recipe-mirror PR#3 (add-backupbot-labels) on
git.autonomic.zone/recipe-maintainers/mailu. That PR adds backupbot v2 labels to the admin
service (/data — sqlite DB) and imap service (/mail — Maildir), making backup_capable=True
at the PR head.
cc-ci tests (all in tests/mailu/):
ops.py—pre_backup: seedscitest@<domain>viaflask mailu user+ injects a uniquely-tagged message (ccci-backup-probe) into the INBOX viasendmailin the smtp container → verified withdoveadm searchin the imap container.pre_restore: (1) deletes the account from sqlite via Python; (2)rm -rf /mail/<domain>/citestin the imap container to wipe the Maildir — both volumes are wiped independently so each must be restored to pass.test_backup.py— two tests:test_backup_captures_mailboxassertscitest@<domain>is inconfig-exportat backup time (SQLite/data);test_backup_captures_mail_messageasserts theccci-backup-probemessage is in INBOX (Maildir/mail).test_restore.py— two tests:test_restore_returns_mailboxasserts the account is back inconfig-exportafter restore (SQLite/data);test_restore_returns_mail_messageasserts theccci-backup-probemessage is back in INBOX (Maildir/mail).
Both volumes (/data SQLite + /mail Maildir) are specifically seeded, independently wiped, and
verified restored — proven in drone build #477 (all four backup/restore tests PASS, L5).
Auto-detected: generic.backup_capable() scans the compose.yml for backupbot.backup.*true and
returns True at the PR head — no BACKUP_CAPABLE override needed in recipe_meta.py.
Browser flow (P6)
Not added: mailu's user-facing UX (webmail/admin) is a standard web UI; the characteristic behaviour (mail send/receive, account auth) is covered functionally above. No Playwright flow owed.