mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 13:52:43 +08:00
369 lines
16 KiB
Python
369 lines
16 KiB
Python
"""Unit tests for CLI alias setup functionality."""
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from openhands.cli.main import alias_setup_declined as main_alias_setup_declined
|
|
from openhands.cli.main import aliases_exist_in_shell_config, run_alias_setup_flow
|
|
from openhands.cli.shell_config import (
|
|
ShellConfigManager,
|
|
add_aliases_to_shell_config,
|
|
alias_setup_declined,
|
|
get_shell_config_path,
|
|
mark_alias_setup_declined,
|
|
)
|
|
from openhands.core.config import OpenHandsConfig
|
|
|
|
|
|
def test_get_shell_config_path_no_files_fallback():
|
|
"""Test shell config path fallback when no shell detection and no config files exist."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to raise an exception (detection failure)
|
|
with patch(
|
|
'shellingham.detect_shell',
|
|
side_effect=Exception('Shell detection failed'),
|
|
):
|
|
profile_path = get_shell_config_path()
|
|
assert profile_path.name == '.bash_profile'
|
|
|
|
|
|
def test_get_shell_config_path_bash_fallback():
|
|
"""Test shell config path fallback to bash when it exists."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create .bashrc
|
|
bashrc = Path(temp_dir) / '.bashrc'
|
|
bashrc.touch()
|
|
|
|
# Mock shellingham to raise an exception (detection failure)
|
|
with patch(
|
|
'shellingham.detect_shell',
|
|
side_effect=Exception('Shell detection failed'),
|
|
):
|
|
profile_path = get_shell_config_path()
|
|
assert profile_path.name == '.bashrc'
|
|
|
|
|
|
def test_get_shell_config_path_with_bash_detection():
|
|
"""Test shell config path when bash is detected."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create .bashrc
|
|
bashrc = Path(temp_dir) / '.bashrc'
|
|
bashrc.touch()
|
|
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
profile_path = get_shell_config_path()
|
|
assert profile_path.name == '.bashrc'
|
|
|
|
|
|
def test_get_shell_config_path_with_zsh_detection():
|
|
"""Test shell config path when zsh is detected."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create .zshrc
|
|
zshrc = Path(temp_dir) / '.zshrc'
|
|
zshrc.touch()
|
|
|
|
# Mock shellingham to return zsh
|
|
with patch('shellingham.detect_shell', return_value=('zsh', 'zsh')):
|
|
profile_path = get_shell_config_path()
|
|
assert profile_path.name == '.zshrc'
|
|
|
|
|
|
def test_get_shell_config_path_with_fish_detection():
|
|
"""Test shell config path when fish is detected."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create fish config directory and file
|
|
fish_config_dir = Path(temp_dir) / '.config' / 'fish'
|
|
fish_config_dir.mkdir(parents=True)
|
|
fish_config = fish_config_dir / 'config.fish'
|
|
fish_config.touch()
|
|
|
|
# Mock shellingham to return fish
|
|
with patch('shellingham.detect_shell', return_value=('fish', 'fish')):
|
|
profile_path = get_shell_config_path()
|
|
assert profile_path.name == 'config.fish'
|
|
assert 'fish' in str(profile_path)
|
|
|
|
|
|
def test_add_aliases_to_shell_config_bash():
|
|
"""Test adding aliases to bash config."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
# Add aliases
|
|
success = add_aliases_to_shell_config()
|
|
assert success is True
|
|
|
|
# Get the actual path that was used
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
profile_path = get_shell_config_path()
|
|
|
|
# Check that the aliases were added
|
|
with open(profile_path, 'r') as f:
|
|
content = f.read()
|
|
assert 'alias openhands=' in content
|
|
assert 'alias oh=' in content
|
|
assert 'uvx --python 3.12 --from openhands-ai openhands' in content
|
|
|
|
|
|
def test_add_aliases_to_shell_config_zsh():
|
|
"""Test adding aliases to zsh config."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return zsh
|
|
with patch('shellingham.detect_shell', return_value=('zsh', 'zsh')):
|
|
# Add aliases
|
|
success = add_aliases_to_shell_config()
|
|
assert success is True
|
|
|
|
# Check that the aliases were added to .zshrc
|
|
profile_path = Path(temp_dir) / '.zshrc'
|
|
with open(profile_path, 'r') as f:
|
|
content = f.read()
|
|
assert 'alias openhands=' in content
|
|
assert 'alias oh=' in content
|
|
assert 'uvx --python 3.12 --from openhands-ai openhands' in content
|
|
|
|
|
|
def test_add_aliases_handles_existing_aliases():
|
|
"""Test that adding aliases handles existing aliases correctly."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
# Add aliases first time
|
|
success = add_aliases_to_shell_config()
|
|
assert success is True
|
|
|
|
# Try adding again - should detect existing aliases
|
|
success = add_aliases_to_shell_config()
|
|
assert success is True
|
|
|
|
# Get the actual path that was used
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
profile_path = get_shell_config_path()
|
|
|
|
# Check that aliases weren't duplicated
|
|
with open(profile_path, 'r') as f:
|
|
content = f.read()
|
|
# Count occurrences of the alias
|
|
openhands_count = content.count('alias openhands=')
|
|
oh_count = content.count('alias oh=')
|
|
assert openhands_count == 1
|
|
assert oh_count == 1
|
|
|
|
|
|
def test_aliases_exist_in_shell_config_no_file():
|
|
"""Test alias detection when no shell config exists."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
assert aliases_exist_in_shell_config() is False
|
|
|
|
|
|
def test_aliases_exist_in_shell_config_no_aliases():
|
|
"""Test alias detection when shell config exists but has no aliases."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
# Create bash profile with other content
|
|
profile_path = get_shell_config_path()
|
|
with open(profile_path, 'w') as f:
|
|
f.write('export PATH=$PATH:/usr/local/bin\n')
|
|
|
|
assert aliases_exist_in_shell_config() is False
|
|
|
|
|
|
def test_aliases_exist_in_shell_config_with_aliases():
|
|
"""Test alias detection when aliases exist."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mock shellingham to return bash
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
# Add aliases first
|
|
add_aliases_to_shell_config()
|
|
|
|
assert aliases_exist_in_shell_config() is True
|
|
|
|
|
|
def test_shell_config_manager_basic_functionality():
|
|
"""Test basic ShellConfigManager functionality."""
|
|
manager = ShellConfigManager()
|
|
|
|
# Test command customization
|
|
custom_manager = ShellConfigManager(command='custom-command')
|
|
assert custom_manager.command == 'custom-command'
|
|
|
|
# Test shell type detection from path
|
|
assert manager.get_shell_type_from_path(Path('/home/user/.bashrc')) == 'bash'
|
|
assert manager.get_shell_type_from_path(Path('/home/user/.zshrc')) == 'zsh'
|
|
assert (
|
|
manager.get_shell_type_from_path(Path('/home/user/.config/fish/config.fish'))
|
|
== 'fish'
|
|
)
|
|
|
|
|
|
def test_shell_config_manager_reload_commands():
|
|
"""Test reload command generation."""
|
|
manager = ShellConfigManager()
|
|
|
|
# Test different shell reload commands
|
|
assert 'source ~/.zshrc' in manager.get_reload_command(Path('/home/user/.zshrc'))
|
|
assert 'source ~/.bashrc' in manager.get_reload_command(Path('/home/user/.bashrc'))
|
|
assert 'source ~/.bash_profile' in manager.get_reload_command(
|
|
Path('/home/user/.bash_profile')
|
|
)
|
|
assert 'source ~/.config/fish/config.fish' in manager.get_reload_command(
|
|
Path('/home/user/.config/fish/config.fish')
|
|
)
|
|
|
|
|
|
def test_shell_config_manager_template_rendering():
|
|
"""Test that templates are properly rendered."""
|
|
manager = ShellConfigManager(command='test-command')
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create a bash config file
|
|
bashrc = Path(temp_dir) / '.bashrc'
|
|
bashrc.touch()
|
|
|
|
# Mock shell detection
|
|
with patch.object(manager, 'detect_shell', return_value='bash'):
|
|
success = manager.add_aliases()
|
|
assert success is True
|
|
|
|
# Check that the custom command was used
|
|
with open(bashrc, 'r') as f:
|
|
content = f.read()
|
|
assert 'test-command' in content
|
|
assert 'alias openhands="test-command"' in content
|
|
assert 'alias oh="test-command"' in content
|
|
|
|
|
|
def test_alias_setup_declined_false():
|
|
"""Test alias setup declined check when marker file doesn't exist."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
assert alias_setup_declined() is False
|
|
|
|
|
|
def test_alias_setup_declined_true():
|
|
"""Test alias setup declined check when marker file exists."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Create the marker file
|
|
mark_alias_setup_declined()
|
|
assert alias_setup_declined() is True
|
|
|
|
|
|
def test_mark_alias_setup_declined():
|
|
"""Test marking alias setup as declined creates the marker file."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Initially should be False
|
|
assert alias_setup_declined() is False
|
|
|
|
# Mark as declined
|
|
mark_alias_setup_declined()
|
|
|
|
# Should now be True
|
|
assert alias_setup_declined() is True
|
|
|
|
# Verify the file exists
|
|
marker_file = Path(temp_dir) / '.openhands' / '.cli_alias_setup_declined'
|
|
assert marker_file.exists()
|
|
|
|
|
|
def test_alias_setup_declined_persisted():
|
|
"""Test that when user declines alias setup, their choice is persisted."""
|
|
config = OpenHandsConfig()
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
with patch(
|
|
'openhands.cli.shell_config.aliases_exist_in_shell_config',
|
|
return_value=False,
|
|
):
|
|
with patch(
|
|
'openhands.cli.main.cli_confirm', return_value=1
|
|
): # User chooses "No"
|
|
with patch('prompt_toolkit.print_formatted_text'):
|
|
# Initially, user hasn't declined
|
|
assert not alias_setup_declined()
|
|
|
|
# Run the alias setup flow
|
|
run_alias_setup_flow(config)
|
|
|
|
# After declining, the marker should be set
|
|
assert alias_setup_declined()
|
|
|
|
|
|
def test_alias_setup_skipped_when_previously_declined():
|
|
"""Test that alias setup is skipped when user has previously declined."""
|
|
OpenHandsConfig()
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
# Mark that user has previously declined
|
|
mark_alias_setup_declined()
|
|
assert alias_setup_declined()
|
|
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
with patch(
|
|
'openhands.cli.shell_config.aliases_exist_in_shell_config',
|
|
return_value=False,
|
|
):
|
|
with patch('openhands.cli.main.cli_confirm'):
|
|
with patch('prompt_toolkit.print_formatted_text'):
|
|
# This should not show the setup flow since user previously declined
|
|
# We test this by checking the main logic conditions
|
|
|
|
should_show = (
|
|
not aliases_exist_in_shell_config()
|
|
and not main_alias_setup_declined()
|
|
)
|
|
|
|
assert not should_show, (
|
|
'Alias setup should be skipped when user previously declined'
|
|
)
|
|
|
|
|
|
def test_alias_setup_accepted_does_not_set_declined_flag():
|
|
"""Test that when user accepts alias setup, no declined marker is created."""
|
|
config = OpenHandsConfig()
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
with patch('openhands.cli.shell_config.Path.home', return_value=Path(temp_dir)):
|
|
with patch('shellingham.detect_shell', return_value=('bash', 'bash')):
|
|
with patch(
|
|
'openhands.cli.shell_config.aliases_exist_in_shell_config',
|
|
return_value=False,
|
|
):
|
|
with patch(
|
|
'openhands.cli.main.cli_confirm', return_value=0
|
|
): # User chooses "Yes"
|
|
with patch(
|
|
'openhands.cli.shell_config.add_aliases_to_shell_config',
|
|
return_value=True,
|
|
):
|
|
with patch('prompt_toolkit.print_formatted_text'):
|
|
# Initially, user hasn't declined
|
|
assert not alias_setup_declined()
|
|
|
|
# Run the alias setup flow
|
|
run_alias_setup_flow(config)
|
|
|
|
# After accepting, the declined marker should still be False
|
|
assert not alias_setup_declined()
|