Files
rippy/docker/api/test_mounts_helpers.py
T
Hitonabi 6a082cf77c
Ampel / ampel (push) Successful in 28s
fix(mounts): mounten() idempotent - stale/tote Mounts vor Re-Mount loesen
Folgefix zum remount-Vorfall 24.07.: mounten() fiel bei einem TOTEN Mount
(os.path.ismount wirft OSError) auf den echten `mount` durch und stapelte auf die
Leiche. Ueber viele Neustarts (via rshared propagiert, ueberlebt Container-Recreate)
wuchs das auf 12 Schichten; die tote oberste blockierte jeden Zugriff (ls-Timeout,
obwohl SMB-445 offen) -> Medien-Mount unbrauchbar.

Fix: _stale_mounts_loesen(ziel) loest per lazy `umount -l` alle Schichten, bevor neu
gemountet wird -> kein Stapeln mehr, Re-Mount idempotent. Ein gesunder Mount wird
weiterhin frueh erkannt (os.path.ismount) und unangetastet gelassen.

Tests (test_mounts_helpers.py): Loesch-Schleife bis leer (monkeypatch) + Verdrahtung.
Ruff gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 22:04:00 +02:00

72 lines
2.3 KiB
Python

"""Tests für die SMB-Fehlerübersetzung (Speicherziele → Freigaben auflisten)."""
from mounts import uebersetze_smb_fehler, validiere_name
def test_access_denied_ohne_credentials_erklaert_gastproblem():
meldung = uebersetze_smb_fehler(
"session setup failed: NT_STATUS_ACCESS_DENIED", mit_credentials=False
)
assert "Gast" in meldung
assert "Benutzername + Passwort" in meldung
def test_access_denied_mit_credentials_verweist_auf_konto():
meldung = uebersetze_smb_fehler(
"session setup failed: NT_STATUS_ACCESS_DENIED", mit_credentials=True
)
assert "stimmen nicht" in meldung
def test_logon_failure_wird_uebersetzt():
meldung = uebersetze_smb_fehler("NT_STATUS_LOGON_FAILURE", mit_credentials=True)
assert "Passwort falsch" in meldung
def test_unerreichbar_wird_uebersetzt():
meldung = uebersetze_smb_fehler(
"do_connect: Connection to 10.0.0.9 failed (Error NT_STATUS_IO_TIMEOUT)",
mit_credentials=False,
)
assert "nicht erreichbar" in meldung
def test_unbekannter_fehler_bleibt_erhalten_und_gekappt():
meldung = uebersetze_smb_fehler("X" * 500, mit_credentials=False)
assert meldung == "X" * 200
def test_validiere_name_bleibt_streng():
assert validiere_name("nas-filme")
assert not validiere_name("NAS Filme")
def test_stale_mounts_loesen_loest_bis_nichts_mehr_geht(monkeypatch):
"""Löst gestapelte Schichten per lazy umount, bis umount nichts mehr findet
(returncode != 0), und meldet die Zahl der gelösten Schichten."""
import types
import mounts
aufrufe = []
def fake_run(cmd, **kwargs):
aufrufe.append(cmd)
rc = 0 if len(aufrufe) <= 3 else 1 # 3 Schichten lösen, dann leer
return types.SimpleNamespace(returncode=rc, stdout=b"", stderr=b"")
monkeypatch.setattr(mounts.subprocess, "run", fake_run)
assert mounts._stale_mounts_loesen("/app/media/x") == 3
assert all(cmd[:2] == ["umount", "-l"] for cmd in aufrufe)
def test_mounten_loest_stale_vor_dem_mount():
"""Regression (Vorfall 24.07.): mounten() muss Alt-Mounts LÖSEN, bevor es neu
mountet — sonst stapelt es auf eine Mount-Leiche (12 Schichten, ls-Timeout)."""
import inspect
import mounts
quelle = inspect.getsource(mounts.mounten)
assert "_stale_mounts_loesen(ziel)" in quelle