Der Test gegen einen echten Server (Kerio Connect 10.0.9 patch 2) hat gezeigt, dass Users.set das Feld loginName stillschweigend ignoriert - kein Fehler, keine Wirkung, waehrend fullName im selben Aufruf sauber uebernommen wird. Vier Varianten geprueft (nur loginName, mit domainId, mit leeren emailAddresses, vollstaendiges User-Objekt): alle wirkungslos. Der bisherige Ansatz konnte also gar nicht funktionieren. Der Login-Name steht in users.cfg, und zwar in zwei Listen: User und UserAdditionalData. Beide verweisen ueber Name+Domain statt ueber die Guid, beide muessen mit, sonst verliert der Benutzer seine Passworthistorie. Geaendert wird der Rohtext, damit Formatierung und unbekannte Felder unangetastet bleiben. users.cfg und Store-Verzeichnis wandern jetzt in EINEM Stopp-Fenster. Laeuft Kerio zwischendurch mit nur einer Haelfte, legt es die vermeintlich fehlende Mailbox sofort neu an - beim Entwickeln genau so passiert. Damit entfaellt auch der Admin-Zugang: das Script braucht keine Zugangsdaten mehr, --list zeigt die Benutzer aus der Datei. Ausserdem gefunden: itemSource heisst 'DSInternalSource', nicht 'Internal' wie angenommen - die alte Pruefung haette bei jedem lokalen Benutzer faelschlich abgebrochen. Die Erkennung laeuft jetzt ueber InternalDb in users.cfg. Tests fuer beide Haelften unter tests/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
80 lines
2.9 KiB
Python
80 lines
2.9 KiB
Python
"""Testet die Store-Logik gegen einen nachgebauten Kerio-Store."""
|
|
import sys, tempfile, os
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
from kerio_rename_user import (
|
|
plan_store_renames, check_rename_targets, apply_store_renames,
|
|
find_dir_ci, AbortedError, revert_store_renames,
|
|
)
|
|
|
|
root = Path(tempfile.mkdtemp())
|
|
store = root / "store"
|
|
|
|
# Kerio-Store nachbauen: mail/ und archive/, Verzeichnisse klein geschrieben
|
|
for sub in ("mail", "archive"):
|
|
d = store / sub / "firma.de" / "anna.mueller"
|
|
d.mkdir(parents=True)
|
|
(d / "INBOX").mkdir()
|
|
(d / "INBOX" / "msg.eml").write_text("hallo")
|
|
# ein anderer Benutzer, der nicht angefasst werden darf
|
|
(store / "mail" / "firma.de" / "bob").mkdir(parents=True)
|
|
|
|
print("== 1. Planung ==")
|
|
renames = plan_store_renames(store, "firma.de", "anna.mueller", "anna.schmidt")
|
|
for s, t in renames:
|
|
print(f" {s.relative_to(store)} -> {t.relative_to(store)}")
|
|
assert len(renames) == 2, renames
|
|
check_rename_targets(renames)
|
|
|
|
print("\n== 2. Domain case-insensitiv (FIRMA.DE) ==")
|
|
r2 = plan_store_renames(store, "FIRMA.DE", "Anna.Mueller", "anna.schmidt")
|
|
assert len(r2) == 2, r2
|
|
print(" ok, findet trotz Gross-/Kleinschreibung")
|
|
|
|
print("\n== 3. Zielverzeichnis belegt -> Abbruch ==")
|
|
(store / "mail" / "firma.de" / "anna.schmidt").mkdir()
|
|
try:
|
|
check_rename_targets(renames)
|
|
print(" FEHLER: haette abbrechen muessen")
|
|
sys.exit(1)
|
|
except AbortedError as e:
|
|
print(f" ok: {e}")
|
|
(store / "mail" / "firma.de" / "anna.schmidt").rmdir()
|
|
|
|
print("\n== 4. Benutzer ohne Verzeichnis -> leere Liste, kein Abbruch ==")
|
|
r4 = plan_store_renames(store, "firma.de", "gibtsnicht", "neu")
|
|
assert r4 == [], r4
|
|
print(" ok, leere Liste (users.cfg wird dann trotzdem geaendert)")
|
|
|
|
print("\n== 5. (dry-run wird jetzt im Aufrufer behandelt) ==")
|
|
pass # dry-run liegt jetzt im Aufrufer
|
|
assert (store / "mail" / "firma.de" / "anna.mueller").is_dir()
|
|
print(" ok, Quelle noch da")
|
|
|
|
print("\n== 6. Tatsaechliches Verschieben ==")
|
|
done = apply_store_renames(renames)
|
|
assert len(done) == 2
|
|
for sub in ("mail", "archive"):
|
|
neu = store / sub / "firma.de" / "anna.schmidt"
|
|
assert neu.is_dir(), neu
|
|
assert (neu / "INBOX" / "msg.eml").read_text() == "hallo"
|
|
assert not (store / sub / "firma.de" / "anna.mueller").exists()
|
|
assert (store / "mail" / "firma.de" / "bob").is_dir(), "anderer Benutzer angefasst!"
|
|
print(" ok, Inhalte erhalten, Fremdbenutzer unberuehrt")
|
|
|
|
print("\n== 7. Rollback ==")
|
|
revert_store_renames(done)
|
|
for sub in ("mail", "archive"):
|
|
assert (store / sub / "firma.de" / "anna.mueller" / "INBOX" / "msg.eml").read_text() == "hallo"
|
|
print(" ok, alles zurueck")
|
|
|
|
print("\n== 8. Store-Pfad falsch -> klare Meldung ==")
|
|
try:
|
|
plan_store_renames(root / "gibtsnicht", "firma.de", "a", "b")
|
|
sys.exit(1)
|
|
except AbortedError as e:
|
|
print(f" ok: {str(e)[:70]}...")
|
|
|
|
print("\nALLE TESTS BESTANDEN")
|