Skip to main content
Glama
sandraschi

Observability MCP Server

test_server.py14.6 kB
""" Tests for the Observability MCP Server. Tests FastMCP 2.14.1 integration, OpenTelemetry functionality, persistent storage, and all monitoring tools. """ import asyncio import pytest from unittest.mock import AsyncMock, MagicMock, patch from datetime import datetime, timedelta from observability_mcp.server import ( mcp, HealthCheckResult, PerformanceMetrics, TraceInfo, AlertConfig, AnomalyResult, monitor_server_health, collect_performance_metrics, trace_mcp_calls, generate_performance_reports, alert_on_anomalies, monitor_system_resources, analyze_mcp_interactions, export_metrics, ) class TestHealthMonitoring: """Test health monitoring functionality.""" @pytest.mark.asyncio async def test_monitor_server_health_success(self): """Test successful health check.""" with patch('observability_mcp.server.aiohttp.ClientSession') as mock_session: # Mock successful response mock_response = AsyncMock() mock_response.status = 200 mock_response.read.return_value = b'OK' mock_response.headers = {'content-type': 'text/plain'} mock_session.return_value.__aenter__.return_value.get.return_value.__aenter__.return_value = mock_response mock_session.return_value.__aenter__.return_value.get.return_value.__aexit__.return_value = None mock_session.return_value.__aexit__.return_value = None # Mock context ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() result = await monitor_server_health( ctx=ctx, service_url="http://example.com/health" ) assert result['health_check']['status'] == 'healthy' assert result['health_check']['response_time_ms'] > 0 assert 'recommendations' in result @pytest.mark.asyncio async def test_monitor_server_health_failure(self): """Test failed health check.""" with patch('observability_mcp.server.aiohttp.ClientSession') as mock_session: # Mock failed response mock_session.return_value.__aenter__.return_value.get.side_effect = Exception("Connection failed") ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() result = await monitor_server_health( ctx=ctx, service_url="http://example.com/health" ) assert result['health_check']['status'] == 'unhealthy' assert 'error_message' in result['health_check'] class TestPerformanceMonitoring: """Test performance monitoring functionality.""" @pytest.mark.asyncio async def test_collect_performance_metrics(self): """Test performance metrics collection.""" with patch('observability_mcp.server.psutil') as mock_psutil: # Mock psutil calls mock_psutil.cpu_percent.return_value = 45.5 mock_psutil.virtual_memory.return_value = MagicMock() mock_psutil.virtual_memory.return_value.used = 1024 * 1024 * 1024 # 1GB mock_psutil.disk_usage.return_value = MagicMock() mock_psutil.disk_usage.return_value.percent = 75.0 mock_psutil.net_io_counters.return_value = MagicMock( bytes_sent=1000, bytes_recv=2000, packets_sent=10, packets_recv=20 ) ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() result = await collect_performance_metrics(ctx=ctx, service_name="test-service") assert result['metrics']['cpu_percent'] == 45.5 assert result['metrics']['memory_mb'] == 1024.0 # 1GB in MB assert result['metrics']['disk_usage_percent'] == 75.0 assert 'trends' in result assert 'recommendations' in result class TestTracing: """Test tracing functionality.""" @pytest.mark.asyncio async def test_trace_mcp_calls(self): """Test MCP call tracing.""" ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() result = await trace_mcp_calls( ctx=ctx, operation_name="test_operation", service_name="test-service", duration_ms=150.5, attributes={"param": "value"} ) assert result['trace']['operation'] == 'test_operation' assert result['trace']['service_name'] == 'test-service' assert result['trace']['duration_ms'] == 150.5 assert result['trace']['attributes']['param'] == 'value' assert 'patterns' in result assert 'performance_insights' in result class TestReporting: """Test reporting functionality.""" @pytest.mark.asyncio async def test_generate_performance_reports(self): """Test performance report generation.""" # Mock historical data mock_history = [ { "timestamp": (datetime.now() - timedelta(days=i)).isoformat(), "cpu_percent": 50.0 + i, "memory_mb": 1000.0 + i * 10 } for i in range(7) # 7 days of data ] ctx = MagicMock() ctx.storage.get.return_value = mock_history ctx.storage.set = AsyncMock() result = await generate_performance_reports( ctx=ctx, service_name="test-service", days=7 ) assert 'summary' in result assert 'trends' in result assert 'anomalies' in result assert 'recommendations' in result assert result['summary']['total_measurements'] == 7 class TestAlerting: """Test alerting functionality.""" @pytest.mark.asyncio async def test_alert_on_anomalies(self): """Test anomaly detection and alerting.""" # Mock alert configurations mock_configs = [ AlertConfig(metric_name="cpu_percent", threshold=80.0, operator="gt", severity="warning").dict() ] ctx = MagicMock() ctx.storage.get.side_effect = lambda key: { "alert_configs": mock_configs, "performance_history:test-service": [ {"timestamp": datetime.now().isoformat(), "cpu_percent": 85.0} # Above threshold ] }[key] result = await alert_on_anomalies(ctx=ctx, service_name="test-service") assert 'active_alerts' in result assert 'detected_anomalies' in result assert 'alert_configs' in result assert 'recommendations' in result class TestSystemMonitoring: """Test system monitoring functionality.""" @pytest.mark.asyncio async def test_monitor_system_resources(self): """Test system resource monitoring.""" with patch('observability_mcp.server.psutil') as mock_psutil: # Mock comprehensive system data mock_cpu_times = MagicMock() mock_cpu_times.user = 100.0 mock_cpu_times.system = 50.0 mock_cpu_times.idle = 200.0 mock_memory = MagicMock() mock_memory.total = 8 * 1024**3 # 8GB mock_memory.available = 4 * 1024**3 # 4GB mock_memory.used = 4 * 1024**3 # 4GB mock_memory.percent = 50.0 mock_swap = MagicMock() mock_swap.total = 2 * 1024**3 # 2GB mock_swap.used = 0.5 * 1024**3 # 0.5GB mock_swap.percent = 25.0 mock_disk = MagicMock() mock_disk.total = 500 * 1024**3 # 500GB mock_disk.used = 250 * 1024**3 # 250GB mock_disk.free = 250 * 1024**3 # 250GB mock_disk.percent = 50.0 mock_network = MagicMock() mock_network.bytes_sent = 1000000 mock_network.bytes_recv = 2000000 mock_network.packets_sent = 10000 mock_network.packets_recv = 20000 mock_psutil.cpu_times.return_value = mock_cpu_times mock_psutil.cpu_percent.return_value = 35.5 mock_psutil.cpu_count.return_value = 8 mock_psutil.cpu_count.return_value = 16 mock_psutil.virtual_memory.return_value = mock_memory mock_psutil.swap_memory.return_value = mock_swap mock_psutil.disk_usage.return_value = mock_disk mock_psutil.net_io_counters.return_value = mock_network # Mock process iterator mock_process = MagicMock() mock_process.pid = 1234 mock_process.name.return_value = "python" mock_process.cpu_percent.return_value = 5.0 mock_process.memory_percent.return_value = 2.0 mock_psutil.process_iter.return_value = [mock_process] ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() result = await monitor_system_resources(ctx=ctx) assert 'system_status' in result assert 'health_analysis' in result assert 'recommendations' in result system_status = result['system_status'] assert system_status['cpu']['percent'] == 35.5 assert system_status['memory']['percent'] == 50.0 assert system_status['disk']['percent'] == 50.0 class TestAnalytics: """Test analytics functionality.""" @pytest.mark.asyncio async def test_analyze_mcp_interactions(self): """Test MCP interaction analysis.""" # Mock trace data mock_traces = [ { "timestamp": (datetime.now() - timedelta(hours=i)).isoformat(), "operation": "health_check", "service_name": "test-service", "duration_ms": 100.0 + i * 10, "status": "completed" } for i in range(24) # 24 hours of data ] ctx = MagicMock() ctx.storage.keys.return_value = ["trace_history:test-service"] ctx.storage.get.side_effect = lambda key: { "trace_history:test-service": mock_traces }.get(key, []) result = await analyze_mcp_interactions(ctx=ctx, days=1) assert 'patterns' in result assert 'insights' in result assert 'recommendations' in result assert result['patterns']['total_interactions'] == 24 class TestExport: """Test export functionality.""" @pytest.mark.asyncio async def test_export_metrics_prometheus(self): """Test Prometheus metrics export.""" ctx = MagicMock() result = await export_metrics(ctx=ctx, format="prometheus") assert result['format'] == 'prometheus' assert 'endpoint' in result assert '9090' in result['endpoint'] @pytest.mark.asyncio async def test_export_metrics_json(self): """Test JSON metrics export.""" ctx = MagicMock() ctx.storage.keys.return_value = [] ctx.storage.get.return_value = [] result = await export_metrics(ctx=ctx, format="json", include_history=False) assert result['format'] == 'json' assert 'timestamp' in result assert 'metrics' in result assert 'version' in result class TestDataModels: """Test data models and validation.""" def test_health_check_result_creation(self): """Test HealthCheckResult model creation.""" result = HealthCheckResult( service_name="test-service", status="healthy", response_time_ms=150.0, timestamp=datetime.now() ) assert result.service_name == "test-service" assert result.status == "healthy" assert result.response_time_ms == 150.0 def test_performance_metrics_creation(self): """Test PerformanceMetrics model creation.""" metrics = PerformanceMetrics( service_name="test-service", timestamp=datetime.now(), cpu_percent=45.5, memory_mb=1024.0, disk_usage_percent=75.0, network_io={"bytes_sent": 1000, "bytes_recv": 2000} ) assert metrics.service_name == "test-service" assert metrics.cpu_percent == 45.5 assert metrics.memory_mb == 1024.0 def test_trace_info_creation(self): """Test TraceInfo model creation.""" trace = TraceInfo( trace_id="test-trace-id", service_name="test-service", operation="test-operation", start_time=datetime.now(), duration_ms=150.5, status="completed" ) assert trace.trace_id == "test-trace-id" assert trace.operation == "test-operation" assert trace.duration_ms == 150.5 def test_alert_config_creation(self): """Test AlertConfig model creation.""" config = AlertConfig( metric_name="cpu_percent", threshold=90.0, operator="gt", severity="warning" ) assert config.metric_name == "cpu_percent" assert config.threshold == 90.0 assert config.operator == "gt" assert config.severity == "warning" class TestIntegration: """Integration tests for the observability server.""" @pytest.mark.asyncio async def test_full_monitoring_workflow(self): """Test complete monitoring workflow.""" with patch('observability_mcp.server.psutil') as mock_psutil: # Setup mocks mock_psutil.cpu_percent.return_value = 65.0 mock_memory = MagicMock() mock_memory.used = 2 * 1024**3 # 2GB mock_psutil.virtual_memory.return_value = mock_memory ctx = MagicMock() ctx.storage.get.return_value = [] ctx.storage.set = AsyncMock() # Step 1: Collect metrics metrics_result = await collect_performance_metrics(ctx=ctx) assert 'metrics' in metrics_result # Step 2: Generate report report_result = await generate_performance_reports(ctx=ctx, days=1) assert 'summary' in report_result # Step 3: Check alerts alert_result = await alert_on_anomalies(ctx=ctx) assert 'active_alerts' in alert_result # Verify workflow completion assert metrics_result['metrics']['cpu_percent'] == 65.0

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/sandraschi/observability-mcp'

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