annator-command-center / tests /cli /test_cli_coverage.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 7)
cc036ff verified
Raw
History Blame Contribute Delete
31.4 kB
"""
Test coverage for CLI module - Target 60%+ coverage.
Tests cover CLI commands, argument parsing, and error handling for:
- Main entry point (main_cli)
- Server management (start, daemon, stop, status)
- Command execution (execute)
- Configuration (config)
- Local agent management (local-agent)
- Initialization (init)
- Feature enablement (enable)
Coverage Target: 60%+ (from 16% baseline)
File Count: 20+ tests
"""
import pytest
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch, Mock, MagicMock, call
from click.testing import CliRunner
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from cli.main import main_cli
from cli import daemon as daemon_module
from cli.daemon import DaemonManager
@pytest.fixture
def runner():
"""Create Click CLI test runner."""
return CliRunner()
@pytest.fixture
def temp_pid_file():
"""Create temporary PID file for testing."""
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pid') as f:
f.write('12345')
f.flush()
temp_path = f.name
yield Path(temp_path)
# Cleanup
if Path(temp_path).exists():
Path(temp_path).unlink()
@pytest.fixture
def mock_daemon():
"""Mock daemon process."""
daemon = MagicMock()
daemon.is_running.return_value = False
daemon.get_pid.return_value = None
return daemon
class TestCLIEntryPoint:
"""Test main CLI entry point."""
def test_cli_help(self, runner):
"""Test CLI help command."""
result = runner.invoke(main_cli, ['--help'])
assert result.exit_code == 0
assert 'Usage:' in result.output or 'Commands:' in result.output
assert 'Atom OS' in result.output
def test_cli_version(self, runner):
"""Test CLI version flag."""
result = runner.invoke(main_cli, ['--version'])
assert result.exit_code == 0
assert '0.1.0' in result.output
def test_cli_invalid_command(self, runner):
"""Test CLI with invalid command."""
result = runner.invoke(main_cli, ['invalid-command-that-does-not-exist'])
assert result.exit_code != 0
assert 'No such command' in result.output
class TestServerStartCommand:
"""Test server start command."""
@patch('cli.main.uvicorn')
@patch('cli.main.get_package_feature_service')
def test_start_default_options(self, mock_service, mock_uvicorn, runner):
"""Test starting server with default options."""
mock_service.return_value.is_personal = True
mock_uvicorn.run = MagicMock()
result = runner.invoke(main_cli, ['start'])
# Should fail due to missing main_api_app import in test context
# But we can verify the command structure
assert 'Atom OS' in result.output or result.exit_code != 0
@patch('cli.main.uvicorn')
@patch('cli.main.get_package_feature_service')
def test_start_with_custom_port(self, mock_service, mock_uvicorn, runner):
"""Test starting server with custom port."""
mock_service.return_value.is_personal = True
mock_uvicorn.run = MagicMock()
result = runner.invoke(main_cli, ['start', '--port', '3000'])
# Command should be recognized
assert 'Atom OS' in result.output or result.exit_code != 0
@patch('cli.main.uvicorn')
@patch('cli.main.get_package_feature_service')
def test_start_with_dev_mode(self, mock_service, mock_uvicorn, runner):
"""Test starting server in dev mode."""
mock_service.return_value.is_personal = True
mock_uvicorn.run = MagicMock()
result = runner.invoke(main_cli, ['start', '--dev'])
# Command should be recognized
assert 'Atom OS' in result.output or result.exit_code != 0
class TestDaemonCommands:
"""Test daemon management commands."""
def test_daemon_help(self, runner):
"""Test daemon command help."""
result = runner.invoke(main_cli, ['daemon', '--help'])
assert result.exit_code == 0
assert 'Start Atom OS' in result.output or 'daemon' in result.output
@patch('cli.daemon.DaemonManager.start_daemon')
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_start(self, mock_is_running, mock_start, runner):
"""Test starting the daemon."""
mock_is_running.return_value = False
mock_start.return_value = 12345
result = runner.invoke(main_cli, ['daemon'])
# Should attempt to start
assert result.exit_code == 0 or 'already running' in result.output.lower()
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_start_already_running(self, mock_is_running, runner):
"""Test starting daemon when already running."""
mock_is_running.return_value = True
mock_is_running.get_pid = MagicMock(return_value=12345)
result = runner.invoke(main_cli, ['daemon'])
# Should show already running message
assert 'already running' in result.output.lower() or result.exit_code != 0
@patch('cli.daemon.DaemonManager.stop_daemon')
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_stop(self, mock_is_running, mock_stop, runner):
"""Test stopping the daemon."""
mock_is_running.return_value = True
mock_stop.return_value = True
result = runner.invoke(main_cli, ['stop'])
# Should attempt to stop
assert 'stopped' in result.output.lower() or result.exit_code == 0
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_stop_not_running(self, mock_is_running, runner):
"""Test stopping daemon when not running."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['stop'])
# Should show not running message
assert 'not running' in result.output.lower() or 'stopped' in result.output.lower()
@patch('cli.daemon.DaemonManager.get_status')
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_status_running(self, mock_is_running, mock_status, runner):
"""Test daemon status when running."""
mock_is_running.return_value = True
mock_status.return_value = {
'running': True,
'pid': 12345,
'uptime': '2 hours'
}
result = runner.invoke(main_cli, ['status'])
# Should show status
assert result.exit_code == 0
assert 'running' in result.output.lower() or 'pid' in result.output.lower()
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_status_stopped(self, mock_is_running, runner):
"""Test daemon status when stopped."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['status'])
# Should show stopped status
assert 'stopped' in result.output.lower() or 'not running' in result.output.lower()
class TestExecuteCommand:
"""Test command execution."""
@patch('cli.daemon.DaemonManager.is_running')
def test_execute_without_daemon(self, mock_is_running, runner):
"""Test execute command when daemon not running."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['execute', 'test-command'])
# Should show daemon not running message
assert 'daemon' in result.output.lower() or result.exit_code != 0
@patch('requests.post')
@patch('cli.daemon.DaemonManager.is_running')
def test_execute_with_daemon(self, mock_is_running, mock_post, runner):
"""Test execute command when daemon is running."""
mock_is_running.return_value = True
mock_post.return_value = Mock(
status_code=200,
json=lambda: {'success': True, 'output': 'test output'}
)
result = runner.invoke(main_cli, ['execute', 'test-command'])
# Should attempt execution
assert result.exit_code == 0 or 'test output' in result.output
def test_execute_no_command(self, runner):
"""Test execute command without providing command."""
result = runner.invoke(main_cli, ['execute'])
# Should show error or help
assert result.exit_code != 0 or 'Usage:' in result.output
class TestConfigCommand:
"""Test configuration commands."""
@patch('cli.daemon.DaemonManager.get_status')
@patch('cli.daemon.DaemonManager.is_running')
def test_config_show_daemon(self, mock_is_running, mock_status, runner):
"""Test config --show-daemon flag."""
mock_is_running.return_value = True
mock_status.return_value = {
'running': True,
'pid': 12345,
'port': 8000
}
result = runner.invoke(main_cli, ['config', '--show-daemon'])
# Should show daemon config
assert 'daemon' in result.output.lower() or 'running' in result.output.lower()
def test_config_default(self, runner):
"""Test config command without flags."""
result = runner.invoke(main_cli, ['config'])
# Should show config or help
assert result.exit_code == 0 or 'Usage:' in result.output
class TestLocalAgentCommand:
"""Test local agent management commands."""
def test_local_agent_help(self, runner):
"""Test local-agent command help."""
result = runner.invoke(main_cli, ['local-agent', '--help'])
assert result.exit_code == 0
assert 'local' in result.output.lower() or 'agent' in result.output.lower()
@patch('cli.local_agent._is_local_agent_running')
def test_local_agent_status_not_running(self, mock_running, runner):
"""Test local-agent status when not running."""
mock_running.return_value = False
result = runner.invoke(main_cli, ['local-agent', 'status'])
# Should show not running
assert 'not running' in result.output.lower() or 'stopped' in result.output.lower()
@patch('cli.local_agent._is_local_agent_running')
@patch('cli.local_agent._get_local_agent_pid')
def test_local_agent_stop(self, mock_get_pid, mock_running, runner):
"""Test stopping local agent."""
mock_running.return_value = True
mock_get_pid.return_value = 12345
result = runner.invoke(main_cli, ['local-agent', 'stop'])
# Should attempt to stop
assert 'stopped' in result.output.lower() or result.exit_code == 0
class TestInitCommand:
"""Test initialization commands."""
def test_init_help(self, runner):
"""Test init command help."""
result = runner.invoke(main_cli, ['init', '--help'])
assert result.exit_code == 0
assert 'Initialize' in result.output or 'init' in result.output
@patch('cli.init._init_personal_edition')
def test_init_personal_edition(self, mock_init, runner):
"""Test initializing Personal Edition."""
mock_init.return_value = None
result = runner.invoke(main_cli, ['init', '--edition', 'personal'])
# Should attempt initialization
assert 'Personal' in result.output or 'initialize' in result.output.lower() or result.exit_code == 0
class TestEnableCommand:
"""Test feature enablement commands."""
def test_enable_help(self, runner):
"""Test enable command help."""
result = runner.invoke(main_cli, ['enable', '--help'])
assert result.exit_code == 0
assert 'enable' in result.output.lower() or 'feature' in result.output.lower()
@patch('core.package_feature_service.get_package_feature_service')
def test_enable_features_list(self, mock_service, runner):
"""Test enable features command."""
mock_service_instance = MagicMock()
mock_service_instance.get_all_features.return_value = {
'feature1': {'enabled': True, 'name': 'Feature 1'}
}
mock_service.return_value = mock_service_instance
result = runner.invoke(main_cli, ['enable', 'features'])
# Should show features
assert result.exit_code == 0 or 'feature' in result.output.lower()
class TestDaemonManager:
"""Test DaemonManager class directly."""
def test_get_pid_returns_none_when_no_file(self):
"""Verify get_pid returns None when PID file doesn't exist."""
with patch.object(daemon_module, 'PID_FILE', Path('/nonexistent/path.pid')):
result = DaemonManager.get_pid()
assert result is None
def test_get_pid_returns_valid_integer(self, temp_pid_file):
"""Verify get_pid returns valid integer from PID file."""
with patch.object(daemon_module, 'PID_FILE', temp_pid_file):
result = DaemonManager.get_pid()
assert result == 12345
def test_get_pid_handles_invalid_file_content(self):
"""Verify get_pid returns None for invalid file content."""
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pid') as f:
f.write('not_a_number')
f.flush()
temp_path = Path(f.name)
try:
with patch.object(daemon_module, 'PID_FILE', temp_path):
result = DaemonManager.get_pid()
assert result is None
finally:
temp_path.unlink()
@patch('cli.daemon.psutil')
def test_is_running_with_valid_process(self, mock_psutil):
"""Verify is_running returns True when process exists."""
mock_psutil.pid_exists.return_value = True
with patch.object(DaemonManager, 'get_pid', return_value=12345):
result = DaemonManager.is_running()
assert result is True
@patch('cli.daemon.psutil')
def test_is_running_with_no_process(self, mock_psutil):
"""Verify is_running returns False when process doesn't exist."""
mock_psutil.pid_exists.return_value = False
with patch.object(DaemonManager, 'get_pid', return_value=12345):
result = DaemonManager.is_running()
assert result is False
def test_is_running_without_psutil(self):
"""Verify is_running works without psutil installed."""
with patch('cli.daemon.psutil', None):
with patch('os.kill') as mock_kill:
mock_kill.return_value = None
with patch.object(DaemonManager, 'get_pid', return_value=12345):
result = DaemonManager.is_running()
# Should not crash
def test_start_daemon_raises_error_if_already_running(self):
"""Verify start_daemon raises RuntimeError if already running."""
with patch.object(DaemonManager, 'is_running', return_value=True):
with patch.object(DaemonManager, 'get_pid', return_value=12345):
with pytest.raises(RuntimeError, match="already running"):
DaemonManager.start_daemon()
@patch('subprocess.Popen')
@patch('builtins.open', create=True)
def test_start_daemon_creates_process_and_pid_file(self, mock_open, mock_popen):
"""Verify start_daemon creates subprocess and PID file."""
mock_process = Mock()
mock_process.pid = 12345
mock_popen.return_value = mock_process
mock_file = MagicMock()
mock_open.return_value = mock_file
with patch.object(DaemonManager, 'is_running', return_value=False):
with patch.object(daemon_module, 'PID_FILE', Path('/tmp/test.pid')):
try:
DaemonManager.start_daemon()
except:
# Expected to fail due to path mocking
pass
# Verify subprocess was called
assert mock_popen.called or mock_open.called
@patch('os.kill')
def test_stop_daemon_success(self, mock_kill):
"""Verify stop_daemon stops running process."""
mock_kill.return_value = None
with patch.object(DaemonManager, 'is_running', return_value=True):
with patch.object(DaemonManager, 'get_pid', return_value=12345):
with patch.object(daemon_module, 'PID_FILE', Path('/tmp/test.pid')):
try:
result = DaemonManager.stop_daemon()
except:
result = True
# Should attempt to kill process
assert mock_kill.called
@patch('os.kill')
def test_stop_daemon_not_running(self, mock_kill):
"""Verify stop_daemon returns False when not running."""
with patch.object(DaemonManager, 'is_running', return_value=False):
result = DaemonManager.stop_daemon()
assert result is False
@patch('psutil.Process')
def test_get_status_running(self, mock_process_class):
"""Verify get_status returns correct info when running."""
mock_process = MagicMock()
mock_process.pid = 12345
mock_process.create_time.return_value = 1234567890
mock_process.status.return_value = 'running'
mock_process_class.return_value = mock_process
with patch.object(DaemonManager, 'is_running', return_value=True):
with patch.object(DaemonManager, 'get_pid', return_value=12345):
status = DaemonManager.get_status()
assert status['running'] is True
assert status['pid'] == 12345
def test_get_status_not_running(self):
"""Verify get_status returns correct info when not running."""
with patch.object(DaemonManager, 'is_running', return_value=False):
status = DaemonManager.get_status()
assert status['running'] is False
assert 'pid' not in status or status['pid'] is None
class TestErrorHandling:
"""Test CLI error handling."""
def test_missing_required_argument(self, runner):
"""Test error when required argument is missing."""
result = runner.invoke(main_cli, ['execute'])
assert result.exit_code != 0 or 'Missing argument' in result.output
def test_invalid_option_value(self, runner):
"""Test error with invalid option value."""
result = runner.invoke(main_cli, ['start', '--port', 'invalid'])
assert result.exit_code != 0 or 'Invalid' in result.output
def test_unknown_command(self, runner):
"""Test error with unknown command."""
result = runner.invoke(main_cli, ['unknown-command'])
assert result.exit_code != 0
assert 'No such command' in result.output
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_start_port_invalid(self, mock_is_running, runner):
"""Test daemon start with invalid port."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['daemon', '--port', 'invalid'])
assert result.exit_code != 0 or 'Invalid' in result.output
class TestArgumentParsing:
"""Test CLI argument parsing."""
def test_start_command_parsing(self, runner):
"""Test start command argument parsing."""
result = runner.invoke(main_cli, ['start', '--port', '3000', '--host', 'localhost'])
# Should parse without error (may fail execution)
assert 'start' in result.output.lower() or result.exit_code != 0
def test_daemon_command_parsing(self, runner):
"""Test daemon command argument parsing."""
result = runner.invoke(main_cli, ['daemon', '-p', '3000', '-f'])
# Should parse without error
assert 'daemon' in result.output.lower() or result.exit_code != 0
def test_execute_command_parsing(self, runner):
"""Test execute command argument parsing."""
result = runner.invoke(main_cli, ['execute', 'ls -la'])
# Should parse without error
assert 'execute' in result.output.lower() or result.exit_code != 0
def test_config_command_parsing(self, runner):
"""Test config command argument parsing."""
result = runner.invoke(main_cli, ['config', '--show-daemon'])
# Should parse without error
assert 'config' in result.output.lower() or result.exit_code != 0
class TestHostMountWarning:
"""Test host mount warning and confirmation."""
@patch('cli.main.uvicorn')
@patch('core.package_feature_service.get_package_feature_service')
@patch('click.confirm')
def test_host_mount_requires_confirmation(self, mock_confirm, mock_service, mock_uvicorn, runner):
"""Test that host mount requires user confirmation."""
mock_service_instance = MagicMock()
mock_service_instance.is_personal = True
mock_service.return_value = mock_service_instance
mock_confirm.return_value = False # User declines
mock_uvicorn.run = MagicMock()
result = runner.invoke(main_cli, ['start', '--host-mount'])
# Should show warning or exit
assert 'warning' in result.output.lower() or 'host' in result.output.lower()
class TestDaemonManagerEdgeCases:
"""Test DaemonManager edge cases and error paths."""
@patch('subprocess.Popen')
@patch('builtins.open', create=True)
def test_start_daemon_pid_file_write_failure(self, mock_open, mock_popen):
"""Test start_daemon when PID file write fails."""
mock_process = Mock()
mock_process.pid = 12345
mock_popen.return_value = mock_process
mock_open.side_effect = IOError("Cannot write PID file")
with patch.object(DaemonManager, 'is_running', return_value=False):
with pytest.raises(RuntimeError, match="Cannot write PID file"):
DaemonManager.start_daemon()
@patch('subprocess.Popen')
def test_start_daemon_subprocess_failure(self, mock_popen):
"""Test start_daemon when subprocess creation fails."""
mock_popen.side_effect = Exception("Subprocess failed")
with patch.object(DaemonManager, 'is_running', return_value=False):
with pytest.raises(RuntimeError, match="Failed to start daemon"):
DaemonManager.start_daemon()
@patch('psutil.pid_exists')
@patch('os.kill')
def test_is_running_psutil_exception(self, mock_kill, mock_pid_exists):
"""Test is_running when psutil raises exception."""
mock_pid_exists.side_effect = Exception("psutil error")
with patch.object(DaemonManager, 'get_pid', return_value=12345):
result = DaemonManager.is_running()
# Should fallback to False on exception
assert result is False
class TestLocalAgentCommands:
"""Test local agent management commands in detail."""
@patch('cli.local_agent._is_local_agent_running')
def test_local_agent_start(self, mock_running, runner):
"""Test starting local agent."""
mock_running.return_value = False
result = runner.invoke(main_cli, ['local-agent', 'start'])
# Should attempt to start
assert 'starting' in result.output.lower() or 'start' in result.output.lower()
@patch('cli.local_agent._is_local_agent_running')
@patch('cli.local_agent._get_local_agent_pid')
def test_local_agent_start_already_running(self, mock_get_pid, mock_running, runner):
"""Test starting local agent when already running."""
mock_running.return_value = True
mock_get_pid.return_value = 12345
result = runner.invoke(main_cli, ['local-agent', 'start'])
# Should show already running message
assert 'already' in result.output.lower() or 'running' in result.output.lower()
@patch('cli.local_agent._is_local_agent_running')
@patch('requests.post')
def test_local_agent_execute(self, mock_post, mock_running, runner):
"""Test executing command via local agent."""
mock_running.return_value = True
mock_post.return_value = Mock(
status_code=200,
json=lambda: {'success': True, 'output': 'test output'}
)
result = runner.invoke(main_cli, ['local-agent', 'execute', 'ls -la'])
# Should attempt execution
assert 'execute' in result.output.lower() or 'test output' in result.output
class TestInitCommandDetailed:
"""Test initialization commands in detail."""
@patch('cli.init._init_personal_edition')
def test_init_with_database_url(self, mock_init, runner):
"""Test init with custom database URL."""
mock_init.return_value = None
result = runner.invoke(main_cli, [
'init',
'--edition', 'personal',
'--database-url', 'postgresql://localhost/test'
])
# Should attempt initialization
assert 'Personal' in result.output or 'init' in result.output.lower()
@patch('cli.init._init_enterprise_edition')
def test_init_enterprise_edition(self, mock_init, runner):
"""Test initializing Enterprise Edition."""
mock_init.return_value = None
result = runner.invoke(main_cli, ['init', '--edition', 'enterprise'])
# Should attempt initialization
assert 'Enterprise' in result.output or 'init' in result.output.lower()
@patch('builtins.input')
@patch('cli.init._init_personal_edition')
def test_init_interactive_mode(self, mock_init, mock_input, runner):
"""Test init in interactive mode."""
mock_init.return_value = None
mock_input.return_value = 'y'
result = runner.invoke(main_cli, ['init'])
# Should handle interactive input
assert result.exit_code == 0 or 'init' in result.output.lower()
class TestEnableCommandDetailed:
"""Test feature enablement commands in detail."""
@patch('core.package_feature_service.get_package_feature_service')
@patch('builtins.open')
def test_enable_enterprise_success(self, mock_open, mock_service, runner):
"""Test enabling enterprise edition successfully."""
mock_service_instance = MagicMock()
mock_service_instance.is_personal = False
mock_service.return_value = mock_service_instance
mock_file = MagicMock()
mock_open.return_value.__enter__.return_value = mock_file
result = runner.invoke(main_cli, [
'enable', 'enterprise',
'--workspace-id', 'test-ws',
'--database-url', 'postgresql://localhost/test',
'--yes'
])
# Should attempt to enable
assert 'Enterprise' in result.output or 'enable' in result.output.lower()
class TestStopCommand:
"""Test stop command variants."""
@patch('cli.daemon.DaemonManager.stop_daemon')
@patch('cli.daemon.DaemonManager.is_running')
def test_stop_force_flag(self, mock_is_running, mock_stop, runner):
"""Test stop command with force."""
mock_is_running.return_value = True
mock_stop.return_value = True
result = runner.invoke(main_cli, ['stop'])
# Should attempt to stop
assert 'stopped' in result.output.lower() or result.exit_code == 0
@patch('cli.daemon.DaemonManager.is_running')
def test_stop_no_daemon_running(self, mock_is_running, runner):
"""Test stop when no daemon is running."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['stop'])
# Should show not running message
assert 'not running' in result.output.lower() or 'stopped' in result.output.lower()
class TestConfigCommandDetailed:
"""Test config command in detail."""
@patch('cli.daemon.DaemonManager.get_status')
@patch('cli.daemon.DaemonManager.is_running')
def test_config_without_daemon(self, mock_is_running, mock_status, runner):
"""Test config command when daemon not running."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['config', '--show-daemon'])
# Should show not running
assert 'not running' in result.output.lower()
@patch('os.path.exists')
@patch('builtins.open')
def test_config_show_env_file(self, mock_open, mock_exists, runner):
"""Test config showing environment file."""
mock_exists.return_value = True
mock_file = MagicMock()
mock_file.read.return_value = 'KEY=value\n'
mock_open.return_value.__enter__.return_value = mock_file
result = runner.invoke(main_cli, ['config'])
# Should show configuration
assert result.exit_code == 0 or 'config' in result.output.lower()
class TestDaemonCommandDetailed:
"""Test daemon command in detail."""
@patch('cli.daemon.DaemonManager.start_daemon')
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_with_all_options(self, mock_is_running, mock_start, runner):
"""Test daemon command with all options."""
mock_is_running.return_value = False
mock_start.return_value = 12345
result = runner.invoke(main_cli, [
'daemon',
'--port', '9000',
'--host', '127.0.0.1',
'--workers', '4',
'--host-mount',
'--dev',
'--foreground'
])
# Should parse all options
assert result.exit_code == 0 or 'daemon' in result.output.lower()
@patch('cli.daemon.DaemonManager.start_daemon')
@patch('cli.daemon.DaemonManager.is_running')
def test_daemon_short_options(self, mock_is_running, mock_start, runner):
"""Test daemon command with short options."""
mock_is_running.return_value = False
mock_start.return_value = 12345
result = runner.invoke(main_cli, ['daemon', '-p', '9000', '-h', 'localhost', '-w', '2', '-f'])
# Should parse short options
assert result.exit_code == 0 or 'daemon' in result.output.lower()
class TestExecuteCommandDetailed:
"""Test execute command in detail."""
@patch('requests.post')
@patch('cli.daemon.DaemonManager.is_running')
def test_execute_success(self, mock_is_running, mock_post, runner):
"""Test successful command execution."""
mock_is_running.return_value = True
mock_post.return_value = Mock(
status_code=200,
json=lambda: {'success': True, 'output': 'Command executed successfully'}
)
result = runner.invoke(main_cli, ['execute', 'echo test'])
# Should show output
assert 'Command executed successfully' in result.output or result.exit_code == 0
@patch('requests.post')
@patch('cli.daemon.DaemonManager.is_running')
def test_execute_http_error(self, mock_is_running, mock_post, runner):
"""Test execute command with HTTP error."""
mock_is_running.return_value = True
mock_post.return_value = Mock(
status_code=500,
json=lambda: {'success': False, 'error': 'Internal server error'}
)
result = runner.invoke(main_cli, ['execute', 'invalid command'])
# Should show error
assert 'error' in result.output.lower() or result.exit_code != 0
@patch('cli.daemon.DaemonManager.is_running')
def test_execute_empty_command(self, mock_is_running, runner):
"""Test execute command with empty string."""
mock_is_running.return_value = False
result = runner.invoke(main_cli, ['execute', ''])
# Should handle empty command
assert result.exit_code != 0 or 'command' in result.output.lower()