Refine VM/cluster/storage tool handlers and auth utilities; add conftest and extend server/console tests for more reliable operator automation. Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
3.7 KiB
Python
123 lines
3.7 KiB
Python
"""
|
|
Tests for VM console operations.
|
|
"""
|
|
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from proxmox_mcp.tools.console.manager import VMConsoleManager
|
|
|
|
|
|
def _agent_mock(*, exec_result: dict, status_result: dict) -> Mock:
|
|
exec_endpoint = Mock()
|
|
exec_endpoint.post.return_value = exec_result
|
|
status_endpoint = Mock()
|
|
status_endpoint.get.return_value = status_result
|
|
|
|
def _route(command: str) -> Mock:
|
|
if command == "exec":
|
|
return exec_endpoint
|
|
if command == "exec-status":
|
|
return status_endpoint
|
|
raise AssertionError(f"unexpected agent command: {command}")
|
|
|
|
return Mock(side_effect=_route)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_proxmox():
|
|
"""Fixture to create a mock ProxmoxAPI instance."""
|
|
mock = Mock()
|
|
mock.nodes.return_value.qemu.return_value.status.current.get.return_value = {
|
|
"status": "running"
|
|
}
|
|
mock.nodes.return_value.qemu.return_value.agent = _agent_mock(
|
|
exec_result={"pid": 123},
|
|
status_result={
|
|
"out-data": "command output",
|
|
"err-data": "",
|
|
"exitcode": 0,
|
|
"exited": 1,
|
|
},
|
|
)
|
|
return mock
|
|
|
|
|
|
@pytest.fixture
|
|
def vm_console(mock_proxmox):
|
|
"""Fixture to create a VMConsoleManager instance."""
|
|
return VMConsoleManager(mock_proxmox)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_command_success(vm_console, mock_proxmox):
|
|
"""Test successful command execution."""
|
|
result = await vm_console.execute_command("node1", "100", "ls -l")
|
|
|
|
assert result["success"] is True
|
|
assert result["output"] == "command output"
|
|
assert result["error"] == ""
|
|
assert result["exit_code"] == 0
|
|
|
|
mock_proxmox.nodes.assert_called_with("node1")
|
|
mock_proxmox.nodes.return_value.qemu.assert_called_with("100")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_command_vm_not_running(vm_console, mock_proxmox):
|
|
"""Test command execution on stopped VM."""
|
|
mock_proxmox.nodes.return_value.qemu.return_value.status.current.get.return_value = {
|
|
"status": "stopped"
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="not running"):
|
|
await vm_console.execute_command("node1", "100", "ls -l")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_command_vm_not_found(vm_console, mock_proxmox):
|
|
"""Test command execution on non-existent VM."""
|
|
mock_proxmox.nodes.return_value.qemu.return_value.status.current.get.side_effect = Exception(
|
|
"VM not found"
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="not found"):
|
|
await vm_console.execute_command("node1", "100", "ls -l")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_command_failure(vm_console, mock_proxmox):
|
|
"""Test command execution failure."""
|
|
mock_proxmox.nodes.return_value.qemu.return_value.agent = _agent_mock(
|
|
exec_result={"pid": 123},
|
|
status_result={},
|
|
)
|
|
mock_proxmox.nodes.return_value.qemu.return_value.agent.side_effect = Exception(
|
|
"Command failed"
|
|
)
|
|
|
|
with pytest.raises(RuntimeError, match="Failed to execute command"):
|
|
await vm_console.execute_command("node1", "100", "ls -l")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_command_with_error_output(vm_console, mock_proxmox):
|
|
"""Test command execution with error output."""
|
|
mock_proxmox.nodes.return_value.qemu.return_value.agent = _agent_mock(
|
|
exec_result={"pid": 123},
|
|
status_result={
|
|
"out-data": "",
|
|
"err-data": "command error",
|
|
"exitcode": 1,
|
|
"exited": 1,
|
|
},
|
|
)
|
|
|
|
result = await vm_console.execute_command("node1", "100", "invalid-command")
|
|
|
|
assert result["success"] is True
|
|
assert result["output"] == ""
|
|
assert result["error"] == "command error"
|
|
assert result["exit_code"] == 1
|