"""Tests der Rohdaten-Suche — mit dem echten Fall, der sie nötig gemacht hat. Job `95afdc89` (26.07.2026, an der laufenden Instanz gemessen): 79,6 GB Rohschnitt unter /app/media/rippy/, Einstellung `workDir` leer, `can_retry` = false. Der Knopf „Neu komprimieren" fehlte, obwohl die Datei intakt war. """ import rohdaten JOB = "95afdc89-2426-4d44-829e-ad6ce1411905" def test_der_echte_fall_wird_gefunden(): """workDir ist LEER (so stand es live) und der Rohschnitt liegt trotzdem auf der NAS — weil beim Start eine Wahl nur für diesen Rip getroffen wurde.""" orte = rohdaten.kandidaten(JOB, "", ["movies", "music", "rippy", "series"]) assert f"/app/media/rippy/{JOB}" in orte # Der Container-Standard bleibt der erste Kandidat (schnellster Treffer) assert orte[0] == f"/app/temp/raw/{JOB}" def test_suche_liefert_nur_was_existiert(): vorhanden = {f"/app/media/rippy/{JOB}"} gefunden = rohdaten.suche( JOB, "", listdir=lambda p: ["movies", "rippy"], isdir=lambda p: p in vorhanden, ) assert gefunden == [f"/app/media/rippy/{JOB}"] def test_eingestelltes_arbeitsverzeichnis_kommt_vor_den_zielen(): orte = rohdaten.kandidaten(JOB, "/app/media/rippy", ["movies", "rippy"]) assert orte[1] == f"/app/media/rippy/{JOB}" # ... und taucht nicht doppelt auf, obwohl „rippy" auch Ablageziel ist assert orte.count(f"/app/media/rippy/{JOB}") == 1 def test_arbeitsverzeichnis_ausserhalb_media_wird_ignoriert(): """Nur /app/media ist erlaubt (so entscheidet _arbeitsverzeichnis im Worker) — ein Pfad daneben darf hier nicht durchrutschen.""" orte = rohdaten.kandidaten(JOB, "/etc", []) assert orte == [f"/app/temp/raw/{JOB}"] # Der Klassiker: ein Pfad, der nur mit dem Präfix ANFÄNGT orte = rohdaten.kandidaten(JOB, "/app/media-boese", []) assert orte == [f"/app/temp/raw/{JOB}"] def test_media_root_selbst_ist_erlaubt(): orte = rohdaten.kandidaten(JOB, "/app/media", []) assert f"/app/media/{JOB}" in orte def test_ohne_job_id_nichts(): assert rohdaten.kandidaten("", "/app/media", ["x"]) == [] def test_kaputter_mount_reisst_die_suche_nicht_mit(): """Ein toter CIFS-Mount lässt isdir mit OSError fliegen. Das darf die Job-Liste nicht mitnehmen (Befund 24.07. bei /storage-targets).""" def isdir_kaputt(p): if "totes-nas" in p: raise OSError("Stale file handle") return p == f"/app/temp/raw/{JOB}" gefunden = rohdaten.suche( JOB, "", listdir=lambda p: ["totes-nas", "movies"], isdir=isdir_kaputt) assert gefunden == [f"/app/temp/raw/{JOB}"] def test_listdir_kaputt_faellt_auf_den_standard_zurueck(): def listdir_kaputt(p): raise OSError("kein /app/media") gefunden = rohdaten.suche( JOB, "", listdir=listdir_kaputt, isdir=lambda p: True) assert gefunden == [f"/app/temp/raw/{JOB}"] # --- Größe ------------------------------------------------------------------ def test_groesse_zaehlt_nur_dateien(): dateien = { f"/app/media/rippy/{JOB}/title_t00.mkv": 79604951639, f"/app/media/rippy/{JOB}/title_t01.mkv": 1000, } bytes_gesamt, anzahl = rohdaten.groesse( [f"/app/media/rippy/{JOB}"], listdir=lambda p: ["title_t00.mkv", "title_t01.mkv", "unterordner"], isfile=lambda p: p in dateien, getsize=lambda p: dateien[p], ) # Die echte Größe des Akira-Rohschnitts, plus eine zweite Datei assert bytes_gesamt == 79604952639 assert anzahl == 2 assert round(bytes_gesamt / 1024**3, 1) == 74.1 def test_groesse_ohne_pfade_ist_null(): assert rohdaten.groesse([], lambda p: [], lambda p: True, lambda p: 1) == (0, 0) assert rohdaten.groesse(None, lambda p: [], lambda p: True, lambda p: 1) == (0, 0) def test_groesse_ueberspringt_unlesbares(): def getsize_kaputt(p): raise OSError("weg") bytes_gesamt, anzahl = rohdaten.groesse( ["/x"], lambda p: ["a.mkv"], lambda p: True, getsize_kaputt) assert (bytes_gesamt, anzahl) == (0, 0)