test(harness): bind the static test server to an ephemeral port

The session-scoped autouse fixture bound 127.0.0.1:7011 and raised when the
port was taken. Because it is autouse, that raise errored every collected
test rather than the browser ones: a second worktree running its own suite
produced 10,612 errors, none of them about the code under test. 7011 is also
the application's own default port, so the suite could not run while a local
instance was up.

Bind port 0 instead and publish the resulting origin as
ODYSSEUS_TEST_STATIC_ORIGIN. The browser tests shell out to node, which
inherits the environment, so the snippets read process.env rather than
hardcoding a port. ODYSSEUS_TEST_STATIC_PORT still pins one when something
outside pytest has to reach the server; that is the only path that can now
fail to bind, and it fails with a message that says so.

Two concurrent full runs from one checkout now both pass. Only the docx export
snippet is an rf-string, so it is the only one whose JS braces needed doubling.
This commit is contained in:
Léo
2026-09-25 10:45:58 +02:00
parent 0e07d9a675
commit 9b2185f1cc
29 changed files with 116 additions and 45 deletions
+31 -13
View File
@@ -98,22 +98,23 @@ def pytest_collection_modifyitems(config, items):
@pytest.fixture(scope="session", autouse=True)
def _serve_test_static():
"""Ensure static assets are available on loopback port 7011 for browser integration tests."""
import socket
"""Serve static assets on loopback for the browser integration tests.
Binds an ephemeral port so several worktrees can run their own suite at the
same time, and publishes the resulting origin through
``ODYSSEUS_TEST_STATIC_ORIGIN``. The browser tests shell out to node, which
inherits the environment, so the snippets read the origin from
``process.env`` instead of hardcoding a port.
Set ``ODYSSEUS_TEST_STATIC_PORT`` to pin a specific port when something
outside pytest has to reach this server.
"""
import os
import threading
import http.server
import socketserver
from pathlib import Path
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
is_bound = (sock.connect_ex(("127.0.0.1", 7011)) == 0)
finally:
sock.close()
if is_bound:
raise RuntimeError("port 7011 is already in use; browser tests require this worktree's static server")
root_dir = Path(__file__).resolve().parent.parent
class _Handler(http.server.SimpleHTTPRequestHandler):
@@ -133,11 +134,28 @@ def _serve_test_static():
class _Server(socketserver.TCPServer):
allow_reuse_address = True
server = _Server(("127.0.0.1", 7011), _Handler)
requested = int(os.environ.get("ODYSSEUS_TEST_STATIC_PORT") or 0)
try:
server = _Server(("127.0.0.1", requested), _Handler)
except OSError as exc:
# Port 0 cannot collide, so this only fires for an explicit pin.
raise RuntimeError(
f"ODYSSEUS_TEST_STATIC_PORT={requested} is not bindable; unset it to "
"let the browser tests pick an ephemeral port"
) from exc
origin = f"http://127.0.0.1:{server.server_address[1]}"
previous_origin = os.environ.get("ODYSSEUS_TEST_STATIC_ORIGIN")
os.environ["ODYSSEUS_TEST_STATIC_ORIGIN"] = origin
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
yield
yield origin
finally:
if previous_origin is None:
os.environ.pop("ODYSSEUS_TEST_STATIC_ORIGIN", None)
else:
os.environ["ODYSSEUS_TEST_STATIC_ORIGIN"] = previous_origin
server.shutdown()
server.server_close()