Spaces:
Paused
Paused
| import queue | |
| import threading | |
| import time | |
| from unittest.mock import patch | |
| import cli as cli_module | |
| import tools.skills_tool as skills_tool_module | |
| from cli import HermesCLI | |
| from hermes_cli.callbacks import prompt_for_secret | |
| from tools.skills_tool import set_secret_capture_callback | |
| class _FakeBuffer: | |
| def __init__(self): | |
| self.reset_called = False | |
| def reset(self): | |
| self.reset_called = True | |
| class _FakeApp: | |
| def __init__(self): | |
| self.invalidated = False | |
| self.current_buffer = _FakeBuffer() | |
| def invalidate(self): | |
| self.invalidated = True | |
| def _make_cli_stub(with_app=False): | |
| cli = HermesCLI.__new__(HermesCLI) | |
| cli._app = _FakeApp() if with_app else None | |
| cli._last_invalidate = 0.0 | |
| cli._secret_state = None | |
| cli._secret_deadline = 0 | |
| return cli | |
| def test_secret_capture_callback_can_be_completed_from_cli_state_machine(): | |
| cli = _make_cli_stub(with_app=True) | |
| results = [] | |
| with patch("hermes_cli.callbacks.save_env_value_secure") as save_secret: | |
| save_secret.return_value = { | |
| "success": True, | |
| "stored_as": "TENOR_API_KEY", | |
| "validated": False, | |
| } | |
| thread = threading.Thread( | |
| target=lambda: results.append( | |
| cli._secret_capture_callback("TENOR_API_KEY", "Tenor API key") | |
| ) | |
| ) | |
| thread.start() | |
| deadline = time.time() + 2 | |
| while cli._secret_state is None and time.time() < deadline: | |
| time.sleep(0.01) | |
| assert cli._secret_state is not None | |
| cli._submit_secret_response("super-secret-value") | |
| thread.join(timeout=2) | |
| assert results[0]["success"] is True | |
| assert results[0]["stored_as"] == "TENOR_API_KEY" | |
| assert results[0]["skipped"] is False | |
| def test_cancel_secret_capture_marks_setup_skipped(): | |
| cli = _make_cli_stub() | |
| cli._secret_state = { | |
| "response_queue": queue.Queue(), | |
| "var_name": "TENOR_API_KEY", | |
| "prompt": "Tenor API key", | |
| "metadata": {}, | |
| } | |
| cli._secret_deadline = 123 | |
| cli._cancel_secret_capture() | |
| assert cli._secret_state is None | |
| assert cli._secret_deadline == 0 | |
| def test_secret_capture_uses_getpass_without_tui(): | |
| cli = _make_cli_stub() | |
| with patch("hermes_cli.callbacks.getpass.getpass", return_value="secret-value"), patch( | |
| "hermes_cli.callbacks.save_env_value_secure" | |
| ) as save_secret: | |
| save_secret.return_value = { | |
| "success": True, | |
| "stored_as": "TENOR_API_KEY", | |
| "validated": False, | |
| } | |
| result = prompt_for_secret(cli, "TENOR_API_KEY", "Tenor API key") | |
| assert result["success"] is True | |
| assert result["stored_as"] == "TENOR_API_KEY" | |
| assert result["skipped"] is False | |
| def test_secret_capture_timeout_clears_hidden_input_buffer(): | |
| cli = _make_cli_stub(with_app=True) | |
| cleared = {"value": False} | |
| def clear_buffer(): | |
| cleared["value"] = True | |
| cli._clear_secret_input_buffer = clear_buffer | |
| with patch("hermes_cli.callbacks.queue.Queue.get", side_effect=queue.Empty), patch( | |
| "hermes_cli.callbacks._time.monotonic", | |
| side_effect=[0, 121], | |
| ): | |
| result = prompt_for_secret(cli, "TENOR_API_KEY", "Tenor API key") | |
| assert result["success"] is True | |
| assert result["skipped"] is True | |
| assert result["reason"] == "timeout" | |
| assert cleared["value"] is True | |
| def test_cli_chat_registers_secret_capture_callback(): | |
| clean_config = { | |
| "model": { | |
| "default": "anthropic/claude-opus-4.6", | |
| "base_url": "https://openrouter.ai/api/v1", | |
| "provider": "auto", | |
| }, | |
| "display": {"compact": False, "tool_progress": "all"}, | |
| "agent": {}, | |
| "terminal": {"env_type": "local"}, | |
| } | |
| with patch("cli.get_tool_definitions", return_value=[]), patch.dict( | |
| "os.environ", {"LLM_MODEL": "", "HERMES_MAX_ITERATIONS": ""}, clear=False | |
| ), patch.dict(cli_module.__dict__, {"CLI_CONFIG": clean_config}): | |
| cli_obj = HermesCLI() | |
| with patch.object(cli_obj, "_ensure_runtime_credentials", return_value=False): | |
| cli_obj.chat("hello") | |
| try: | |
| assert skills_tool_module._secret_capture_callback == cli_obj._secret_capture_callback | |
| finally: | |
| set_secret_capture_callback(None) | |