From 681b43174b28ff9a2d24cf0dc35ef754cb421e57 Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Tue, 5 May 2026 02:24:29 +0200 Subject: [PATCH] fix: implement random-port discovery for CI E2E backend When PORT=0, the backend now writes its actual bound port to data/test/.port. test-env.sh reads that file when TT_TEST_PORT=0 so all targets (test-up, test-reset, test-down) resolve the real URL. test-up waits for .port to appear before the health-check loop. --- Makefile | 9 +++++++++ backend/src/main.rs | 9 ++++++++- scripts/test-env.sh | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ee9f40d..c635dc1 100644 --- a/Makefile +++ b/Makefile @@ -60,10 +60,19 @@ test-up: exit 0; \ fi; \ [ -f "$$TT_TEST_DB" ] || $(MAKE) test-rebuild; \ + rm -f data/test/.port; \ DATABASE_URL="sqlite:$$TT_TEST_DB" PORT=$$TT_TEST_PORT TT_TEST_MODE=1 JWT_SECRET=testsecret STATIC_DIR=frontend/build \ cargo run --manifest-path backend/Cargo.toml &>/tmp/tutortool-test.log & \ echo $$! > data/test/.pid; \ echo "[test-up] Backend PID $$(cat data/test/.pid) starting on port $$TT_TEST_PORT..."; \ + if [ "$$TT_TEST_PORT" = "0" ]; then \ + for i in $$(seq 1 30); do \ + [ -f data/test/.port ] && break; \ + sleep 1; \ + done; \ + . scripts/test-env.sh; \ + echo "[test-up] Backend bound to port $$TT_TEST_PORT"; \ + fi; \ for i in $$(seq 1 30); do \ curl -fs "$$TT_BASE_URL/health" >/dev/null 2>&1 && break; \ sleep 1; \ diff --git a/backend/src/main.rs b/backend/src/main.rs index 9920801..cc6da32 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -85,7 +85,14 @@ async fn main() { let listener = tokio::net::TcpListener::bind(&addr) .await .expect("failed to bind"); - tracing::info!("listening on {}", addr); + let actual_addr = listener.local_addr().expect("failed to get local addr"); + tracing::info!("listening on {}", actual_addr); + + // When started with PORT=0 (random), write actual port so the test harness can discover it + if port == "0" { + let _ = std::fs::create_dir_all("data/test"); + let _ = std::fs::write("data/test/.port", actual_addr.port().to_string()); + } axum::serve( listener, diff --git a/scripts/test-env.sh b/scripts/test-env.sh index 41b016c..88b2426 100755 --- a/scripts/test-env.sh +++ b/scripts/test-env.sh @@ -13,6 +13,15 @@ if [ "${TT_TEST_PORT_RANDOM:-0}" = "1" ]; then TT_TEST_PORT=0 fi +# If port is 0 and the backend already wrote its actual port, use it +_port_file="${TT_WORKTREE_ROOT}/data/test/.port" +if [ "$TT_TEST_PORT" = "0" ] && [ -f "$_port_file" ]; then + _real_port=$(cat "$_port_file") + if [ -n "$_real_port" ] && [ "$_real_port" != "0" ]; then + TT_TEST_PORT=$_real_port + fi +fi + TT_TEST_DB="${TT_WORKTREE_ROOT}/data/test/attendance.db" TT_TEST_MODE=1 TT_BASE_URL="http://127.0.0.1:${TT_TEST_PORT}"