mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
638 lines
23 KiB
Python
638 lines
23 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from prompt_toolkit.formatted_text import HTML
|
|
|
|
from openhands.cli.commands import (
|
|
display_mcp_servers,
|
|
handle_commands,
|
|
handle_exit_command,
|
|
handle_help_command,
|
|
handle_init_command,
|
|
handle_mcp_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:
|
|
@pytest.fixture
|
|
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)
|
|
agent_state = AgentState.RUNNING
|
|
|
|
return {
|
|
'event_stream': event_stream,
|
|
'usage_metrics': usage_metrics,
|
|
'sid': sid,
|
|
'config': config,
|
|
'current_dir': current_dir,
|
|
'settings_store': settings_store,
|
|
'agent_state': agent_state,
|
|
}
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_exit_command')
|
|
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['config'],
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_help_command')
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_init_command')
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_status_command')
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_new_command')
|
|
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['config'],
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_settings_command')
|
|
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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.handle_mcp_command')
|
|
async def test_handle_mcp_command(self, mock_handle_mcp, mock_dependencies):
|
|
close_repl, reload_microagents, new_session, _ = await handle_commands(
|
|
'/mcp', **mock_dependencies
|
|
)
|
|
|
|
mock_handle_mcp.assert_called_once_with(mock_dependencies['config'])
|
|
assert close_repl is False
|
|
assert reload_microagents is False
|
|
assert new_session is False
|
|
|
|
@pytest.mark.asyncio
|
|
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:
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.display_shutdown_message')
|
|
def test_exit_with_confirmation(self, mock_display_shutdown, mock_cli_confirm):
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
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(config, 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
|
|
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.display_shutdown_message')
|
|
def test_exit_without_confirmation(self, mock_display_shutdown, mock_cli_confirm):
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
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(config, 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:
|
|
@patch('openhands.cli.commands.display_help')
|
|
def test_help_command(self, mock_display_help):
|
|
handle_help_command()
|
|
mock_display_help.assert_called_once()
|
|
|
|
|
|
class TestDisplayMcpServers:
|
|
@patch('openhands.cli.commands.print_formatted_text')
|
|
def test_display_mcp_servers_no_servers(self, mock_print):
|
|
from openhands.core.config.mcp_config import MCPConfig
|
|
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
config.mcp = MCPConfig() # Empty config with no servers
|
|
|
|
display_mcp_servers(config)
|
|
|
|
mock_print.assert_called_once()
|
|
call_args = mock_print.call_args[0][0]
|
|
assert 'No custom MCP servers configured' in call_args
|
|
assert (
|
|
'https://docs.all-hands.dev/usage/how-to/cli-mode#using-mcp-servers'
|
|
in call_args
|
|
)
|
|
|
|
@patch('openhands.cli.commands.print_formatted_text')
|
|
def test_display_mcp_servers_with_servers(self, mock_print):
|
|
from openhands.core.config.mcp_config import (
|
|
MCPConfig,
|
|
MCPSHTTPServerConfig,
|
|
MCPSSEServerConfig,
|
|
MCPStdioServerConfig,
|
|
)
|
|
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
config.mcp = MCPConfig(
|
|
sse_servers=[MCPSSEServerConfig(url='https://example.com/sse')],
|
|
stdio_servers=[MCPStdioServerConfig(name='tavily', command='npx')],
|
|
shttp_servers=[MCPSHTTPServerConfig(url='http://localhost:3000/mcp')],
|
|
)
|
|
|
|
display_mcp_servers(config)
|
|
|
|
# Should be called multiple times for different sections
|
|
assert mock_print.call_count >= 4
|
|
|
|
# Check that the summary is printed
|
|
first_call = mock_print.call_args_list[0][0][0]
|
|
assert 'Configured MCP servers:' in first_call
|
|
assert 'SSE servers: 1' in first_call
|
|
assert 'Stdio servers: 1' in first_call
|
|
assert 'SHTTP servers: 1' in first_call
|
|
assert 'Total: 3' in first_call
|
|
|
|
|
|
class TestHandleMcpCommand:
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.display_mcp_servers')
|
|
async def test_handle_mcp_command_list_action(self, mock_display, mock_cli_confirm):
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
mock_cli_confirm.return_value = 0 # List action
|
|
|
|
await handle_mcp_command(config)
|
|
|
|
mock_cli_confirm.assert_called_once_with(
|
|
config,
|
|
'MCP Server Configuration',
|
|
[
|
|
'List configured servers',
|
|
'Add new server',
|
|
'Remove server',
|
|
'View errors',
|
|
'Go back',
|
|
],
|
|
)
|
|
mock_display.assert_called_once_with(config)
|
|
|
|
|
|
class TestHandleStatusCommand:
|
|
@patch('openhands.cli.commands.display_status')
|
|
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:
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.display_shutdown_message')
|
|
def test_new_with_confirmation(self, mock_display_shutdown, mock_cli_confirm):
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
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(
|
|
config, 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
|
|
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.display_shutdown_message')
|
|
def test_new_without_confirmation(self, mock_display_shutdown, mock_cli_confirm):
|
|
config = MagicMock(spec=OpenHandsConfig)
|
|
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(
|
|
config, 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:
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.init_repository')
|
|
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(config, 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
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.init_repository')
|
|
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(config, current_dir)
|
|
event_stream.add_event.assert_not_called()
|
|
|
|
assert close_repl is False
|
|
assert reload_microagents is False
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.print_formatted_text')
|
|
@patch('openhands.cli.commands.init_repository')
|
|
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:
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.display_settings')
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.modify_llm_settings_basic')
|
|
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)
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.display_settings')
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.modify_llm_settings_basic')
|
|
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)
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.display_settings')
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.modify_llm_settings_advanced')
|
|
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)
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.display_settings')
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
@patch('openhands.cli.commands.modify_llm_settings_advanced')
|
|
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)
|
|
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.display_settings')
|
|
@patch('openhands.cli.commands.cli_confirm')
|
|
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" (now option 4, index 3)
|
|
mock_cli_confirm.return_value = 3
|
|
|
|
# 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:
|
|
@pytest.mark.asyncio
|
|
@patch('openhands.cli.commands.print_formatted_text')
|
|
async def test_handle_resume_command_paused_state(self, mock_print):
|
|
"""Test that handle_resume_command works when agent is in PAUSED state."""
|
|
# Create a mock event stream
|
|
event_stream = MagicMock(spec=EventStream)
|
|
|
|
# Call the function with PAUSED state
|
|
close_repl, new_session_requested = await handle_resume_command(
|
|
event_stream, AgentState.PAUSED
|
|
)
|
|
|
|
# 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
|
|
|
|
# Verify no error message was printed
|
|
mock_print.assert_not_called()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize(
|
|
'invalid_state', [AgentState.RUNNING, AgentState.FINISHED, AgentState.ERROR]
|
|
)
|
|
@patch('openhands.cli.commands.print_formatted_text')
|
|
async def test_handle_resume_command_invalid_states(
|
|
self, mock_print, invalid_state
|
|
):
|
|
"""Test that handle_resume_command shows error for all non-PAUSED states."""
|
|
event_stream = MagicMock(spec=EventStream)
|
|
|
|
close_repl, new_session_requested = await handle_resume_command(
|
|
event_stream, invalid_state
|
|
)
|
|
|
|
# Check that no event was added to the stream
|
|
event_stream.add_event.assert_not_called()
|
|
|
|
# Verify print was called with the error message
|
|
assert mock_print.call_count == 1
|
|
error_call = mock_print.call_args_list[0][0][0]
|
|
assert isinstance(error_call, HTML)
|
|
assert 'Error: Agent is not paused' in str(error_call)
|
|
assert '/resume command is only available when agent is paused' in str(
|
|
error_call
|
|
)
|
|
|
|
# Check the return values
|
|
assert close_repl is False
|
|
assert new_session_requested is False
|
|
|
|
|
|
class TestMCPErrorHandling:
|
|
"""Test MCP error handling in commands."""
|
|
|
|
@patch('openhands.cli.commands.display_mcp_errors')
|
|
def test_handle_mcp_errors_command(self, mock_display_errors):
|
|
"""Test handling MCP errors command."""
|
|
from openhands.cli.commands import handle_mcp_errors_command
|
|
|
|
handle_mcp_errors_command()
|
|
|
|
mock_display_errors.assert_called_once()
|