From e0d225852a2e64ea9227d7d642b63c4522950f46 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 23 Feb 2026 17:59:32 -0600 Subject: [PATCH 1/3] handle the non-tty case --- dimos/protocol/service/system_configurator.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dimos/protocol/service/system_configurator.py b/dimos/protocol/service/system_configurator.py index 44b8c45276..d67a92073e 100644 --- a/dimos/protocol/service/system_configurator.py +++ b/dimos/protocol/service/system_configurator.py @@ -20,6 +20,7 @@ import re import resource import subprocess +import sys from typing import Any # ----------------------------- sudo helpers ----------------------------- @@ -112,10 +113,14 @@ def configure_system(checks: list[SystemConfigurator], check_only: bool = False) if check_only: return - try: - answer = input("Apply these changes now? [y/N]: ").strip().lower() - except (EOFError, KeyboardInterrupt): - answer = "" + if not sys.stdin.isatty(): + print("Non-interactive stdin detected: assuming yes to 'Apply these changes now?'") + answer = "yes" + else: + try: + answer = input("Apply these changes now? [y/N]: ").strip().lower() + except (EOFError, KeyboardInterrupt): + answer = "" if answer not in ("y", "yes"): if any(check.critical for check in failing): From 754e8fd724b8dbe72f128c6787cd1a4942c5d956 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 23 Feb 2026 18:28:31 -0600 Subject: [PATCH 2/3] fix tests --- .../service/test_system_configurator.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/dimos/protocol/service/test_system_configurator.py b/dimos/protocol/service/test_system_configurator.py index 07f8ede64c..ecb594a20e 100644 --- a/dimos/protocol/service/test_system_configurator.py +++ b/dimos/protocol/service/test_system_configurator.py @@ -163,31 +163,39 @@ def test_check_only_mode_does_not_fix(self) -> None: def test_prompts_user_and_fixes_on_yes(self) -> None: with patch.dict(os.environ, {"CI": ""}, clear=False): mock_check = MockConfigurator(passes=False) - with patch("builtins.input", return_value="y"): - configure_system([mock_check]) - assert mock_check.fix_called + with patch("sys.stdin") as mock_stdin: + mock_stdin.isatty.return_value = True + with patch("builtins.input", return_value="y"): + configure_system([mock_check]) + assert mock_check.fix_called def test_does_not_fix_on_no(self) -> None: with patch.dict(os.environ, {"CI": ""}, clear=False): mock_check = MockConfigurator(passes=False) - with patch("builtins.input", return_value="n"): - configure_system([mock_check]) - assert not mock_check.fix_called + with patch("sys.stdin") as mock_stdin: + mock_stdin.isatty.return_value = True + with patch("builtins.input", return_value="n"): + configure_system([mock_check]) + assert not mock_check.fix_called def test_exits_on_no_with_critical_check(self) -> None: with patch.dict(os.environ, {"CI": ""}, clear=False): mock_check = MockConfigurator(passes=False, is_critical=True) - with patch("builtins.input", return_value="n"): - with pytest.raises(SystemExit) as exc_info: - configure_system([mock_check]) - assert exc_info.value.code == 1 + with patch("sys.stdin") as mock_stdin: + mock_stdin.isatty.return_value = True + with patch("builtins.input", return_value="n"): + with pytest.raises(SystemExit) as exc_info: + configure_system([mock_check]) + assert exc_info.value.code == 1 def test_handles_eof_error_on_input(self) -> None: with patch.dict(os.environ, {"CI": ""}, clear=False): mock_check = MockConfigurator(passes=False) - with patch("builtins.input", side_effect=EOFError): - configure_system([mock_check]) - assert not mock_check.fix_called + with patch("sys.stdin") as mock_stdin: + mock_stdin.isatty.return_value = True + with patch("builtins.input", side_effect=EOFError): + configure_system([mock_check]) + assert not mock_check.fix_called # ----------------------------- MulticastConfiguratorLinux tests ----------------------------- From 60b60d765115558787f9ce389c32fb1280d6c634 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Thu, 26 Feb 2026 12:19:01 -0600 Subject: [PATCH 3/3] fixup tests --- .../service/test_system_configurator.py | 98 +++++++++---------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/dimos/protocol/service/test_system_configurator.py b/dimos/protocol/service/test_system_configurator.py index ecb594a20e..d8566e1285 100644 --- a/dimos/protocol/service/test_system_configurator.py +++ b/dimos/protocol/service/test_system_configurator.py @@ -142,60 +142,58 @@ def fix(self) -> None: class TestConfigureSystem: - def test_skips_in_ci_environment(self) -> None: - with patch.dict(os.environ, {"CI": "true"}): - mock_check = MockConfigurator(passes=False) - configure_system([mock_check]) - assert not mock_check.fix_called + @pytest.fixture(autouse=True) + def non_ci_env(self, mocker): + mocker.patch.dict(os.environ, {"CI": ""}, clear=False) + + def test_skips_in_ci_environment(self, mocker) -> None: + mocker.patch.dict(os.environ, {"CI": "true"}) + mock_check = MockConfigurator(passes=False) + configure_system([mock_check]) + assert not mock_check.fix_called def test_does_nothing_when_all_checks_pass(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=True) - configure_system([mock_check]) - assert not mock_check.fix_called + mock_check = MockConfigurator(passes=True) + configure_system([mock_check]) + assert not mock_check.fix_called def test_check_only_mode_does_not_fix(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=False) - configure_system([mock_check], check_only=True) - assert not mock_check.fix_called - - def test_prompts_user_and_fixes_on_yes(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=False) - with patch("sys.stdin") as mock_stdin: - mock_stdin.isatty.return_value = True - with patch("builtins.input", return_value="y"): - configure_system([mock_check]) - assert mock_check.fix_called - - def test_does_not_fix_on_no(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=False) - with patch("sys.stdin") as mock_stdin: - mock_stdin.isatty.return_value = True - with patch("builtins.input", return_value="n"): - configure_system([mock_check]) - assert not mock_check.fix_called - - def test_exits_on_no_with_critical_check(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=False, is_critical=True) - with patch("sys.stdin") as mock_stdin: - mock_stdin.isatty.return_value = True - with patch("builtins.input", return_value="n"): - with pytest.raises(SystemExit) as exc_info: - configure_system([mock_check]) - assert exc_info.value.code == 1 - - def test_handles_eof_error_on_input(self) -> None: - with patch.dict(os.environ, {"CI": ""}, clear=False): - mock_check = MockConfigurator(passes=False) - with patch("sys.stdin") as mock_stdin: - mock_stdin.isatty.return_value = True - with patch("builtins.input", side_effect=EOFError): - configure_system([mock_check]) - assert not mock_check.fix_called + mock_check = MockConfigurator(passes=False) + configure_system([mock_check], check_only=True) + assert not mock_check.fix_called + + def test_prompts_user_and_fixes_on_yes(self, mocker) -> None: + mock_check = MockConfigurator(passes=False) + mock_stdin = mocker.patch("sys.stdin") + mock_stdin.isatty.return_value = True + mocker.patch("builtins.input", return_value="y") + configure_system([mock_check]) + assert mock_check.fix_called + + def test_does_not_fix_on_no(self, mocker) -> None: + mock_check = MockConfigurator(passes=False) + mock_stdin = mocker.patch("sys.stdin") + mock_stdin.isatty.return_value = True + mocker.patch("builtins.input", return_value="n") + configure_system([mock_check]) + assert not mock_check.fix_called + + def test_exits_on_no_with_critical_check(self, mocker) -> None: + mock_check = MockConfigurator(passes=False, is_critical=True) + mock_stdin = mocker.patch("sys.stdin") + mock_stdin.isatty.return_value = True + mocker.patch("builtins.input", return_value="n") + with pytest.raises(SystemExit) as exc_info: + configure_system([mock_check]) + assert exc_info.value.code == 1 + + def test_handles_eof_error_on_input(self, mocker) -> None: + mock_check = MockConfigurator(passes=False) + mock_stdin = mocker.patch("sys.stdin") + mock_stdin.isatty.return_value = True + mocker.patch("builtins.input", side_effect=EOFError) + configure_system([mock_check]) + assert not mock_check.fix_called # ----------------------------- MulticastConfiguratorLinux tests -----------------------------