From b43bb9e0fad09645e42de3b9008adfa45950a3d3 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 17 May 2026 19:20:20 +0200 Subject: [PATCH] test(files): add server-detail e2e fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds server_with_files fixture: seeds alice + a Blueprint + a Server row pinned to port 27015, then pre-creates LEFT4ME_ROOT/runtime//merged/ with a single seed file. The /servers/ page lists files from that merged directory (the kernel-overlayfs view of a running server), which never exists on dev/test boxes — without the pre-mkdir + seed, the page renders the empty-state branch instead of file rows. Server.port is a global UNIQUE constraint on the model, but every e2e test gets its own SQLite DB, so a fixed L4D2-default port is fine. Sets the stage for the Tier 3 hover-download test. Co-Authored-By: Claude Opus 4.7 (1M context) --- l4d2web/tests/e2e/conftest.py | 64 ++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/l4d2web/tests/e2e/conftest.py b/l4d2web/tests/e2e/conftest.py index fb11bf4..0136194 100644 --- a/l4d2web/tests/e2e/conftest.py +++ b/l4d2web/tests/e2e/conftest.py @@ -14,7 +14,7 @@ from werkzeug.serving import make_server from l4d2web.app import create_app from l4d2web.auth import hash_password from l4d2web.db import init_db, session_scope -from l4d2web.models import Blueprint, Overlay, User +from l4d2web.models import Blueprint, Overlay, Server, User def _free_port() -> int: @@ -164,3 +164,65 @@ def files_overlay_server(tmp_path, monkeypatch): } finally: shutdown() + + +@pytest.fixture(scope="function") +def server_with_files(tmp_path, monkeypatch): + """live_server + a Server owned by alice with a populated runtime + merged directory. Used by the server-detail e2e tests that exercise + file rows + download. + + The /servers/ page lists files from + LEFT4ME_ROOT/runtime//merged/ (the kernel-overlayfs view + of a running server). On a dev/test box no overlayfs is mounted, so + the fixture pre-creates that directory and seeds one plain file — + enough for the file-tree partial to render rows. + + Yields {base_url, user_id, blueprint_id, server_id, merged_root}. + """ + monkeypatch.setenv("LEFT4ME_ROOT", str(tmp_path)) + app = _boot_app(tmp_path, monkeypatch) + + with session_scope() as session: + user = User( + username="alice", + password_digest=hash_password("secret"), + admin=False, + ) + session.add(user) + session.flush() + bp = Blueprint( + user_id=user.id, name="bp", arguments="[]", config="[]" + ) + session.add(bp) + session.flush() + # Server.port has a global UNIQUE constraint, but this is a + # fresh per-test SQLite DB so any value works — 27015 is the + # L4D2 default, semantically obvious. + server = Server( + user_id=user.id, + blueprint_id=bp.id, + name="srv", + port=27015, + ) + session.add(server) + session.flush() + user_id = user.id + blueprint_id = bp.id + server_id = server.id + + merged_root = tmp_path / "runtime" / str(server_id) / "merged" + merged_root.mkdir(parents=True) + (merged_root / "server.cfg").write_text('hostname "from-merged-runtime"\n') + + base_url, shutdown = _serve(app) + try: + yield { + "base_url": base_url, + "user_id": user_id, + "blueprint_id": blueprint_id, + "server_id": server_id, + "merged_root": merged_root, + } + finally: + shutdown()