Skip to main content
Glama
test_system.py11.1 kB
""" Tests for OPNsense MCP Server system domain. This module tests the system management tools including status monitoring, health metrics, service management, and security auditing. """ import pytest import json import sys from unittest.mock import AsyncMock, Mock, patch, MagicMock from mcp.server.fastmcp import FastMCP # Mock the circular import with proper FastMCP instance mock_mcp = FastMCP("test-server") mock_server_state = MagicMock() mock_main = MagicMock() mock_main.mcp = mock_mcp mock_main.server_state = mock_server_state sys.modules['src.opnsense_mcp.main'] = mock_main from src.opnsense_mcp.domains.system import ( get_system_status, get_system_health, restart_service, backup_config, _get_all_rules, _get_wan_interfaces ) from src.opnsense_mcp.core.exceptions import ( ConfigurationError, AuthenticationError, APIError ) @pytest.mark.asyncio class TestGetSystemStatus: """Test get_system_status tool.""" async def test_successful_status_retrieval(self, mock_mcp_context, mock_firmware_status_response): """Test successful system status retrieval.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(side_effect=[ mock_firmware_status_response, # Firmware {"hostname": "opnsense.local"}, # System info {"rows": [{"name": "sshd", "status": "running"}]} # Services ]) mock_get_client.return_value = mock_client result = await get_system_status(ctx=mock_mcp_context) # Should return JSON with firmware, system, and services result_data = json.loads(result) assert "firmware" in result_data assert "system" in result_data assert "services" in result_data assert mock_client.request.call_count == 3 async def test_configuration_error_handling(self, mock_mcp_context): """Test handling of configuration errors.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_get_client.side_effect = ConfigurationError("Not configured") result = await get_system_status(ctx=mock_mcp_context) assert "Configuration Error" in result mock_mcp_context.error.assert_called_once() async def test_api_error_handling(self, mock_mcp_context): """Test handling of API errors.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(side_effect=APIError("API error")) mock_get_client.return_value = mock_client result = await get_system_status(ctx=mock_mcp_context) assert "Error" in result @pytest.mark.asyncio class TestGetSystemHealth: """Test get_system_health tool.""" async def test_successful_health_retrieval(self, mock_mcp_context): """Test successful system health metrics retrieval.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(side_effect=[ {"usage": "25.5"}, # CPU {"physical": "16384", "used": "8192"}, # Memory {"/": {"used": "50%"}}, # Storage {"cpu": "45.5"} # Temperature ]) mock_get_client.return_value = mock_client result = await get_system_health(ctx=mock_mcp_context) # Should return JSON with health metrics result_data = json.loads(result) assert "cpu" in result_data assert "memory" in result_data assert "disk" in result_data async def test_partial_health_data(self, mock_mcp_context): """Test health retrieval with some API endpoints failing.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() # When any request fails, the whole function returns an error string mock_client.request = AsyncMock(side_effect=Exception("Memory API failed")) mock_get_client.return_value = mock_client result = await get_system_health(ctx=mock_mcp_context) # Should return error string assert "error" in result.lower() @pytest.mark.asyncio class TestRestartService: """Test restart_service tool.""" async def test_successful_service_restart(self, mock_mcp_context): """Test successful service restart.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(return_value={"status": "ok"}) mock_get_client.return_value = mock_client result = await restart_service(ctx=mock_mcp_context, service_name="sshd") assert "successfully" in result.lower() or "ok" in result.lower() # Verify correct endpoint was called call_args = mock_client.request.call_args[0] assert "service" in call_args[1] assert "restart" in call_args[1] async def test_service_restart_error(self, mock_mcp_context): """Test service restart error handling.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(side_effect=APIError("Service not found")) mock_get_client.return_value = mock_client result = await restart_service(ctx=mock_mcp_context, service_name="nonexistent") assert "Error" in result @pytest.mark.asyncio class TestBackupConfig: """Test backup_config tool.""" async def test_successful_backup(self, mock_mcp_context): """Test successful configuration backup.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(return_value="<?xml version='1.0'?><config></config>") mock_get_client.return_value = mock_client result = await backup_config(ctx=mock_mcp_context) # Should return XML configuration or success message assert "xml" in result.lower() or "backup" in result.lower() async def test_backup_error_handling(self, mock_mcp_context): """Test backup error handling.""" with patch('src.opnsense_mcp.domains.system.get_opnsense_client', new_callable=AsyncMock) as mock_get_client: mock_client = Mock() mock_client.request = AsyncMock(side_effect=APIError("Backup failed")) mock_get_client.return_value = mock_client result = await backup_config(ctx=mock_mcp_context) assert "Error" in result @pytest.mark.asyncio class TestHelperFunctions: """Test helper functions.""" async def test_get_all_rules_single_page(self): """Test fetching firewall rules when all fit in one page.""" mock_client = Mock() mock_rules = [ {"uuid": "rule1", "description": "Rule 1"}, {"uuid": "rule2", "description": "Rule 2"} ] mock_client.request = AsyncMock(return_value={"rows": mock_rules}) result = await _get_all_rules(mock_client) assert len(result) == 2 assert result[0]["uuid"] == "rule1" mock_client.request.assert_called_once() async def test_get_all_rules_multiple_pages(self): """Test fetching firewall rules with pagination.""" mock_client = Mock() # First call returns 500 rules (full page), second returns 200 (partial page, triggers break) mock_client.request = AsyncMock(side_effect=[ {"rows": [{"uuid": f"rule{i}"} for i in range(500)]}, {"rows": [{"uuid": f"rule{i}"} for i in range(500, 700)]} ]) result = await _get_all_rules(mock_client) assert len(result) == 700 # Function stops when it receives < 500 rules (line 75-76 of system.py) assert mock_client.request.call_count == 2 async def test_get_all_rules_handles_error(self): """Test that _get_all_rules handles errors gracefully.""" mock_client = Mock() mock_client.request = AsyncMock(side_effect=[ {"rows": [{"uuid": "rule1"}]}, Exception("API error on page 2") ]) result = await _get_all_rules(mock_client) # Should return partial results assert len(result) == 1 assert result[0]["uuid"] == "rule1" async def test_get_wan_interfaces_with_gateway(self): """Test identifying WAN interfaces by gateway presence.""" mock_client = Mock() mock_interfaces = { "wan": {"gateway": "192.168.1.1", "status": "up"}, "lan": {"gateway": "none", "status": "up"}, "opt1": {"gateway": "10.0.0.1", "status": "up"} } mock_client.request = AsyncMock(return_value=mock_interfaces) result = await _get_wan_interfaces(mock_client) # Should identify wan and opt1 as WAN interfaces (they have gateways) assert "wan" in result assert "opt1" in result assert "lan" not in result async def test_get_wan_interfaces_fallback_to_name(self): """Test WAN identification fallback to interface name.""" mock_client = Mock() mock_interfaces = { "wan": {"gateway": "none", "status": "up"}, # No gateway but named 'wan' "lan": {"gateway": "none", "status": "up"} } mock_client.request = AsyncMock(return_value=mock_interfaces) result = await _get_wan_interfaces(mock_client) # Should still identify 'wan' by name assert "wan" in result async def test_get_wan_interfaces_handles_error(self): """Test that _get_wan_interfaces handles errors gracefully.""" mock_client = Mock() mock_client.request = AsyncMock(side_effect=Exception("API error")) result = await _get_wan_interfaces(mock_client) # Should return empty list on error assert result == [] async def test_get_wan_interfaces_none_identified(self): """Test when no WAN interfaces can be identified.""" mock_client = Mock() mock_interfaces = { "lan": {"gateway": "none", "status": "up"}, "opt1": {"gateway": "none", "status": "up"} } mock_client.request = AsyncMock(return_value=mock_interfaces) result = await _get_wan_interfaces(mock_client) # Should return empty list assert result == []

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/floriangrousset/opnsense-mcp-server'

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