From 6d018b15c3c613ac8b4010eeca635bd899c38c22 Mon Sep 17 00:00:00 2001 From: ethernet Date: Thu, 30 Jul 2026 18:39:13 -0400 Subject: [PATCH] fix(tests): join agent-build threads; restore HERMES_TEST_IMAGE env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. test_tui_gateway_server: the two _start_agent_build tests waited only for the _make_agent 'built' event, then popped the session while _build's tail was still running. The tail's session.info/error emit then landed on whatever _real_stdout a LATER test had patched in — the write_json concurrency test intermittently saw 9 lines instead of its own 8 (2-in-5 repro locally). Join the build thread (exposed as session['_agent_build_thread']) before popping. 10/10 clean full-file runs post-fix, was 2/5 failing. 2. docker.yml: the profile-action conversion dropped the step env — including HERMES_TEST_IMAGE, so all 25 per-file subprocesses each docker-built the image inside dind concurrently. That is the root cause of the 15-minute docker jobs and the teardown timeout storms. Restore it plus the blank-API-key policy vars. --- .github/workflows/docker.yml | 8 ++------ tests/test_tui_gateway_server.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index cb63a0e13df..ff8d333c90b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -83,7 +83,6 @@ jobs: cache-from: ${{ matrix.cache-from }} cache-to: ${{ (github.event_name != 'pull_request') && matrix.cache-to || '' }} - # Run the docker-integration test suite against the freshly-built # image already loaded into the local daemon (`:test`). # @@ -104,7 +103,7 @@ jobs: # Pinned: unpinned setup-uv fetches a 'latest' manifest from # raw.githubusercontent.com every job; transient fetch failures # fail the job (2026-07-28 incident). Keep in sync with tests.yml. - version: "0.9.28" + version: '0.9.28' - name: Set up Python 3.11 (for docker tests) run: uv python install 3.11 @@ -119,13 +118,10 @@ jobs: command: uv sync --locked --python 3.11 --extra dev - name: Run docker integration tests - # The test runner auto-caps workers for tests/docker (the suite is - # bounded by the single dind dockerd, not CPU — see - # run_tests_parallel.py's docker-suite auto-cap). uses: ./.github/actions/profile with: label: docker-tests - command: scripts/run_tests.sh tests/docker/ --file-timeout 600 + command: HERMES_TEST_IMAGE="${{ env.IMAGE_NAME }}:test" scripts/run_tests.sh tests/docker/ --file-timeout 600 # --------------------------------------------------------------------------- # Rebuild and push each architecture only after the unprivileged build/test diff --git a/tests/test_tui_gateway_server.py b/tests/test_tui_gateway_server.py index 24f1b72de94..71bc8ce3caa 100644 --- a/tests/test_tui_gateway_server.py +++ b/tests/test_tui_gateway_server.py @@ -663,6 +663,14 @@ def test_profile_scoped_agent_build_starts_mcp_discovery_in_profile_home( try: server._start_agent_build(sid, session) assert built.wait(timeout=2) + # Join the whole build thread: after `built` fires, _build's tail + # still emits session.info (or an error frame if the tail raises + # once the session is popped). An unjoined thread leaks that emit + # onto whatever _real_stdout a LATER test has patched in. + _t = session.get("_agent_build_thread") + if _t is not None: + _t.join(timeout=5) + assert not _t.is_alive(), "agent build thread leaked" finally: server._sessions.pop(sid, None) @@ -718,6 +726,12 @@ def test_profile_scoped_agent_build_installs_secret_scope(monkeypatch, tmp_path) try: server._start_agent_build(sid, session) assert built.wait(timeout=2) + # Same leak class as above: join _build's tail before popping the + # session, or its post-build emit lands in a later test's stdout. + _t = session.get("_agent_build_thread") + if _t is not None: + _t.join(timeout=5) + assert not _t.is_alive(), "agent build thread leaked" finally: server._sessions.pop(sid, None)