feat(mailu): add ops.py + backup/restore tests + update PARITY.md (P4 now covered via PR#3)
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing

This commit is contained in:
autonomic-bot
2026-06-11 20:41:33 +00:00
parent ccabad8209
commit 4b5051f003
4 changed files with 102 additions and 7 deletions

View File

@ -28,13 +28,21 @@ email stack: nginx front + admin + postfix/smtp + dovecot/imap + rspamd/antispam
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) — N/A (recipe ships no backup config)
The upstream mailu recipe declares **no `backupbot.backup` label** on any service, so the cc-ci
backup/restore tiers cleanly SKIP (`backup_capable=False`). There is no recipe backup mechanism to
exercise — P4 is genuinely N/A for mailu as published, not a cut corner. The durable fix (if P4
coverage is wanted) is a recipe-PR adding backupbot labels (mailu admin sqlite at /data + mail
volume), filed as a deferral mirroring the immich Q3.5 / Q3.2b pattern — see DEFERRED.md. Pending
Adversary §7.1 sign-off on the N/A.
## 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`: seeds `citest@<domain>` via `flask mailu user`; `pre_restore`: deletes
it via sqlite3 Python to simulate data loss.
- `test_backup.py` — asserts `citest@<domain>` is in `config-export` at backup time.
- `test_restore.py` — asserts `citest@<domain>` is back in `config-export` after restore.
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

36
tests/mailu/ops.py Normal file
View File

@ -0,0 +1,36 @@
"""mailu — pre-op seed hooks. Creates / deletes a test mailbox in the admin sqlite DB to prove
backup→restore data integrity on real mail data (P4 coverage, phase-mailu)."""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
from harness import lifecycle # noqa: E402
sys.path.insert(0, os.path.dirname(__file__))
import _mailu # noqa: E402
_CI_LOCALPART = "citest"
_CI_PASSWORD = "CcCi-BackupTest1!Aa"
def pre_backup(ctx):
_mailu.ensure_domain(ctx.domain, ctx.domain)
_mailu.create_user(ctx.domain, _CI_LOCALPART, ctx.domain, _CI_PASSWORD)
def pre_restore(ctx):
# Delete the seeded user directly from sqlite to simulate data loss before restore.
# (flask mailu has no user-delete subcommand in 2024.06.52; sqlite3 module is always available.)
lifecycle.exec_in_app(
ctx.domain,
[
"python3",
"-c",
f"import sqlite3; db=sqlite3.connect('/data/main.db'); "
f"db.execute(\"DELETE FROM user WHERE localpart='{_CI_LOCALPART}'\"); db.commit()",
],
service="admin",
)

View File

@ -0,0 +1,25 @@
"""mailu — BACKUP overlay: assert the seeded mailbox is present at backup time.
ops.pre_backup created citest@<domain> via the admin container; this overlay verifies the admin
sqlite DB contains that user at the moment the backup is taken. The backup→restore divergence
is in ops.pre_restore (which deletes the user before restore runs)."""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
import _mailu # noqa: E402
_CI_LOCALPART = "citest"
def test_backup_captures_mailbox(live_app):
email = f"{_CI_LOCALPART}@{live_app}"
cfg = _mailu.config_export(live_app)
emails = _mailu.user_emails(cfg)
assert email in emails, (
f"seeded mailbox {email!r} not found in config-export at backup time; "
f"users present: {emails}"
)

View File

@ -0,0 +1,26 @@
"""mailu — RESTORE overlay: assert the seeded mailbox is back after restore.
ops.pre_restore deleted citest@<domain> from the admin sqlite to simulate data loss; this
overlay verifies that abra app restore brings the user back (the /data sqlite volume is
restored from the backup taken by pre_backup, which contained the seeded user)."""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
import _mailu # noqa: E402
_CI_LOCALPART = "citest"
def test_restore_returns_mailbox(live_app):
email = f"{_CI_LOCALPART}@{live_app}"
cfg = _mailu.config_export(live_app)
emails = _mailu.user_emails(cfg)
assert email in emails, (
f"seeded mailbox {email!r} not found in config-export after restore; "
f"restore did not return the pre-mutation mail data. "
f"users present: {emails}"
)