Spaces:
Build error
Build error
| from unittest.mock import MagicMock, patch | |
| import pytest | |
| from openhands.cli.commands import ( | |
| handle_commands, | |
| handle_exit_command, | |
| handle_help_command, | |
| handle_init_command, | |
| handle_new_command, | |
| handle_resume_command, | |
| handle_settings_command, | |
| handle_status_command, | |
| ) | |
| from openhands.cli.tui import UsageMetrics | |
| from openhands.core.config import OpenHandsConfig | |
| from openhands.core.schema import AgentState | |
| from openhands.events import EventSource | |
| from openhands.events.action import ChangeAgentStateAction, MessageAction | |
| from openhands.events.stream import EventStream | |
| from openhands.storage.settings.file_settings_store import FileSettingsStore | |
| class TestHandleCommands: | |
| def mock_dependencies(self): | |
| event_stream = MagicMock(spec=EventStream) | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| config = MagicMock(spec=OpenHandsConfig) | |
| current_dir = '/test/dir' | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| return { | |
| 'event_stream': event_stream, | |
| 'usage_metrics': usage_metrics, | |
| 'sid': sid, | |
| 'config': config, | |
| 'current_dir': current_dir, | |
| 'settings_store': settings_store, | |
| } | |
| async def test_handle_exit_command(self, mock_handle_exit, mock_dependencies): | |
| mock_handle_exit.return_value = True | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/exit', **mock_dependencies | |
| ) | |
| mock_handle_exit.assert_called_once_with( | |
| mock_dependencies['event_stream'], | |
| mock_dependencies['usage_metrics'], | |
| mock_dependencies['sid'], | |
| ) | |
| assert close_repl is True | |
| assert reload_microagents is False | |
| assert new_session is False | |
| async def test_handle_help_command(self, mock_handle_help, mock_dependencies): | |
| mock_handle_help.return_value = (False, False, False) | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/help', **mock_dependencies | |
| ) | |
| mock_handle_help.assert_called_once() | |
| assert close_repl is False | |
| assert reload_microagents is False | |
| assert new_session is False | |
| async def test_handle_init_command(self, mock_handle_init, mock_dependencies): | |
| mock_handle_init.return_value = (True, True) | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/init', **mock_dependencies | |
| ) | |
| mock_handle_init.assert_called_once_with( | |
| mock_dependencies['config'], | |
| mock_dependencies['event_stream'], | |
| mock_dependencies['current_dir'], | |
| ) | |
| assert close_repl is True | |
| assert reload_microagents is True | |
| assert new_session is False | |
| async def test_handle_status_command(self, mock_handle_status, mock_dependencies): | |
| mock_handle_status.return_value = (False, False, False) | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/status', **mock_dependencies | |
| ) | |
| mock_handle_status.assert_called_once_with( | |
| mock_dependencies['usage_metrics'], mock_dependencies['sid'] | |
| ) | |
| assert close_repl is False | |
| assert reload_microagents is False | |
| assert new_session is False | |
| async def test_handle_new_command(self, mock_handle_new, mock_dependencies): | |
| mock_handle_new.return_value = (True, True) | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/new', **mock_dependencies | |
| ) | |
| mock_handle_new.assert_called_once_with( | |
| mock_dependencies['event_stream'], | |
| mock_dependencies['usage_metrics'], | |
| mock_dependencies['sid'], | |
| ) | |
| assert close_repl is True | |
| assert reload_microagents is False | |
| assert new_session is True | |
| async def test_handle_settings_command( | |
| self, mock_handle_settings, mock_dependencies | |
| ): | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| '/settings', **mock_dependencies | |
| ) | |
| mock_handle_settings.assert_called_once_with( | |
| mock_dependencies['config'], | |
| mock_dependencies['settings_store'], | |
| ) | |
| assert close_repl is False | |
| assert reload_microagents is False | |
| assert new_session is False | |
| async def test_handle_unknown_command(self, mock_dependencies): | |
| user_message = 'Hello, this is not a command' | |
| close_repl, reload_microagents, new_session = await handle_commands( | |
| user_message, **mock_dependencies | |
| ) | |
| # The command should be treated as a message and added to the event stream | |
| mock_dependencies['event_stream'].add_event.assert_called_once() | |
| # Check the first argument is a MessageAction with the right content | |
| args, kwargs = mock_dependencies['event_stream'].add_event.call_args | |
| assert isinstance(args[0], MessageAction) | |
| assert args[0].content == user_message | |
| assert args[1] == EventSource.USER | |
| assert close_repl is True | |
| assert reload_microagents is False | |
| assert new_session is False | |
| class TestHandleExitCommand: | |
| def test_exit_with_confirmation(self, mock_display_shutdown, mock_cli_confirm): | |
| event_stream = MagicMock(spec=EventStream) | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| # Mock user confirming exit | |
| mock_cli_confirm.return_value = 0 # First option, which is "Yes, proceed" | |
| # Call the function under test | |
| result = handle_exit_command(event_stream, usage_metrics, sid) | |
| # Verify correct behavior | |
| mock_cli_confirm.assert_called_once() | |
| event_stream.add_event.assert_called_once() | |
| # Check event is the right type | |
| args, kwargs = event_stream.add_event.call_args | |
| assert isinstance(args[0], ChangeAgentStateAction) | |
| assert args[0].agent_state == AgentState.STOPPED | |
| assert args[1] == EventSource.ENVIRONMENT | |
| mock_display_shutdown.assert_called_once_with(usage_metrics, sid) | |
| assert result is True | |
| def test_exit_without_confirmation(self, mock_display_shutdown, mock_cli_confirm): | |
| event_stream = MagicMock(spec=EventStream) | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| # Mock user rejecting exit | |
| mock_cli_confirm.return_value = 1 # Second option, which is "No, dismiss" | |
| # Call the function under test | |
| result = handle_exit_command(event_stream, usage_metrics, sid) | |
| # Verify correct behavior | |
| mock_cli_confirm.assert_called_once() | |
| event_stream.add_event.assert_not_called() | |
| mock_display_shutdown.assert_not_called() | |
| assert result is False | |
| class TestHandleHelpCommand: | |
| def test_help_command(self, mock_display_help): | |
| handle_help_command() | |
| mock_display_help.assert_called_once() | |
| class TestHandleStatusCommand: | |
| def test_status_command(self, mock_display_status): | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| handle_status_command(usage_metrics, sid) | |
| mock_display_status.assert_called_once_with(usage_metrics, sid) | |
| class TestHandleNewCommand: | |
| def test_new_with_confirmation(self, mock_display_shutdown, mock_cli_confirm): | |
| event_stream = MagicMock(spec=EventStream) | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| # Mock user confirming new session | |
| mock_cli_confirm.return_value = 0 # First option, which is "Yes, proceed" | |
| # Call the function under test | |
| close_repl, new_session = handle_new_command(event_stream, usage_metrics, sid) | |
| # Verify correct behavior | |
| mock_cli_confirm.assert_called_once() | |
| event_stream.add_event.assert_called_once() | |
| # Check event is the right type | |
| args, kwargs = event_stream.add_event.call_args | |
| assert isinstance(args[0], ChangeAgentStateAction) | |
| assert args[0].agent_state == AgentState.STOPPED | |
| assert args[1] == EventSource.ENVIRONMENT | |
| mock_display_shutdown.assert_called_once_with(usage_metrics, sid) | |
| assert close_repl is True | |
| assert new_session is True | |
| def test_new_without_confirmation(self, mock_display_shutdown, mock_cli_confirm): | |
| event_stream = MagicMock(spec=EventStream) | |
| usage_metrics = MagicMock(spec=UsageMetrics) | |
| sid = 'test-session-id' | |
| # Mock user rejecting new session | |
| mock_cli_confirm.return_value = 1 # Second option, which is "No, dismiss" | |
| # Call the function under test | |
| close_repl, new_session = handle_new_command(event_stream, usage_metrics, sid) | |
| # Verify correct behavior | |
| mock_cli_confirm.assert_called_once() | |
| event_stream.add_event.assert_not_called() | |
| mock_display_shutdown.assert_not_called() | |
| assert close_repl is False | |
| assert new_session is False | |
| class TestHandleInitCommand: | |
| async def test_init_local_runtime_successful(self, mock_init_repository): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| config.runtime = 'local' | |
| event_stream = MagicMock(spec=EventStream) | |
| current_dir = '/test/dir' | |
| # Mock successful repository initialization | |
| mock_init_repository.return_value = True | |
| # Call the function under test | |
| close_repl, reload_microagents = await handle_init_command( | |
| config, event_stream, current_dir | |
| ) | |
| # Verify correct behavior | |
| mock_init_repository.assert_called_once_with(current_dir) | |
| event_stream.add_event.assert_called_once() | |
| # Check event is the right type | |
| args, kwargs = event_stream.add_event.call_args | |
| assert isinstance(args[0], MessageAction) | |
| assert 'Please explore this repository' in args[0].content | |
| assert args[1] == EventSource.USER | |
| assert close_repl is True | |
| assert reload_microagents is True | |
| async def test_init_local_runtime_unsuccessful(self, mock_init_repository): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| config.runtime = 'local' | |
| event_stream = MagicMock(spec=EventStream) | |
| current_dir = '/test/dir' | |
| # Mock unsuccessful repository initialization | |
| mock_init_repository.return_value = False | |
| # Call the function under test | |
| close_repl, reload_microagents = await handle_init_command( | |
| config, event_stream, current_dir | |
| ) | |
| # Verify correct behavior | |
| mock_init_repository.assert_called_once_with(current_dir) | |
| event_stream.add_event.assert_not_called() | |
| assert close_repl is False | |
| assert reload_microagents is False | |
| async def test_init_non_local_runtime(self, mock_init_repository, mock_print): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| config.runtime = 'remote' # Not local | |
| event_stream = MagicMock(spec=EventStream) | |
| current_dir = '/test/dir' | |
| # Call the function under test | |
| close_repl, reload_microagents = await handle_init_command( | |
| config, event_stream, current_dir | |
| ) | |
| # Verify correct behavior | |
| mock_init_repository.assert_not_called() | |
| mock_print.assert_called_once() | |
| event_stream.add_event.assert_not_called() | |
| assert close_repl is False | |
| assert reload_microagents is False | |
| class TestHandleSettingsCommand: | |
| async def test_settings_basic_with_changes( | |
| self, | |
| mock_modify_basic, | |
| mock_cli_confirm, | |
| mock_display_settings, | |
| ): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| # Mock user selecting "Basic" settings | |
| mock_cli_confirm.return_value = 0 | |
| # Call the function under test | |
| await handle_settings_command(config, settings_store) | |
| # Verify correct behavior | |
| mock_display_settings.assert_called_once_with(config) | |
| mock_cli_confirm.assert_called_once() | |
| mock_modify_basic.assert_called_once_with(config, settings_store) | |
| async def test_settings_basic_without_changes( | |
| self, | |
| mock_modify_basic, | |
| mock_cli_confirm, | |
| mock_display_settings, | |
| ): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| # Mock user selecting "Basic" settings | |
| mock_cli_confirm.return_value = 0 | |
| # Call the function under test | |
| await handle_settings_command(config, settings_store) | |
| # Verify correct behavior | |
| mock_display_settings.assert_called_once_with(config) | |
| mock_cli_confirm.assert_called_once() | |
| mock_modify_basic.assert_called_once_with(config, settings_store) | |
| async def test_settings_advanced_with_changes( | |
| self, | |
| mock_modify_advanced, | |
| mock_cli_confirm, | |
| mock_display_settings, | |
| ): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| # Mock user selecting "Advanced" settings | |
| mock_cli_confirm.return_value = 1 | |
| # Call the function under test | |
| await handle_settings_command(config, settings_store) | |
| # Verify correct behavior | |
| mock_display_settings.assert_called_once_with(config) | |
| mock_cli_confirm.assert_called_once() | |
| mock_modify_advanced.assert_called_once_with(config, settings_store) | |
| async def test_settings_advanced_without_changes( | |
| self, | |
| mock_modify_advanced, | |
| mock_cli_confirm, | |
| mock_display_settings, | |
| ): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| # Mock user selecting "Advanced" settings | |
| mock_cli_confirm.return_value = 1 | |
| # Call the function under test | |
| await handle_settings_command(config, settings_store) | |
| # Verify correct behavior | |
| mock_display_settings.assert_called_once_with(config) | |
| mock_cli_confirm.assert_called_once() | |
| mock_modify_advanced.assert_called_once_with(config, settings_store) | |
| async def test_settings_go_back(self, mock_cli_confirm, mock_display_settings): | |
| config = MagicMock(spec=OpenHandsConfig) | |
| settings_store = MagicMock(spec=FileSettingsStore) | |
| # Mock user selecting "Go back" | |
| mock_cli_confirm.return_value = 2 | |
| # Call the function under test | |
| await handle_settings_command(config, settings_store) | |
| # Verify correct behavior | |
| mock_display_settings.assert_called_once_with(config) | |
| mock_cli_confirm.assert_called_once() | |
| class TestHandleResumeCommand: | |
| async def test_handle_resume_command(self): | |
| """Test that handle_resume_command adds the 'continue' message to the event stream.""" | |
| # Create a mock event stream | |
| event_stream = MagicMock(spec=EventStream) | |
| # Call the function | |
| close_repl, new_session_requested = await handle_resume_command(event_stream) | |
| # Check that the event stream add_event was called with the correct message action | |
| event_stream.add_event.assert_called_once() | |
| args, kwargs = event_stream.add_event.call_args | |
| message_action, source = args | |
| assert isinstance(message_action, MessageAction) | |
| assert message_action.content == 'continue' | |
| assert source == EventSource.USER | |
| # Check the return values | |
| assert close_repl is True | |
| assert new_session_requested is False | |