Skip to main content
Glama
test_vm_operations.py13 kB
"""Tests for VM operations module.""" import asyncio import json from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.homelab_mcp.vm_operations import ( VMManager, control_vm_state, deploy_vm, get_vm_logs, get_vm_status, list_vms_on_device, remove_vm, ) class TestVMManager: """Test VMManager class.""" def setup_method(self): """Set up test fixtures.""" self.manager = VMManager() @patch("src.homelab_mcp.vm_operations.NetworkSiteMap") def test_get_device_connection_info_found(self, mock_sitemap_class): """Test getting connection info for existing device.""" mock_sitemap = MagicMock() mock_sitemap.get_all_devices.return_value = [ {"id": 1, "hostname": "pi-server", "connection_ip": "192.168.1.100"}, {"id": 2, "hostname": "mini-pc", "connection_ip": "192.168.1.101"}, ] mock_sitemap_class.return_value = mock_sitemap self.manager.sitemap = mock_sitemap result = asyncio.run(self.manager.get_device_connection_info(1)) assert result is not None assert result["hostname"] == "192.168.1.100" assert result["username"] == "mcp_admin" assert result["port"] == 22 @patch("src.homelab_mcp.vm_operations.NetworkSiteMap") def test_get_device_connection_info_not_found(self, mock_sitemap_class): """Test getting connection info for non-existent device.""" mock_sitemap = MagicMock() mock_sitemap.get_all_devices.return_value = [{"id": 1, "hostname": "pi-server"}] mock_sitemap_class.return_value = mock_sitemap self.manager.sitemap = mock_sitemap result = asyncio.run(self.manager.get_device_connection_info(999)) assert result is None @pytest.mark.asyncio class TestVMOperations: """Test VM operation functions.""" @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_deploy_vm_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM deployment.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.deploy_vm = AsyncMock( return_value={ "status": "success", "vm_name": "test-nginx", "container_id": "abc123", } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test deployment vm_config = {"image": "nginx:latest", "ports": ["80:80"]} result_json = await deploy_vm(1, "docker", "test-nginx", vm_config) result = json.loads(result_json) assert result["status"] == "success" assert result["vm_name"] == "test-nginx" assert result["device_id"] == 1 assert result["platform"] == "docker" # Verify calls mock_provider.deploy_vm.assert_called_once_with( mock_conn, "test-nginx", vm_config ) @patch("src.homelab_mcp.vm_operations.VMManager") async def test_deploy_vm_device_not_found(self, mock_manager_class): """Test VM deployment when device not found.""" mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock(return_value=None) mock_manager_class.return_value = mock_manager result_json = await deploy_vm(999, "docker", "test", {}) result = json.loads(result_json) assert result["status"] == "error" assert "not found in sitemap" in result["message"] @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_control_vm_state_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM state control.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.control_vm = AsyncMock( return_value={ "status": "success", "operation": "start", "vm_name": "test-container", } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test start action result_json = await control_vm_state(1, "docker", "test-container", "start") result = json.loads(result_json) assert result["status"] == "success" assert result["operation"] == "start" assert result["vm_name"] == "test-container" assert result["device_id"] == 1 assert result["platform"] == "docker" mock_provider.control_vm.assert_called_once_with( mock_conn, "test-container", "start" ) @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_get_vm_status_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM status retrieval.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.get_vm_status = AsyncMock( return_value={ "status": "success", "vm_name": "test-container", "container_status": "running", "pid": 1234, } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test status retrieval result_json = await get_vm_status(1, "docker", "test-container") result = json.loads(result_json) assert result["status"] == "success" assert result["vm_name"] == "test-container" assert result["container_status"] == "running" assert result["device_id"] == 1 assert result["platform"] == "docker" @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_list_vms_on_device_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM listing on device.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.list_vms = AsyncMock( return_value={ "status": "success", "containers": [ {"name": "nginx", "status": "running"}, {"name": "redis", "status": "stopped"}, ], } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test VM listing result_json = await list_vms_on_device(1, ["docker"]) result = json.loads(result_json) assert result["status"] == "success" assert result["device_id"] == 1 assert result["total_vms"] == 2 assert len(result["vms"]) == 2 assert result["vms"][0]["name"] == "nginx" assert result["vms"][0]["platform"] == "docker" @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_get_vm_logs_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM log retrieval.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.get_vm_logs = AsyncMock( return_value={ "status": "success", "vm_name": "test-container", "logs": "Starting nginx\\nReady to accept connections", "lines_requested": 50, } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test log retrieval result_json = await get_vm_logs(1, "docker", "test-container", 50) result = json.loads(result_json) assert result["status"] == "success" assert result["vm_name"] == "test-container" assert result["lines_requested"] == 50 assert "Starting nginx" in result["logs"] assert result["device_id"] == 1 assert result["platform"] == "docker" @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") @patch("src.homelab_mcp.vm_operations.asyncssh.connect") async def test_remove_vm_success( self, mock_connect, mock_get_provider, mock_manager_class ): """Test successful VM removal.""" # Setup mocks mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager mock_provider = MagicMock() mock_provider.remove_vm = AsyncMock( return_value={ "status": "success", "operation": "remove", "vm_name": "test-container", "forced": False, } ) mock_get_provider.return_value = mock_provider mock_conn = AsyncMock() mock_connect.return_value.__aenter__.return_value = mock_conn # Test VM removal result_json = await remove_vm(1, "docker", "test-container", force=False) result = json.loads(result_json) assert result["status"] == "success" assert result["operation"] == "remove" assert result["vm_name"] == "test-container" assert not result["forced"] assert result["device_id"] == 1 assert result["platform"] == "docker" mock_provider.remove_vm.assert_called_once_with( mock_conn, "test-container", False ) @patch("src.homelab_mcp.vm_operations.VMManager") @patch("src.homelab_mcp.vm_operations.get_vm_provider") async def test_unsupported_platform(self, mock_get_provider, mock_manager_class): """Test handling of unsupported platform.""" # Mock device lookup to succeed mock_manager = MagicMock() mock_manager.get_device_connection_info = AsyncMock( return_value={ "hostname": "192.168.1.100", "username": "mcp_admin", "port": 22, } ) mock_manager_class.return_value = mock_manager # Mock platform provider to fail with unsupported platform mock_get_provider.side_effect = ValueError("Unsupported platform: unsupported") result_json = await deploy_vm(1, "unsupported", "test", {}) result = json.loads(result_json) assert result["status"] == "error" assert "Unsupported platform" in result["message"]

Latest Blog Posts

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/washyu/mcp_python_server'

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