fix(tests): mock name_color_modal in test_cli_start_selector setUp

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 01:42:16 +00:00
parent 7aff69fbe0
commit e72ec71047
2 changed files with 17 additions and 4 deletions
+8 -4
View File
@@ -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:
+9
View File
@@ -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()
# ------------------------------------------------------------------