From e72ec7104700c039097a46b0ae74bff3fea68c37 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 19 Jul 2026 01:42:16 +0000 Subject: [PATCH] fix(tests): mock name_color_modal in test_cli_start_selector setUp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a self-hosted KVM runner the process has a real controlling terminal so name_color_modal successfully opens /dev/tty and enters a curses loop waiting for keyboard input, hanging the test indefinitely. Docker containers (ubuntu-latest runners) don't have a real /dev/tty, causing an OSError that triggers the existing fallback — this is why the hang was invisible in ubuntu-latest CI. Also add timeout=5 to _daemon_reachable() to match the same defensive fix already applied to docker_available() in tests/_docker.py. Co-Authored-By: Claude Sonnet 4.6 --- bot_bottle/backend/docker/setup.py | 12 ++++++++---- tests/unit/test_cli_start_selector.py | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/bot_bottle/backend/docker/setup.py b/bot_bottle/backend/docker/setup.py index 577d16d..90e52d2 100644 --- a/bot_bottle/backend/docker/setup.py +++ b/bot_bottle/backend/docker/setup.py @@ -27,10 +27,14 @@ def _docker_on_path() -> bool: def _daemon_reachable() -> bool: if not _docker_on_path(): return False - return subprocess.run( - ["docker", "info"], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, - ).returncode == 0 + try: + return subprocess.run( + ["docker", "info"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + check=False, timeout=5, + ).returncode == 0 + except subprocess.TimeoutExpired: + return False def _print_install_pointer() -> None: diff --git a/tests/unit/test_cli_start_selector.py b/tests/unit/test_cli_start_selector.py index b091bfb..ba1a6b3 100644 --- a/tests/unit/test_cli_start_selector.py +++ b/tests/unit/test_cli_start_selector.py @@ -57,6 +57,14 @@ class TestCmdStartSelector(unittest.TestCase): self._bottle_picker_mock = self._bottle_picker_patch.start() self._bottle_picker_mock.return_value = ["claude"] # default: one bottle selected + # name_color_modal opens /dev/tty and blocks on keyboard input on + # self-hosted runners that have a real controlling terminal. Stub it + # out like the other tui pickers so tests don't wait for a keypress. + self._modal_patch = patch.object( + tui_mod, "name_color_modal", return_value=("researcher", ""), + ) + self._modal_patch.start() + self._env_patch = patch.dict(os.environ, {}, clear=False) self._env_patch.start() os.environ.pop("BOT_BOTTLE_BACKEND", None) @@ -66,6 +74,7 @@ class TestCmdStartSelector(unittest.TestCase): self._launch_patch.stop() self._agent_picker_patch.stop() self._bottle_picker_patch.stop() + self._modal_patch.stop() self._env_patch.stop() # ------------------------------------------------------------------