"""Containers op de host: wat is beheerd en wat niet. Het dashboard telt alles wat de Docker-daemon kent, terwijl de stacklijst alleen toont wat in LIBRARY_DIR staat. Containers die daarbuiten vallen waren zichtbaar in de cijfers maar nergens terug te vinden. """ import pytest from conftest import login PS_UITVOER = "\n".join([ "abc123\tvaultwarden\tvaultwarden/server:latest\trunning\tUp 2 hours\t0.0.0.0:8222->80/tcp\tvaultwarden\t/opt/serverup/stacks/vaultwarden", "def456\tforgejo\tcodeberg.org/forgejo/forgejo:1.21\trunning\tUp 3 days\t0.0.0.0:3000->3000/tcp\tforgejo\t/opt/docker/forgejo", "ghi789\tact_runner\tgitea/act_runner:latest\trunning\tUp 3 days\t\t\t", "jkl012\tserver-up\tserver-up:0.5.46-beta\trunning\tUp 5 minutes\t127.0.0.1:5000->5000/tcp\tserver-up\t/opt/docker/server-up", "mno345\toud-testje\tnginx:latest\texited\tExited (0) 4 days ago\t\t\t", ]) @pytest.fixture def host(env, monkeypatch): from core import docker monkeypatch.setattr(docker, "_run", lambda *a, **k: type( "R", (), {"returncode": 0, "stdout": PS_UITVOER, "stderr": ""})()) return docker def test_list_containers_leest_alle_velden(host): c = host.list_containers() assert len(c) == 5 vw = next(x for x in c if x["name"] == "vaultwarden") assert vw["image"] == "vaultwarden/server:latest" assert vw["running"] is True assert vw["project"] == "vaultwarden" assert vw["working_dir"] == "/opt/serverup/stacks/vaultwarden" def test_list_containers_zonder_labels(host): """Een container zonder compose-labels heeft lege project-/werkmapvelden.""" c = next(x for x in host.list_containers() if x["name"] == "act_runner") assert c["project"] == "" and c["working_dir"] == "" assert c["running"] is True def test_gestopte_container_wordt_herkend(host): c = next(x for x in host.list_containers() if x["name"] == "oud-testje") assert c["running"] is False def test_endpoint_scheidt_beheerd_van_de_rest(client, env, monkeypatch): from core import docker login(client) # Eén stack in de library: vaultwarden. stack = env["lib"] / "vaultwarden" stack.mkdir(parents=True) (stack / "docker-compose.yml").write_text("services:\n app:\n image: nginx\n") monkeypatch.setattr(docker, "_run", lambda *a, **k: type( "R", (), {"returncode": 0, "stdout": PS_UITVOER.replace("/opt/serverup/stacks/vaultwarden", str(stack)), "stderr": ""})()) d = client.get("/api/containers").get_json() beheerd = [c["name"] for c in d["containers"] if c["managed"]] overig = [c["name"] for c in d["containers"] if not c["managed"]] assert beheerd == ["vaultwarden"] assert set(overig) == {"forgejo", "act_runner", "server-up", "oud-testje"} assert d["managed"] == 1 assert d["total"] == 5 assert d["running"] == 4 def test_eigen_container_wordt_gemarkeerd(client, env, monkeypatch): from core import docker import app as app_module login(client) monkeypatch.setattr(app_module, "CONTAINER_NAME", "server-up") monkeypatch.setattr(docker, "_run", lambda *a, **k: type( "R", (), {"returncode": 0, "stdout": PS_UITVOER, "stderr": ""})()) d = client.get("/api/containers").get_json() assert next(c for c in d["containers"] if c["name"] == "server-up")["self"] is True def test_eigen_container_kan_niet_gestopt_worden(client, monkeypatch): """Zichzelf stoppen midden in een verzoek levert een halve actie op; daar is de aparte herstartknop voor.""" import app as app_module csrf = login(client) monkeypatch.setattr(app_module, "CONTAINER_NAME", "server-up") r = client.post("/api/containers/server-up/stop", headers={"X-CSRF-Token": csrf}) assert r.status_code == 400 assert "herstarten" in r.get_json()["msg"] def test_onbekende_container(client, host): csrf = login(client) r = client.post("/api/containers/bestaatniet/stop", headers={"X-CSRF-Token": csrf}) assert r.status_code == 404 def test_onbekende_actie(client, host): csrf = login(client) r = client.post("/api/containers/forgejo/verwijder", headers={"X-CSRF-Token": csrf}) assert r.status_code == 400 def test_viewer_mag_geen_containers_bedienen(client, host): csrf = login(client) client.post("/api/auth/users", json={"username": "kijker", "password": "eenlangwachtwoord", "role": "viewer"}, headers={"X-CSRF-Token": csrf}) r = client.post("/api/auth/login", json={"username": "kijker", "password": "eenlangwachtwoord"}) kijker_csrf = r.get_json()["csrf_token"] assert client.get("/api/containers").status_code == 200 assert client.post("/api/containers/forgejo/stop", headers={"X-CSRF-Token": kijker_csrf}).status_code == 403 # ── Een map zonder compose-bestand ─────────────────────────────────────────── # Die werd stilzwijgend overgeslagen: niet in de lijst, en de containers die # erbij hoorden belandden onder "handmatig gestart". Je zag een leeg scherm en # nergens stond waarom. def test_map_zonder_compose_verdwijnt_niet_uit_de_lijst(client, env): from conftest import login login(client) (env["lib"] / "kapotte-app").mkdir(parents=True, exist_ok=True) d = client.get("/api/stacks").get_json() namen = [s["name"] for s in d] assert "kapotte-app" in namen, f"de map is niet zichtbaar: {namen}" s = next(s for s in d if s["name"] == "kapotte-app") assert s["has_compose"] is False, "hij doet alsof er compose is" assert s["containers"] == [] assert s["running"] is False def test_een_gewone_app_blijft_gewoon_staan(client, env, monkeypatch): from conftest import login from core import docker monkeypatch.setattr(docker, "compose_ps", lambda *a, **k: []) login(client) d = env["lib"] / "echte-app" d.mkdir(parents=True, exist_ok=True) (d / "docker-compose.yml").write_text("services:\n web:\n image: nginx\n") uit = client.get("/api/stacks").get_json() s = next(s for s in uit if s["name"] == "echte-app") assert s["has_compose"] is True