feat(ui): die Oberflaeche weiss jetzt, worauf sie laeuft
Commander-Befund 28.08.2026, zum Windows-Fenster:
Worker erreichbar: 0 von 1
Kein Worker antwortet — Pruefen: docker compose ps
Container-Platte: unbekannt
Freigaben: keine eingehaengt
Kein Satz davon ergibt auf einem Windows-PC einen Sinn. Sein Urteil: "Du hast
ja quasi nur rippy genommen und die docker installation fuer Windows gebaut.
Das gilt fuer die ganze standalone version fuer Windows, auch fuer die
settings und die Anleitung usw."
## Die Ursache war nicht die Anzeige
Die naheliegende Reparatur waere ein `if (windows)` an dreissig Stellen
gewesen -- dieselbe Falle noch einmal, nur mit einer zweiten Sorte Vermutung.
Es fehlte etwas anderes: Das UI hat nie erfahren, worauf es laeuft. config.py
kennt das Profil seit V2-2, weitergegeben wurde es nie. Also hat das UI
angenommen.
Neu: GET /betrieb meldet FAEHIGKEITEN, keinen Modus-Namen.
externe_worker Gibt es andere Maschinen, die Jobs uebernehmen?
freigaben_einhaengen Kann Rippy Netzwerk-Freigaben selbst einhaengen?
container_pfade Sind Pfade wie /app/media ueberhaupt gemeint?
werkzeuge_verwalten Kann Rippy MakeMKV/HandBrake selbst beschaffen?
Ein Modus-Name wuerde das UI zwingen, aus einem Namen auf Verhalten zu
schliessen -- und das bricht beim naechsten Betriebsfall: Ein
Docker-All-in-One hat Container-Pfade, aber keinen zweiten Worker.
## Was sich sichtbar aendert (auf Windows nachgemessen)
Server-Status "Rippy arbeitet: auf diesem Rechner" statt Worker-Zaehler
"Platz fuer Rippy: 59.2 von 232 GB frei" statt "unbekannt"
Freigaben-Block faellt weg
Einstellungen kein Reiter "Worker"; Speicherziele BLEIBT (dort steht die
Ablage -- "wo will ich das hinspeichern" war die Frage),
aber mit Pfadfeld statt Container-Auswahl und ohne die
Maske zum Einhaengen
Anleitung kein docker, kein Encoding-Worker-Abschnitt, stattdessen
der echte Ordner (C:\Users\...\Videos\Rippy)
## Zwei Fehler, die dabei aufgefallen sind
* MEDIA_ROOT = "/app/media" war in main.py fest verdrahtet. shutil.disk_usage
warf unter Windows, die Liste blieb leer -- daher "unbekannt", obwohl auf
dem Laufwerk 59 GB frei waren. Eine Nichtauskunft, die wie eine Auskunft
aussieht. platz_orte() liefert die Orte jetzt je Betrieb, und ein noch
nicht angelegter Ordner faellt auf das naechste vorhandene Elternteil
zurueck.
* Der SSE-Schnappschuss enthielt den Server-Zustand NICHT, und der Waechter
schickt ihn nur alle 15 Sekunden. Nach jedem Neuladen stand deshalb bis zu
eine Viertelminute "unbekannt" da. Jetzt ist er im Schnappschuss, und das
UI uebernimmt ihn auch von dort.
Dazu: der Windows-Skip in test_api_smoke.py ist weg. Er stammte aus der Zeit
vor V2-4, als main.py fcntl brauchte; seit der Treiberwahl ueber den Port
laedt es auf beiden Plattformen (57 Routen, gemessen). Damit laufen 20 Tests
mehr auch lokal statt nur auf der Ampel.
Ampel lokal: 654 gruen, ruff sauber. Docker-Zweig durch Unit-Tests gedeckt,
am echten Container noch nicht gegengeprueft -- das kommt beim Deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a5e64f8e56
commit
66642b8d93
@@ -0,0 +1,208 @@
|
||||
"""In welchem Betrieb läuft Rippy — und was kann dieser Betrieb überhaupt?
|
||||
|
||||
## Der Befund des Commanders (28.08.2026)
|
||||
|
||||
Im Windows-Fenster stand auf der Server-Status-Kachel:
|
||||
|
||||
Worker erreichbar: 0 von 1
|
||||
Kein Worker antwortet — ohne ihn läuft kein Rip. Prüfen: docker compose ps
|
||||
Container-Platte: unbekannt
|
||||
Freigaben: keine eingehängt
|
||||
|
||||
Kein einziger dieser Sätze ergibt auf einem Windows-PC einen Sinn. Es gibt
|
||||
keinen Container, kein `docker compose`, keinen zweiten Worker — Rippy rippt
|
||||
dort selbst. Sein Urteil:
|
||||
|
||||
> „Du hast ja quasi nur rippy genommen und die docker installation für Windows
|
||||
> gebaut. Das macht ja aber keinen sinn da auf Windows andere dinge relevant
|
||||
> sind als für den Windows Client. Das gilt für die ganze standalone version
|
||||
> für Windows, auch für die settings und die Anleitung usw."
|
||||
|
||||
## Warum das kein Anzeigefehler ist
|
||||
|
||||
Die naheliegende Reparatur wäre, an dreißig Stellen `if (windows)` einzubauen.
|
||||
Das wäre dieselbe Falle noch einmal: Die Oberfläche wüsste weiterhin nichts
|
||||
über ihren Betrieb, sie hätte nur eine zweite Sorte Vermutung.
|
||||
|
||||
Die Ursache liegt tiefer: **Die Oberfläche hat nie erfahren, worauf sie
|
||||
läuft.** `config.py` kennt das Profil seit V2-2, die API hat es nie
|
||||
weitergegeben. Also hat das UI angenommen — und Annahmen sind in diesem
|
||||
Projekt schon oft genug teuer geworden.
|
||||
|
||||
## Warum FÄHIGKEITEN und nicht ein Modus-Name
|
||||
|
||||
Ein `modus == "standalone"` würde die Oberfläche zwingen, aus einem Namen auf
|
||||
Verhalten zu schließen — genau die Sorte Ableitung, die bei jedem neuen
|
||||
Betriebsfall bricht. Der Kopflos-Betrieb (V2-6) hat keinen Container, aber
|
||||
sehr wohl externe Worker; ein Docker-All-in-One hat einen Container, aber
|
||||
keinen zweiten Worker.
|
||||
|
||||
Deshalb sagt Rippy, **was geht**, nicht **wie es heißt**:
|
||||
|
||||
externe_worker Gibt es andere Maschinen, die Jobs übernehmen?
|
||||
freigaben_einhaengen Kann Rippy Netzwerk-Freigaben selbst einhängen?
|
||||
container_pfade Sind Pfade wie /app/media überhaupt gemeint?
|
||||
werkzeuge_verwalten Kann Rippy MakeMKV/HandBrake selbst beschaffen?
|
||||
|
||||
Der Modus-Name bleibt trotzdem dabei — für Fehlerberichte und Überschriften.
|
||||
Aber gerechnet wird mit den Fähigkeiten.
|
||||
|
||||
## Was hier GEMESSEN und was gelesen wird
|
||||
|
||||
Wo es geht, wird nachgesehen statt geglaubt:
|
||||
|
||||
im_container `/.dockerenv` existiert (Docker legt sie an)
|
||||
plattform `sys.platform`
|
||||
externe_worker Queue-Treiber `celery` (ohne Broker gibt es keine)
|
||||
|
||||
`profil` aus der Konfiguration ist der Rückfall, nicht die erste Quelle.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Docker legt diese Datei in jedem Container an. Sie ist das verlässlichste
|
||||
# Kennzeichen, das ohne Zusatzpakete zu haben ist — verlässlicher als eine
|
||||
# Umgebungsvariable, die jeder setzen (und vergessen) kann.
|
||||
DOCKER_KENNZEICHEN = "/.dockerenv"
|
||||
|
||||
|
||||
def im_container(pruefen=None) -> bool:
|
||||
"""Läuft dieser Prozess in einem Container? (gemessen, nicht geraten)"""
|
||||
pruefen = pruefen or os.path.exists
|
||||
return bool(pruefen(DOCKER_KENNZEICHEN))
|
||||
|
||||
|
||||
def plattform(name: str = None) -> str:
|
||||
"""`windows`, `linux` oder `macos` — nie der rohe sys.platform-String."""
|
||||
roh = (name if name is not None else sys.platform).lower()
|
||||
if roh.startswith("win"):
|
||||
return "windows"
|
||||
if roh == "darwin":
|
||||
return "macos"
|
||||
return "linux"
|
||||
|
||||
|
||||
def modus(werte: dict, container: bool = None) -> str:
|
||||
"""Der Name des Betriebs — für Überschriften und Fehlerberichte.
|
||||
|
||||
`standalone` Ein Prozess macht alles (Windows-App, Docker-All-in-One)
|
||||
`verteilt` Mehrere Maschinen teilen sich die Arbeit (Celery/Redis)
|
||||
"""
|
||||
queue = (werte or {}).get("queue", {}) or {}
|
||||
if queue.get("treiber") == "celery":
|
||||
return "verteilt"
|
||||
profil = (werte or {}).get("profil", "standalone")
|
||||
return "verteilt" if profil in ("api", "node") else "standalone"
|
||||
|
||||
|
||||
def auskunft(werte: dict, container: bool = None,
|
||||
plattform_name: str = None) -> dict:
|
||||
"""Was ist dieser Betrieb, und was kann er? — die Antwort für die API.
|
||||
|
||||
Reine Funktion: `container` und `plattform_name` sind einspritzbar, damit
|
||||
jeder Betriebsfall prüfbar ist, ohne ihn herzustellen.
|
||||
"""
|
||||
werte = werte or {}
|
||||
container = im_container() if container is None else container
|
||||
system = plattform(plattform_name)
|
||||
art = modus(werte, container)
|
||||
verteilt = art == "verteilt"
|
||||
|
||||
return {
|
||||
"modus": art,
|
||||
"plattform": system,
|
||||
"im_container": container,
|
||||
"kann": {
|
||||
# Ohne Celery gibt es niemanden, der von außen Jobs übernimmt.
|
||||
# Die Worker-Liste, das Worker-Setup und jede Rede von
|
||||
# „Kein Worker antwortet" gehören dann nicht auf den Bildschirm.
|
||||
"externe_worker": verteilt,
|
||||
# Einhängen macht seit dem Entscheid vom 28.08.2026 der HOST
|
||||
# (KONZEPT-V2.md § 4.3) — im Container zeigt Rippy nur noch die
|
||||
# Zeile zum Kopieren. Unter Windows hängt man gar nichts ein:
|
||||
# Dort gibt man einen UNC-Pfad an, fertig.
|
||||
"freigaben_einhaengen": container,
|
||||
# `/app/media` ist ein Pfad IM Container. Auf Windows heißt der
|
||||
# Ordner anders und liegt woanders.
|
||||
"container_pfade": container,
|
||||
# Im Image steckt alles fest; ein Update ist ein Rebuild. Nur die
|
||||
# native Installation kann ihre Werkzeuge selbst pflegen.
|
||||
"werkzeuge_verwalten": not container,
|
||||
},
|
||||
# Was in der Oberfläche als Vorgabe stehen soll. Ein Feld, das mit
|
||||
# `/app/media` vorbelegt ist, ist auf Windows schlicht falsch.
|
||||
"ablage_vorgabe": ablage_vorgabe(werte, container, system),
|
||||
"hilfe_befehl": "docker compose -p rippy ps" if container else "",
|
||||
}
|
||||
|
||||
|
||||
def naechster_vorhandener(pfad: str, existiert=None) -> str:
|
||||
"""Der nächste Ordner nach oben, den es WIRKLICH gibt. "" wenn keiner.
|
||||
|
||||
Frisch installiert gibt es den Ablage-Ordner noch nicht — `disk_usage`
|
||||
wirft dann, und im Dashboard stand „unbekannt". Der Nutzer will aber
|
||||
wissen, ob auf dem LAUFWERK Platz ist, und das lässt sich beantworten.
|
||||
"""
|
||||
existiert = existiert or os.path.isdir
|
||||
pfad = (pfad or "").rstrip("\\/")
|
||||
gesehen = set()
|
||||
while pfad and pfad not in gesehen:
|
||||
if existiert(pfad):
|
||||
return pfad
|
||||
gesehen.add(pfad)
|
||||
eltern = os.path.dirname(pfad)
|
||||
if eltern == pfad: # Wurzel erreicht
|
||||
break
|
||||
pfad = eltern
|
||||
return ""
|
||||
|
||||
|
||||
def platz_orte(werte: dict, container: bool = None,
|
||||
plattform_name: str = None) -> list:
|
||||
"""Wo Platz gemessen werden soll — je Betrieb andere Orte.
|
||||
|
||||
## Warum das nicht fest verdrahtet sein darf (28.08.2026)
|
||||
|
||||
In `main.py` stand:
|
||||
|
||||
for name, pfad in (("Media (/app/media)", MEDIA_ROOT),
|
||||
("Arbeitsverzeichnis (/app/temp)", "/app/temp")):
|
||||
|
||||
Beide Pfade gibt es unter Windows nicht. `shutil.disk_usage` warf, die
|
||||
Liste blieb leer, und im Windows-Fenster stand **„Platz für Rippy:
|
||||
unbekannt"** — obwohl auf dem Laufwerk natürlich Platz war. Eine
|
||||
Nichtauskunft, die wie eine Auskunft aussieht.
|
||||
|
||||
Gibt eine Liste von `{"name": …, "pfad": …}` zurück; der Aufrufer misst.
|
||||
"""
|
||||
werte = werte or {}
|
||||
container = im_container() if container is None else container
|
||||
system = plattform(plattform_name)
|
||||
lager = werte.get("storage", {}) or {}
|
||||
|
||||
if container:
|
||||
return [{"name": "Media (/app/media)", "pfad": "/app/media"},
|
||||
{"name": "Arbeitsverzeichnis (/app/temp)", "pfad": "/app/temp"}]
|
||||
|
||||
ablage = ablage_vorgabe(werte, container, system)
|
||||
temp = lager.get("temp") or os.path.join(ablage, "_arbeit")
|
||||
orte = [{"name": "Ablage", "pfad": ablage}]
|
||||
# Nur wenn es ein ANDERES Laufwerk ist, lohnt eine zweite Zeile. Zweimal
|
||||
# dieselbe Zahl unter zwei Namen sieht aus wie zwei Auskünfte und ist eine.
|
||||
if os.path.splitdrive(temp)[0].lower() != os.path.splitdrive(ablage)[0].lower():
|
||||
orte.append({"name": "Arbeitsverzeichnis", "pfad": temp})
|
||||
return orte
|
||||
|
||||
|
||||
def ablage_vorgabe(werte: dict, container: bool, system: str) -> str:
|
||||
"""Wohin Rippy standardmäßig ablegt — je Betrieb ein anderer Ort."""
|
||||
eigen = ((werte or {}).get("storage", {}) or {}).get("medien", "")
|
||||
if eigen:
|
||||
return eigen
|
||||
if container:
|
||||
return "/app/media"
|
||||
if system == "windows":
|
||||
basis = os.environ.get("USERPROFILE") or os.path.expanduser("~")
|
||||
return os.path.join(basis, "Videos", "Rippy")
|
||||
return os.path.join(os.path.expanduser("~"), "Videos", "Rippy")
|
||||
@@ -0,0 +1,192 @@
|
||||
"""Der Betriebsmodus und seine Faehigkeiten.
|
||||
|
||||
## Warum es diese Tests gibt
|
||||
|
||||
Commander am 28.08.2026, zum Windows-Fenster:
|
||||
|
||||
Worker erreichbar: 0 von 1
|
||||
Kein Worker antwortet — Pruefen: docker compose ps
|
||||
Container-Platte: unbekannt
|
||||
Freigaben: keine eingehaengt
|
||||
|
||||
Kein Satz davon ergibt auf einem Windows-PC einen Sinn. Die Oberflaeche hatte
|
||||
nie erfahren, worauf sie laeuft — also hat sie angenommen.
|
||||
|
||||
Alle Pruefungen hier sind eingespritzt: Jeder Betriebsfall ist pruefbar, ohne
|
||||
ihn herzustellen. Das ist der Punkt — sonst waere „laeuft im Container" nur
|
||||
auf einem Container-Rechner testbar, und genau dort faellt der Windows-Fehler
|
||||
nie auf.
|
||||
"""
|
||||
|
||||
from rippy import betrieb
|
||||
|
||||
DOCKER = {"profil": "api", "queue": {"treiber": "celery", "broker": "redis://x"}}
|
||||
WINDOWS = {"profil": "standalone", "queue": {"treiber": "lokal"}}
|
||||
|
||||
|
||||
# ── Plattform ───────────────────────────────────────────────────────────
|
||||
def test_plattform_wird_zu_einem_klaren_namen():
|
||||
assert betrieb.plattform("win32") == "windows"
|
||||
assert betrieb.plattform("linux") == "linux"
|
||||
assert betrieb.plattform("darwin") == "macos"
|
||||
|
||||
|
||||
def test_unbekannte_plattform_gilt_als_linux():
|
||||
"""FreeBSD und Verwandte verhalten sich wie Linux — und ein Rueckfall auf
|
||||
etwas Bekanntes ist besser als ein leeres Feld."""
|
||||
assert betrieb.plattform("freebsd14") == "linux"
|
||||
|
||||
|
||||
# ── Container ───────────────────────────────────────────────────────────
|
||||
def test_container_wird_gemessen_nicht_geraten():
|
||||
"""`/.dockerenv` legt Docker selbst an — verlaesslicher als eine
|
||||
Umgebungsvariable, die jeder setzen und vergessen kann."""
|
||||
assert betrieb.im_container(lambda p: p == "/.dockerenv") is True
|
||||
assert betrieb.im_container(lambda p: False) is False
|
||||
|
||||
|
||||
# ── Modus ───────────────────────────────────────────────────────────────
|
||||
def test_celery_heisst_verteilt():
|
||||
assert betrieb.modus(DOCKER) == "verteilt"
|
||||
|
||||
|
||||
def test_lokale_queue_heisst_standalone():
|
||||
assert betrieb.modus(WINDOWS) == "standalone"
|
||||
|
||||
|
||||
def test_ohne_angaben_gilt_standalone():
|
||||
"""Der harmlosere Rueckfall: Ein standalone-Rippy zeigt nichts an, was es
|
||||
nicht gibt. Andersherum entstuenden die Phantom-Worker wieder."""
|
||||
assert betrieb.modus({}) == "standalone"
|
||||
|
||||
|
||||
# ── Die Faehigkeiten — hier sass der gemeldete Fehler ───────────────────
|
||||
def test_windows_hat_KEINE_externen_worker():
|
||||
"""DER Befund: „Worker erreichbar: 0 von 1 — Kein Worker antwortet."
|
||||
Unter Windows rippt Rippy selbst; es gibt keinen zweiten, auf den man
|
||||
warten koennte. Die Meldung war nicht nur unpassend, sie war falsch."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["kann"]["externe_worker"] is False
|
||||
|
||||
|
||||
def test_windows_kennt_KEINE_container_pfade():
|
||||
"""`/app/media` ist ein Pfad IM Container. Auf Windows heisst der Ordner
|
||||
anders und liegt woanders — ein damit vorbelegtes Feld ist schlicht
|
||||
falsch."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["kann"]["container_pfade"] is False
|
||||
assert "/app/" not in a["ablage_vorgabe"]
|
||||
|
||||
|
||||
def test_windows_haengt_keine_freigaben_ein():
|
||||
"""Unter Windows gibt man einen UNC-Pfad an, fertig. Eine Maske zum
|
||||
Einhaengen waere eine Bedienung, die ins Leere fuehrt."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["kann"]["freigaben_einhaengen"] is False
|
||||
|
||||
|
||||
def test_windows_darf_seine_werkzeuge_selbst_pflegen():
|
||||
"""Im Image steckt alles fest, ein Update ist ein Rebuild. Nur die native
|
||||
Installation kann MakeMKV/HandBrake selbst beschaffen."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["kann"]["werkzeuge_verwalten"] is True
|
||||
|
||||
|
||||
def test_docker_behaelt_alles_was_es_hatte():
|
||||
"""Die Windows-Reparatur darf dem Docker-Betrieb nichts wegnehmen."""
|
||||
a = betrieb.auskunft(DOCKER, container=True, plattform_name="linux")
|
||||
assert a["kann"]["externe_worker"] is True
|
||||
assert a["kann"]["container_pfade"] is True
|
||||
assert a["kann"]["freigaben_einhaengen"] is True
|
||||
assert a["ablage_vorgabe"] == "/app/media"
|
||||
assert a["hilfe_befehl"] == "docker compose -p rippy ps"
|
||||
|
||||
|
||||
def test_der_docker_hinweis_taucht_ausserhalb_von_docker_NICHT_auf():
|
||||
"""„Pruefen: docker compose ps" auf einem Windows-PC ist ein Rat, den
|
||||
niemand befolgen kann."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["hilfe_befehl"] == ""
|
||||
|
||||
|
||||
def test_docker_allinone_hat_container_aber_keine_externen_worker():
|
||||
"""Der Fall, an dem ein blosser Modus-Name zerbrechen wuerde: EIN
|
||||
Container, der alles selbst macht. Er hat Container-Pfade, aber niemanden,
|
||||
der von aussen Jobs uebernimmt."""
|
||||
a = betrieb.auskunft({"profil": "standalone", "queue": {"treiber": "lokal"}},
|
||||
container=True, plattform_name="linux")
|
||||
assert a["kann"]["container_pfade"] is True
|
||||
assert a["kann"]["externe_worker"] is False
|
||||
|
||||
|
||||
# ── Die Ablage-Vorgabe ──────────────────────────────────────────────────
|
||||
def test_eingestellte_ablage_schlaegt_jede_vorgabe():
|
||||
a = betrieb.auskunft({"storage": {"medien": "D:\\Filme"}},
|
||||
container=False, plattform_name="win32")
|
||||
assert a["ablage_vorgabe"] == "D:\\Filme"
|
||||
|
||||
|
||||
def test_windows_vorgabe_liegt_beim_nutzer(monkeypatch):
|
||||
monkeypatch.setenv("USERPROFILE", "C:\\Users\\Test")
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert a["ablage_vorgabe"].startswith("C:\\Users\\Test")
|
||||
assert a["ablage_vorgabe"].endswith("Rippy")
|
||||
|
||||
|
||||
# ── Der Vertrag als Ganzes ──────────────────────────────────────────────
|
||||
def test_die_auskunft_ist_vollstaendig():
|
||||
"""Ein fehlendes Feld waere im UI ein `undefined` — und `undefined` ist
|
||||
in JavaScript falsch, also verschwaende die Oberflaeche stillschweigend
|
||||
einen Bereich. Lieber hier rot."""
|
||||
a = betrieb.auskunft(WINDOWS, container=False, plattform_name="win32")
|
||||
assert set(a) == {"modus", "plattform", "im_container", "kann",
|
||||
"ablage_vorgabe", "hilfe_befehl"}
|
||||
assert set(a["kann"]) == {"externe_worker", "freigaben_einhaengen",
|
||||
"container_pfade", "werkzeuge_verwalten"}
|
||||
assert all(isinstance(v, bool) for v in a["kann"].values())
|
||||
|
||||
|
||||
# ── Wo Platz gemessen wird ──────────────────────────────────────────────
|
||||
def test_windows_misst_NICHT_in_app_media():
|
||||
"""DER Befund: Im Windows-Fenster stand „Platz fuer Rippy: unbekannt".
|
||||
|
||||
`main.py` mass fest an `/app/media` und `/app/temp`. Beide gibt es unter
|
||||
Windows nicht, `shutil.disk_usage` warf, die Liste blieb leer — und aus
|
||||
einer Nichtauskunft wurde eine Anzeige, die wie eine Auskunft aussieht.
|
||||
"""
|
||||
orte = betrieb.platz_orte(WINDOWS, container=False, plattform_name="win32")
|
||||
assert orte, "es muss mindestens ein Ort gemessen werden"
|
||||
assert all("/app/" not in o["pfad"] for o in orte), orte
|
||||
|
||||
|
||||
def test_docker_misst_weiter_die_container_pfade():
|
||||
orte = betrieb.platz_orte(DOCKER, container=True, plattform_name="linux")
|
||||
assert [o["pfad"] for o in orte] == ["/app/media", "/app/temp"]
|
||||
|
||||
|
||||
def test_gleiches_laufwerk_wird_nicht_doppelt_gezaehlt():
|
||||
"""Zweimal dieselbe Zahl unter zwei Namen sieht aus wie zwei Auskuenfte
|
||||
und ist eine."""
|
||||
orte = betrieb.platz_orte({"storage": {"medien": r"D:\Filme"}},
|
||||
container=False, plattform_name="win32")
|
||||
assert len(orte) == 1, orte
|
||||
|
||||
|
||||
def test_zweites_laufwerk_bekommt_eine_eigene_zeile():
|
||||
orte = betrieb.platz_orte(
|
||||
{"storage": {"medien": r"D:\Filme", "temp": r"E:\Arbeit"}},
|
||||
container=False, plattform_name="win32")
|
||||
assert len(orte) == 2, orte
|
||||
|
||||
|
||||
def test_noch_nicht_angelegter_ordner_faellt_auf_das_laufwerk_zurueck():
|
||||
"""Frisch installiert gibt es den Ablage-Ordner noch nicht. Der Nutzer
|
||||
will trotzdem wissen, ob auf dem LAUFWERK Platz ist."""
|
||||
da = r"C:\Users\Test"
|
||||
assert betrieb.naechster_vorhandener(
|
||||
r"C:\Users\Test\Videos\Rippy", existiert=lambda p: p == da) == da
|
||||
|
||||
|
||||
def test_wenn_gar_nichts_existiert_wird_nichts_behauptet():
|
||||
assert betrieb.naechster_vorhandener(r"Z:\gibt\es\nicht",
|
||||
existiert=lambda p: False) == ""
|
||||
Reference in New Issue
Block a user