Skip to main content
Glama

Sumanshu Arora

test_interactive_cli.py40.7 kB
""" Integration tests for the interactive CLI module. These tests focus on realistic user workflows and interactions, using real or controlled environments where possible. """ import json from unittest.mock import Mock, patch import pytest from mcp_template.cli.interactive_cli import InteractiveSession pytestmark = pytest.mark.integration @pytest.mark.integration class TestInteractiveSession: """Integration tests for InteractiveSession.""" def test_session_persistence(self): """Test that session state persists across operations.""" with patch( "mcp_template.cli.interactive_cli.CacheManager" ) as mock_cache_manager: mock_cache = Mock() mock_cache_manager.return_value = mock_cache mock_cache.get.return_value = {} session = InteractiveSession() # Select template and configure it session.select_template("demo") session.update_template_config( "demo", {"api_key": "test123", "endpoint": "https://api.example.com"} ) # Verify state is maintained assert session.selected_template == "demo" config = session.get_template_config("demo") assert config["api_key"] == "test123" assert config["endpoint"] == "https://api.example.com" def test_multiple_templates_config(self): """Test managing configuration for multiple templates.""" with patch( "mcp_template.cli.interactive_cli.CacheManager" ) as mock_cache_manager: mock_cache = Mock() mock_cache_manager.return_value = mock_cache mock_cache.get.return_value = {} session = InteractiveSession() # Configure multiple templates session.update_template_config("demo", {"api_key": "demo_key"}) session.update_template_config( "github", {"github_token": "github_key", "github_host": "https://api.github.com"}, ) # Verify isolation between templates demo_config = session.get_template_config("demo") github_config = session.get_template_config("github") assert demo_config["api_key"] == "demo_key" assert github_config["github_token"] == "github_key" assert "github_token" not in demo_config assert "api_key" not in github_config # Verify all configs assert demo_config == {"api_key": "demo_key"} assert github_config == { "github_token": "github_key", "github_host": "https://api.github.com", } def test_template_switching_workflow(self): """Test switching between templates.""" with patch( "mcp_template.cli.interactive_cli.CacheManager" ) as mock_cache_manager: mock_cache = Mock() mock_cache_manager.return_value = mock_cache mock_cache.get.return_value = {} session = InteractiveSession() # Start with demo template session.select_template("demo") assert session.get_prompt() == "mcpt(demo)> " # Switch to github template session.select_template("github") assert session.get_prompt() == "mcpt(github)> " # Unselect template session.unselect_template() assert session.get_prompt() == "mcpt> " @pytest.mark.integration class TestCommandWorkflows: """Integration tests for complete command workflows.""" @patch("mcp_template.cli.interactive_cli.MCPClient") @patch("mcp_template.cli.interactive_cli.get_session") def test_template_selection_and_tools_workflow( self, mock_get_session, mock_mcp_client ): """Test selecting a template and listing its tools.""" from mcp_template.cli.interactive_cli import list_tools, select_template # Setup mocks mock_session = Mock() mock_session.get_selected_template.return_value = None mock_get_session.return_value = mock_session with patch("mcp_template.cli.list_tools") as mock_cli_list_tools: # Select template select_template("demo") mock_session.select_template.assert_called_once_with("demo") # List tools for selected template mock_session.get_selected_template.return_value = ( "demo" # Simulate selection ) list_tools(template=None) # Verify the main CLI function was called with selected template mock_cli_list_tools.assert_called_once_with( template="demo", backend="docker", force_refresh=False, static=True, dynamic=True, output_format="table", ) @patch("mcp_template.cli.interactive_cli.get_session") def test_configuration_workflow(self, mock_get_session): """Test complete configuration workflow.""" from mcp_template.cli.interactive_cli import configure_template # Setup mocks mock_session = Mock() mock_client = Mock() mock_session.client = mock_client mock_client.list_templates.return_value = {"demo": {"version": "1.0.0"}} mock_session.get_selected_template.return_value = "demo" mock_get_session.return_value = mock_session with ( patch("mcp_template.cli.interactive_cli.console"), patch("mcp_template.cli.interactive_cli.show_config") as mock_show_config, ): # Configure template configure_template( template=None, config_pairs=["api_key=test123", "endpoint=https://api.example.com"], ) # Verify configuration was set with correct method mock_session.update_template_config.assert_called_once_with( "demo", {"api_key": "test123", "endpoint": "https://api.example.com"} ) mock_show_config.assert_called_once_with("demo") @patch("mcp_template.cli.interactive_cli.get_session") def test_tool_call_workflow(self, mock_get_session): """Test calling a tool with configuration.""" from mcp_template.cli.interactive_cli import call_tool # Setup mocks mock_session = Mock() mock_session.get_selected_template.return_value = "demo" mock_session.get_template_config.return_value = {"api_key": "test123"} # Mock the client methods mock_client = Mock() mock_session.client = mock_client mock_client.list_templates.return_value = {"demo": {"version": "1.0.0"}} mock_client.get_template_info.return_value = None mock_client.call_tool_with_config.return_value = { "success": True, "result": "Hello World", } # Mock the formatter mock_formatter = Mock() mock_session.formatter = mock_formatter mock_get_session.return_value = mock_session call_tool( template=None, tool_name="say_hello", args='{"name": "World"}', config_file=None, env=[], config=[], backend=None, no_pull=False, raw=False, force_stdio=False, ) # Verify the client method was called mock_client.call_tool_with_config.assert_called_once() @patch("mcp_template.cli.interactive_cli.get_session") def test_server_management_workflow(self, mock_get_session): """Test server deployment and management workflow.""" from mcp_template.cli.interactive_cli import deploy_template # Setup mocks mock_session = Mock() mock_session.get_selected_template.return_value = "demo" mock_session.get_template_config.return_value = {"api_key": "test123"} mock_get_session.return_value = mock_session with patch("mcp_template.cli.deploy") as mock_cli_deploy: # Deploy server deploy_template( template="demo", config_file=None, env=[], config=[], transport="http", port=None, no_pull=False, ) # Verify the main CLI deploy function was called mock_cli_deploy.assert_called_once() @pytest.mark.integration class TestErrorRecovery: """Integration tests for error recovery scenarios.""" @patch("mcp_template.cli.list") def test_api_error_recovery(self, mock_cli_list): """Test recovery from API errors.""" from mcp_template.cli.interactive_cli import list_templates # Setup mock to raise exception mock_cli_list.side_effect = Exception("Network error") with patch("mcp_template.cli.interactive_cli.console") as mock_console: # Should not raise exception, but handle error gracefully list_templates() # Should print error message error_calls = [ call for call in mock_console.print.call_args_list if "Error" in str(call) or "❌" in str(call) ] assert len(error_calls) > 0 @patch("mcp_template.cli.interactive_cli.get_session") def test_missing_template_error_recovery(self, mock_get_session): """Test recovery from missing template errors.""" from mcp_template.cli.interactive_cli import list_tools mock_session = Mock() mock_session.get_selected_template.return_value = None mock_get_session.return_value = mock_session with patch("mcp_template.cli.interactive_cli.console") as mock_console: # Should handle missing template gracefully list_tools(template=None) # Should print error message about missing template error_calls = [ call for call in mock_console.print.call_args_list if "❌" in str(call) and ("template" in str(call) or "Template" in str(call)) ] assert len(error_calls) > 0 @patch("mcp_template.cli.interactive_cli.get_session") def test_invalid_config_format_recovery(self, mock_get_session): """Test recovery from invalid configuration format.""" from mcp_template.cli.interactive_cli import configure_template mock_session = Mock() mock_client = Mock() mock_session.client = mock_client mock_client.list_templates.return_value = {"demo": {"version": "1.0.0"}} mock_session.get_selected_template.return_value = "demo" mock_get_session.return_value = mock_session with patch("mcp_template.cli.interactive_cli.console") as mock_console: # Should handle invalid format gracefully configure_template(template=None, config_pairs=["invalid_format_no_equals"]) # Should print error message about no valid config pairs error_calls = [ call for call in mock_console.print.call_args_list if "❌" in str(call) and ("configuration" in str(call) or "config" in str(call)) ] assert len(error_calls) > 0 @pytest.mark.integration class TestComplexWorkflows: """Integration tests for complex, multi-step workflows.""" @patch("mcp_template.cli.interactive_cli.get_session") def test_complete_user_session(self, mock_get_session): """Test a complete user session from start to finish.""" from mcp_template.cli.interactive_cli import ( call_tool, configure_template, deploy_template, list_tools, select_template, unselect_template, ) # Setup session mock_session = Mock() mock_client = Mock() mock_session.client = mock_client mock_client.list_templates.return_value = {"demo": {"version": "1.0.0"}} mock_client.get_template_info.return_value = None mock_client.call_tool_with_config.return_value = { "success": True, "result": "Hello World", } mock_session.get_selected_template.return_value = None mock_session.get_template_config.return_value = {} # Mock the formatter mock_formatter = Mock() mock_session.formatter = mock_formatter mock_get_session.return_value = mock_session with ( patch("mcp_template.cli.list_tools") as mock_cli_list_tools, patch("mcp_template.cli.deploy") as mock_cli_deploy, patch("mcp_template.cli.interactive_cli.show_config"), ): # 1. Select template select_template("demo") mock_session.select_template.assert_called_with("demo") # 2. Configure template mock_session.get_selected_template.return_value = ( "demo" # Simulate selection ) configure_template(template=None, config_pairs=["api_key=test123"]) mock_session.update_template_config.assert_called_with( "demo", {"api_key": "test123"} ) # 3. List tools list_tools(template=None) mock_cli_list_tools.assert_called() # 4. Call tool call_tool( template=None, tool_name="say_hello", args='{"name": "World"}', config_file=None, env=[], config=[], backend=None, no_pull=False, raw=False, force_stdio=False, ) mock_client.call_tool_with_config.assert_called() # 5. Deploy template deploy_template( template="demo", config_file=None, env=[], config=[], transport="http", port=None, no_pull=False, ) mock_cli_deploy.assert_called() # 6. Unselect template unselect_template() mock_session.unselect_template.assert_called() @patch("mcp_template.cli.interactive_cli.get_session") def test_multi_template_workflow(self, mock_get_session): """Test working with multiple templates in one session.""" from mcp_template.cli.interactive_cli import ( configure_template, list_tools, select_template, ) mock_session = Mock() mock_client = Mock() mock_session.client = mock_client mock_client.list_templates.return_value = { "demo": {"version": "1.0.0"}, "github": {"version": "1.0.0"}, } mock_get_session.return_value = mock_session with ( patch("mcp_template.cli.list_tools") as mock_cli_list_tools, patch("mcp_template.cli.interactive_cli.show_config"), ): # Work with demo template mock_session.get_selected_template.return_value = "demo" select_template("demo") configure_template(template=None, config_pairs=["api_key=demo_key"]) list_tools(template=None) # Switch to github template mock_session.get_selected_template.return_value = "github" select_template("github") configure_template(template=None, config_pairs=["github_token=github_key"]) list_tools(template=None) # Verify both templates were configured assert mock_session.update_template_config.call_count == 2 mock_session.update_template_config.assert_any_call( "demo", {"api_key": "demo_key"} ) mock_session.update_template_config.assert_any_call( "github", {"github_token": "github_key"} ) # Verify tools were listed for both templates assert mock_cli_list_tools.call_count == 2 @pytest.mark.integration class TestPerformanceAndLimits: """Integration tests for performance and edge cases.""" @patch("mcp_template.cli.interactive_cli.CacheManager") def test_large_configuration_handling(self, mock_cache_manager): """Test handling of large configuration sets.""" mock_cache = Mock() mock_cache_manager.return_value = mock_cache mock_cache.get.return_value = {} session = InteractiveSession() # Set many configuration values config_dict = {} for i in range(100): config_dict[f"key_{i}"] = f"value_{i}" session.update_template_config("demo", config_dict) # Verify all configs are accessible all_config = session.get_template_config("demo") assert len(all_config) == 100 for i in range(100): assert all_config[f"key_{i}"] == f"value_{i}" def test_deeply_nested_json_args(self): """Test handling of complex JSON arguments.""" complex_json = { "user": { "name": "John Doe", "settings": { "theme": "dark", "notifications": { "email": True, "push": False, "categories": ["updates", "alerts"], }, }, }, "data": [1, 2, 3, {"nested": True}], } json_str = json.dumps(complex_json) # Test that JSON can be parsed correctly parsed = json.loads(json_str) assert parsed == complex_json @pytest.mark.integration class TestCommandlineArgumentParsing: """Test complex command line argument parsing in main interactive loop.""" @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") @patch("mcp_template.cli.interactive_cli.get_session") def test_logs_command_backend_flag_parsing( self, mock_get_session, mock_console, mock_input ): """Test logs command with --backend flag parsing.""" mock_session = Mock() mock_session.get_selected_template.return_value = "test-template" mock_get_session.return_value = mock_session mock_input.side_effect = ["logs target --backend docker", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_get_logs.assert_called_with( target="target", backend="docker", lines=100 ) @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") def test_logs_command_lines_flag_parsing(self, mock_console, mock_input): """Test logs command with --lines flag parsing.""" mock_input.side_effect = ["logs target --lines 50", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_get_logs.assert_called_with(target="target", backend=None, lines=50) @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") def test_logs_command_lines_flag_invalid_number(self, mock_console, mock_input): """Test logs command with invalid --lines number.""" mock_input.side_effect = ["logs target --lines invalid", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[red]❌ --lines requires a valid number[/red]" ) @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") def test_logs_command_backend_flag_missing_value(self, mock_console, mock_input): """Test logs command with --backend flag missing value.""" mock_input.side_effect = ["logs target --backend", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[red]❌ --backend requires a backend name[/red]" ) @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") def test_logs_command_unknown_flag_warning(self, mock_console, mock_input): """Test logs command with unknown flag shows warning.""" mock_input.side_effect = ["logs target --unknown-flag", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[yellow]⚠️ Ignoring unknown flag: --unknown-flag[/yellow]" ) @patch("mcp_template.cli.interactive_cli.input") @patch("mcp_template.cli.interactive_cli.console") @patch("mcp_template.cli.interactive_cli.get_session") def test_logs_command_no_target_no_selected_template( self, mock_get_session, mock_console, mock_input ): """Test logs command with no target and no selected template.""" mock_session = Mock() mock_session.get_selected_template.return_value = None mock_get_session.return_value = mock_session mock_input.side_effect = ["logs", "exit"] with patch("mcp_template.cli.interactive_cli.get_logs") as mock_get_logs: from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[red]❌ Target required: deployment ID or template name[/red]" ) @pytest.mark.integration class TestInteractiveLoopMainFlow: """Test main interactive loop functionality.""" @patch("mcp_template.cli.interactive_cli.READLINE_AVAILABLE", True) @patch("mcp_template.cli.interactive_cli.setup_completion") @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_run_interactive_shell_with_readline( self, mock_console, mock_get_session, mock_setup ): """Test interactive shell with readline available.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session mock_setup.return_value = "/tmp/history" # Mock input to return exit immediately with patch("builtins.input", side_effect=["exit"]): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_setup.assert_called_once() mock_console.print.assert_any_call( "[dim]✨ Command history and tab completion enabled[/dim]" ) @patch("mcp_template.cli.interactive_cli.READLINE_AVAILABLE", False) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_run_interactive_shell_no_readline(self, mock_console, mock_get_session): """Test interactive shell without readline.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session # Mock input to return exit immediately with patch("builtins.input", side_effect=["exit"]): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[dim]💡 Install readline for command history and tab completion[/dim]" ) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_interactive_shell_keyboard_interrupt(self, mock_console, mock_get_session): """Test keyboard interrupt handling in interactive shell.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session # Mock input to raise KeyboardInterrupt then exit with patch("builtins.input", side_effect=[KeyboardInterrupt(), "exit"]): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "\n[yellow]Use 'exit' or 'quit' to leave the interactive shell[/yellow]" ) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_interactive_shell_eof_error(self, mock_console, mock_get_session): """Test EOF error handling in interactive shell.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session # Mock input to raise EOFError with patch("builtins.input", side_effect=EOFError()): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call("\n[yellow]Goodbye![/yellow]") @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_interactive_shell_unknown_command(self, mock_console, mock_get_session): """Test unknown command handling.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session with patch("builtins.input", side_effect=["unknown_command", "exit"]): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() mock_console.print.assert_any_call( "[red]❌ Unknown command: unknown_command[/red]" ) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.interactive_cli.console") def test_interactive_shell_empty_command(self, mock_console, mock_get_session): """Test empty command handling.""" mock_session = Mock() mock_session.get_prompt.return_value = "mcpt> " mock_get_session.return_value = mock_session with patch("builtins.input", side_effect=["", " ", "exit"]): from mcp_template.cli.interactive_cli import run_interactive_shell run_interactive_shell() # Should not print any error messages for empty commands assert not any( "❌" in str(call) for call in mock_console.print.call_args_list if "command" in str(call) ) @pytest.mark.integration class TestMainFunctionIntegration: """Test main function behavior.""" @patch("sys.argv", ["script", "--help"]) @patch("mcp_template.cli.interactive_cli.console") def test_main_with_help_flag(self, mock_console): """Test main function with --help flag.""" from mcp_template.cli import interactive_cli interactive_cli.main() mock_console.print.assert_any_call("Enhanced MCP Interactive CLI") @patch("sys.argv", ["script", "-h"]) @patch("mcp_template.cli.interactive_cli.console") def test_main_with_h_flag(self, mock_console): """Test main function with -h flag.""" from mcp_template.cli import interactive_cli interactive_cli.main() mock_console.print.assert_any_call("Enhanced MCP Interactive CLI") @patch("sys.argv", ["script"]) @patch("mcp_template.cli.interactive_cli.run_interactive_shell") def test_main_without_args(self, mock_run_shell): """Test main function without arguments.""" from mcp_template.cli import interactive_cli interactive_cli.main() mock_run_shell.assert_called_once() class TestStopCommandEnhanced: """Enhanced tests for stop command functionality in Interactive CLI.""" @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.stop") def test_stop_server_no_target_with_session_template( self, mock_cli_stop, mock_get_session ): """Test stop command with no target uses session template.""" from mcp_template.cli.interactive_cli import stop_server # Mock session with selected template mock_session = Mock() mock_session.get_selected_template.return_value = "demo" mock_get_session.return_value = mock_session with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server() # Should call CLI stop with session template as target mock_cli_stop.assert_called_once_with( target="demo", backend=None, all=False, template=None, dry_run=False, timeout=30, force=False, ) @patch("mcp_template.cli.stop") def test_stop_server_dry_run_mode(self, mock_cli_stop): """Test stop command in dry-run mode.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(all=True, dry_run=True) # Should call CLI stop with dry_run enabled mock_cli_stop.assert_called_once_with( target=None, backend=None, all=True, template=None, dry_run=True, timeout=30, force=False, ) @patch("mcp_template.cli.stop") def test_stop_all_servers_with_force(self, mock_cli_stop): """Test stopping all servers with force option.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(all=True, force=True, timeout=60) # Should call CLI stop with all and force options mock_cli_stop.assert_called_once_with( target=None, backend=None, all=True, template=None, dry_run=False, timeout=60, force=True, ) @patch("mcp_template.cli.stop") def test_stop_specific_template_servers(self, mock_cli_stop): """Test stopping all servers for a specific template.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(template="demo", force=True) # Should call CLI stop with template option mock_cli_stop.assert_called_once_with( target=None, backend=None, all=False, template="demo", dry_run=False, timeout=30, force=True, ) @patch("mcp_template.cli.stop") def test_stop_specific_deployment_id(self, mock_cli_stop): """Test stopping a specific deployment by ID.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(target="deployment-123", force=True) # Should call CLI stop with specific target mock_cli_stop.assert_called_once_with( target="deployment-123", backend=None, all=False, template=None, dry_run=False, timeout=30, force=True, ) @patch("mcp_template.cli.stop") def test_stop_with_backend_specification(self, mock_cli_stop): """Test stopping servers with specific backend.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(all=True, backend="docker", force=True) # Should call CLI stop with backend specification mock_cli_stop.assert_called_once_with( target=None, backend="docker", all=True, template=None, dry_run=False, timeout=30, force=True, ) @patch("mcp_template.cli.stop") def test_stop_no_running_servers_delegation(self, mock_cli_stop): """Test stop command delegation when no target specified.""" from mcp_template.cli.interactive_cli import stop_server # Mock CLI stop to raise an exception to test error handling mock_cli_stop.side_effect = Exception("No servers found") with patch("mcp_template.cli.interactive_cli.console") as mock_console: with patch("mcp_template.cli.interactive_cli.get_session") as mock_session: mock_session.return_value.get_selected_template.return_value = None stop_server() # Should display error message without calling CLI stop mock_cli_stop.assert_not_called() mock_console.print.assert_called() @patch("mcp_template.cli.stop") def test_stop_server_error_handling(self, mock_cli_stop): """Test stop command handling CLI function errors.""" from mcp_template.cli.interactive_cli import stop_server # Mock CLI stop to raise an exception mock_cli_stop.side_effect = Exception("Stop operation failed") with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(target="server-1", force=True) # Should attempt to call CLI stop mock_cli_stop.assert_called_once() # Should display error message mock_console.print.assert_called() call_args = mock_console.print.call_args[0][0] assert "Error stopping server" in call_args @patch("mcp_template.cli.stop") def test_stop_with_custom_timeout(self, mock_cli_stop): """Test stop command with custom timeout.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(target="server-1", timeout=120, force=True) # Should call CLI stop with custom timeout mock_cli_stop.assert_called_once_with( target="server-1", backend=None, all=False, template=None, dry_run=False, timeout=120, force=True, ) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.stop") def test_stop_session_template_fallback(self, mock_cli_stop, mock_get_session): """Test stop command falling back to session template when no args.""" from mcp_template.cli.interactive_cli import stop_server # Mock session with selected template mock_session = Mock() mock_session.get_selected_template.return_value = "github" mock_get_session.return_value = mock_session with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server() # Should use session template as target mock_cli_stop.assert_called_once_with( target="github", backend=None, all=False, template=None, dry_run=False, timeout=30, force=False, ) @patch("mcp_template.cli.stop") def test_stop_template_with_dry_run(self, mock_cli_stop): """Test stopping template servers in dry-run mode.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server(template="demo", dry_run=True) # Should call CLI stop with both template and dry_run mock_cli_stop.assert_called_once_with( target=None, backend=None, all=False, template="demo", dry_run=True, timeout=30, force=False, ) @patch("mcp_template.cli.stop") def test_stop_with_all_parameters(self, mock_cli_stop): """Test stop command with all parameters specified.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server( target="test-deployment", backend="kubernetes", all=False, template=None, dry_run=True, timeout=90, force=True, ) # Should call CLI stop with all parameters mock_cli_stop.assert_called_once_with( target="test-deployment", backend="kubernetes", all=False, template=None, dry_run=True, timeout=90, force=True, ) @patch("mcp_template.cli.stop") def test_stop_function_parameter_passing(self, mock_cli_stop): """Test that all parameters are correctly passed to CLI function.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: # Test with complex parameter combination stop_server( target="complex-test", backend="podman", all=True, # This combination might be unusual but should pass through template="filesystem", dry_run=False, timeout=45, force=False, ) # Verify exact parameter mapping mock_cli_stop.assert_called_once_with( target="complex-test", backend="podman", all=True, template="filesystem", dry_run=False, timeout=45, force=False, ) @patch("mcp_template.cli.interactive_cli.get_session") @patch("mcp_template.cli.stop") def test_stop_no_session_template_error(self, mock_cli_stop, mock_get_session): """Test stop command when no target and no session template.""" from mcp_template.cli.interactive_cli import stop_server # Mock session with no selected template mock_session = Mock() mock_session.get_selected_template.return_value = None mock_get_session.return_value = mock_session with patch("mcp_template.cli.interactive_cli.console") as mock_console: stop_server() # Should not call CLI stop mock_cli_stop.assert_not_called() # Should display error message mock_console.print.assert_called() call_args = mock_console.print.call_args[0][0] assert "Target required" in call_args @patch("mcp_template.cli.stop") def test_stop_delegation_preserves_defaults(self, mock_cli_stop): """Test that default parameter values are preserved in delegation.""" from mcp_template.cli.interactive_cli import stop_server with patch("mcp_template.cli.interactive_cli.console") as mock_console: # Call with minimal parameters stop_server(target="minimal-test") # Should call CLI stop with default values mock_cli_stop.assert_called_once_with( target="minimal-test", backend=None, # Default all=False, # Default template=None, # Default dry_run=False, # Default timeout=30, # Default force=False, # Default )

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Data-Everything/mcp-server-templates'

If you have feedback or need assistance with the MCP directory API, please join our Discord server