"""Testet die Uebernahme eines verwaisten Mailbox-Verzeichnisses (--adopt).""" import sys, tempfile from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from kerio_rename_user import ( plan_store_adoption, check_rename_targets, apply_store_renames, count_mails, revert_store_renames, AbortedError, ) STAMP = "20260807-120000" ok = True def check(label, cond): global ok print(f" {'ok ' if cond else 'FEHLER'} {label}") if not cond: ok = False def build_store(with_target=True, target_mails=0): """Baut einen Store: verwaistes Verzeichnis + optional Ziel des neuen Benutzers.""" root = Path(tempfile.mkdtemp()) store = root / "store" alt = store / "mail" / "firma.de" / "anna.mueller" (alt / "INBOX" / "#msgs").mkdir(parents=True) for i in (1, 2, 3): (alt / "INBOX" / "#msgs" / f"0000000{i}.eml").write_text(f"Mail {i}") (alt / ".personal").write_text("vCard von Anna Mueller") (alt / "stats.usr").write_text("Statistik alt") if with_target: neu = store / "mail" / "firma.de" / "anna.schmidt" (neu / "INBOX" / "#msgs").mkdir(parents=True) (neu / ".personal").write_text("vCard von Anna Schmidt") for i in range(target_mails): (neu / "INBOX" / "#msgs" / f"9000000{i}.eml").write_text(f"Zielmail {i}") (store / "mail" / "firma.de" / "fremder").mkdir(parents=True) return store print("== 1. Zielverzeichnis existiert, ist aber leer ==") store = build_store(with_target=True, target_mails=0) moves = plan_store_adoption(store, "firma.de", "anna.mueller", "anna.schmidt", STAMP, force=False) check("zwei Schritte geplant (beiseite + uebernehmen)", len(moves) == 2) check("erst wird das Ziel beiseitegeschoben", ".abgeloest-" in moves[0][1].name and moves[0][0].name == "anna.schmidt") check("dann rueckt die Quelle nach", moves[1][0].name == "anna.mueller" and moves[1][1].name == "anna.schmidt") check_rename_targets(moves) print(" Sequenzpruefung akzeptiert die Abfolge") done = apply_store_renames(moves) ziel = store / "mail" / "firma.de" / "anna.schmidt" check("3 Mails beim neuen Benutzer", count_mails(ziel) == 3) check("alte Mail-Datei inhaltlich da", (ziel / "INBOX" / "#msgs" / "00000001.eml").read_text() == "Mail 1") check("abgeloestes Zielverzeichnis daneben", (store / "mail" / "firma.de" / f"anna.schmidt.abgeloest-{STAMP}").is_dir()) check("Quellverzeichnis weg", not (store / "mail" / "firma.de" / "anna.mueller").exists()) check("Fremder unberuehrt", (store / "mail" / "firma.de" / "fremder").is_dir()) print("\n== 2. Rollback nimmt beide Schritte zurueck ==") revert_store_renames(done) check("Quelle wieder da", (store / "mail" / "firma.de" / "anna.mueller" / "INBOX" / "#msgs" / "00000001.eml").read_text() == "Mail 1") check("Ziel wieder am Platz", (store / "mail" / "firma.de" / "anna.schmidt" / ".personal").read_text() == "vCard von Anna Schmidt") check("kein abgeloestes Verzeichnis mehr", not (store / "mail" / "firma.de" / f"anna.schmidt.abgeloest-{STAMP}").exists()) print("\n== 3. Ziel enthaelt schon Mails -> Abbruch ohne --force ==") store = build_store(with_target=True, target_mails=2) try: plan_store_adoption(store, "firma.de", "anna.mueller", "anna.schmidt", STAMP, force=False) check("haette abbrechen muessen", False) except AbortedError as exc: check("bricht ab und nennt die Anzahl", "2 Mail" in str(exc)) print("\n== 4. ... mit --force geht es, Mails bleiben erhalten ==") moves = plan_store_adoption(store, "firma.de", "anna.mueller", "anna.schmidt", STAMP, force=True) check_rename_targets(moves) apply_store_renames(moves) abgeloest = store / "mail" / "firma.de" / f"anna.schmidt.abgeloest-{STAMP}" check("die 2 Zielmails sind erhalten geblieben", count_mails(abgeloest) == 2) check("die 3 uebernommenen Mails sind am Platz", count_mails(store / "mail" / "firma.de" / "anna.schmidt") == 3) print("\n== 5. Kein Zielverzeichnis vorhanden -> nur ein Schritt ==") store = build_store(with_target=False) moves = plan_store_adoption(store, "firma.de", "anna.mueller", "anna.schmidt", STAMP, force=False) check("genau ein Schritt", len(moves) == 1) check_rename_targets(moves) apply_store_renames(moves) check("Mails angekommen", count_mails(store / "mail" / "firma.de" / "anna.schmidt") == 3) print("\n== 6. Quellverzeichnis gibt es nicht -> klare Meldung ==") try: plan_store_adoption(store, "firma.de", "gibtsnicht", "anna.schmidt", STAMP, force=False) check("haette abbrechen muessen", False) except AbortedError as exc: check("klare Fehlermeldung", "gibtsnicht" in str(exc)) print("\n== 7. Sequenzpruefung erkennt zwei Moves auf dasselbe Ziel ==") store = build_store(with_target=False) d = store / "mail" / "firma.de" try: check_rename_targets([(d / "anna.mueller", d / "ziel"), (d / "fremder", d / "ziel")]) check("haette abbrechen muessen", False) except AbortedError as exc: check("Kollision erkannt", "Zwei Verschiebungen" in str(exc)) # --------------------------------------------------------------------------- print("\n== 8. Kontaktkarte (.personal) umschreiben ==") from kerio_rename_user import rewrite_personal_vcard mb = Path(tempfile.mkdtemp()) VCARD = (b"Subject: Maxi Musterfrau\r\n" b"Date: Fri, 7 Aug 2026 11:33:20 +0000\r\n" b'Content-Type: text/vcard; charset="utf-8"\r\n' b"\r\n" b"BEGIN:VCARD\r\n" b"VERSION:3.0\r\n" b"EMAIL;TYPE=PREF,WORK:maxi.musterfrau@local.lan\r\n" b"FN:Maxi Musterfrau\r\n" b"X-JABBER:maxi.musterfrau@local.lan\r\n" b"END:VCARD\r\n") (mb / ".personal").write_bytes(VCARD) changed = rewrite_personal_vcard(mb, "maxi.musterfrau@local.lan", "anna.neu@local.lan", "Anna Neu") out = (mb / ".personal").read_bytes() check("meldet Aenderung", changed is True) check("EMAIL umgeschrieben", b"EMAIL;TYPE=PREF,WORK:anna.neu@local.lan\r\n" in out) check("X-JABBER umgeschrieben", b"X-JABBER:anna.neu@local.lan\r\n" in out) check("FN umgeschrieben", b"FN:Anna Neu\r\n" in out) check("Subject umgeschrieben", b"Subject: Anna Neu\r\n" in out) check("keine alte Adresse mehr", b"maxi.musterfrau" not in out) check("CRLF erhalten", out.count(b"\r\n") == VCARD.count(b"\r\n")) check("VCARD-Struktur intakt", out.startswith(b"Subject:") and out.rstrip().endswith(b"END:VCARD")) print("\n== 9. Kontaktkarte fehlt -> kein Fehler ==") leer = Path(tempfile.mkdtemp()) check("meldet 'nichts geaendert'", rewrite_personal_vcard(leer, "a@b.c", "d@e.f", "X") is False) print("\n== 10. Nichts zu ersetzen -> Datei bleibt unveraendert ==") mb2 = Path(tempfile.mkdtemp()) (mb2 / ".personal").write_bytes(VCARD) rewrite_personal_vcard(mb2, "fremder@local.lan", "wer@local.lan", None) check("Datei unveraendert", (mb2 / ".personal").read_bytes() == VCARD) print("\n" + ("ALLE TESTS BESTANDEN" if ok else "FEHLGESCHLAGEN")) sys.exit(0 if ok else 1)