Skip to main content
Glama

NetBox MCP Server

by fringemonkey
test_example.py15.8 kB
"""Example test file demonstrating MCP server testing best practices. This file serves as a teaching example for how to test MCP servers effectively. It demonstrates various testing patterns and techniques. """ import asyncio import pytest from unittest.mock import Mock, patch from mcp.types import TextContent from src.tools import hosts class TestMCPToolExample: """Example test class demonstrating MCP tool testing patterns.""" @pytest.fixture def sample_device_data(self): """Example fixture providing sample device data.""" return { "id": 1, "name": "example-server", "display": "example-server", "device_type": {"slug": "server", "display": "Server"}, "device_role": {"slug": "web-server", "display": "Web Server"}, "status": {"value": "active", "label": "Active"}, "primary_ip4": { "address": "192.168.1.100/24", "display": "192.168.1.100/24" }, "site": {"slug": "datacenter-1", "display": "Datacenter 1"}, "rack": {"name": "R01", "display": "R01"}, "position": 10, "created": "2025-01-01T00:00:00Z", "last_updated": "2025-01-01T12:00:00Z" } @pytest.fixture def mock_netbox_client(self, sample_device_data): """Example fixture providing a mocked NetBox client.""" client = Mock() client.list_devices.return_value = [sample_device_data] client.get_device.return_value = sample_device_data client.search_devices.return_value = [sample_device_data] return client @pytest.fixture def mock_state_client(self): """Example fixture providing a mocked state confidence client.""" client = Mock() client.get_certainty_score.return_value = { "certainty_score": 0.95, "data_age_seconds": 300, "last_verified": "2025-01-01T12:00:00Z" } return client # Example 1: Basic Success Test @pytest.mark.unit @pytest.mark.asyncio async def test_list_hosts_success_basic(self, mock_netbox_client, mock_state_client): """Example: Basic success test for list_hosts tool. This test demonstrates: - Simple success scenario testing - Basic assertions - Fixture usage """ # Arrange arguments = {"limit": 10, "include_certainty": True} # Act result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 assert isinstance(result[0], TextContent) assert "Found 1 host(s)" in result[0].text assert "example-server" in result[0].text # Example 2: Parameter Validation Test @pytest.mark.unit @pytest.mark.asyncio async def test_list_hosts_with_filters(self, mock_netbox_client, mock_state_client): """Example: Testing with different parameter combinations. This test demonstrates: - Testing with various input parameters - Verifying correct client method calls - Parameter validation """ # Test with name filter arguments = {"name": "example", "limit": 5} await hosts.handle_list_hosts(arguments, mock_netbox_client, mock_state_client) # Verify the client was called with correct parameters mock_netbox_client.list_devices.assert_called_with( name="example", primary_ip=None, role=None, limit=5 ) # Test with role filter arguments = {"role": "web-server", "limit": 5} await hosts.handle_list_hosts(arguments, mock_netbox_client, mock_state_client) mock_netbox_client.list_devices.assert_called_with( name=None, primary_ip=None, role="web-server", limit=5 ) # Example 3: Error Handling Test @pytest.mark.unit @pytest.mark.error_handling @pytest.mark.asyncio async def test_list_hosts_netbox_error(self, mock_state_client): """Example: Testing error handling scenarios. This test demonstrates: - Testing error conditions - Verifying error messages - Graceful error handling """ # Arrange - Create a client that raises an exception mock_netbox_client = Mock() mock_netbox_client.list_devices.side_effect = Exception("NetBox API error") arguments = {"limit": 10} # Act result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 assert "Error" in result[0].text assert "NetBox" in result[0].text # Example 4: Edge Case Test @pytest.mark.unit @pytest.mark.edge_cases @pytest.mark.asyncio async def test_list_hosts_empty_result(self, mock_state_client): """Example: Testing edge cases and boundary conditions. This test demonstrates: - Testing with empty results - Edge case handling - Boundary condition testing """ # Arrange - Create a client that returns empty results mock_netbox_client = Mock() mock_netbox_client.list_devices.return_value = [] arguments = {"limit": 10} # Act result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 assert "Found 0 host(s)" in result[0].text assert "[]" in result[0].text # Example 5: State Confidence Integration Test @pytest.mark.unit @pytest.mark.asyncio async def test_list_hosts_with_certainty_scores(self, mock_netbox_client, mock_state_client): """Example: Testing integration with state confidence system. This test demonstrates: - Testing optional integrations - Verifying data enrichment - Testing with external dependencies """ # Arrange arguments = {"limit": 10, "include_certainty": True} # Act result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 assert "Found 1 host(s)" in result[0].text assert "certainty" in result[0].text.lower() assert "0.95" in result[0].text # Certainty score should be included # Verify state client was called mock_state_client.get_certainty_score.assert_called() # Example 6: Performance Test @pytest.mark.performance @pytest.mark.asyncio async def test_list_hosts_performance(self, mock_netbox_client, mock_state_client): """Example: Basic performance testing. This test demonstrates: - Response time testing - Performance assertions - Basic benchmarking """ import time # Arrange arguments = {"limit": 10} # Act start_time = time.time() result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) end_time = time.time() # Assert response_time = end_time - start_time assert response_time < 0.1, f"Response too slow: {response_time:.3f}s" assert len(result) == 1 # Example 7: Data Validation Test @pytest.mark.unit @pytest.mark.asyncio async def test_get_host_data_structure(self, mock_netbox_client, mock_state_client, sample_device_data): """Example: Testing data structure and content. This test demonstrates: - Verifying data structure - Content validation - Field presence checking """ # Arrange arguments = {"hostname": "example-server", "include_certainty": True} # Act result = await hosts.handle_get_host( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 result_text = result[0].text # Verify all important fields are present assert "example-server" in result_text assert "Web Server" in result_text # Device role display assert "Server" in result_text # Device type display assert "Active" in result_text # Status assert "192.168.1.100" in result_text # IP address assert "Datacenter 1" in result_text # Site assert "R01" in result_text # Rack # Example 8: Concurrent Execution Test @pytest.mark.integration @pytest.mark.asyncio async def test_concurrent_tool_calls(self, mock_netbox_client, mock_state_client): """Example: Testing concurrent execution. This test demonstrates: - Concurrent execution testing - Async/await patterns - Performance under load """ # Arrange arguments = {"limit": 10} # Act - Create multiple concurrent calls tasks = [ hosts.handle_list_hosts(arguments, mock_netbox_client, mock_state_client) for _ in range(5) ] results = await asyncio.gather(*tasks) # Assert assert len(results) == 5 for result in results: assert len(result) == 1 assert isinstance(result[0], TextContent) # Example 9: Mock Verification Test @pytest.mark.unit @pytest.mark.asyncio async def test_mock_verification(self, mock_netbox_client, mock_state_client): """Example: Verifying mock interactions. This test demonstrates: - Verifying mock calls - Call argument validation - Mock interaction testing """ # Arrange arguments = {"name": "test", "limit": 5, "include_certainty": True} # Act await hosts.handle_list_hosts(arguments, mock_netbox_client, mock_state_client) # Assert - Verify mock was called correctly mock_netbox_client.list_devices.assert_called_once_with( name="test", primary_ip=None, role=None, limit=5 ) # Verify state client was called for certainty scores mock_state_client.get_certainty_score.assert_called() # Example 10: Parameterized Test @pytest.mark.unit @pytest.mark.parametrize("limit,expected_text", [ (1, "Found 1 host(s)"), (5, "Found 1 host(s)"), (10, "Found 1 host(s)"), (100, "Found 1 host(s)"), ]) @pytest.mark.asyncio async def test_list_hosts_with_different_limits(self, mock_netbox_client, mock_state_client, limit, expected_text): """Example: Parameterized testing. This test demonstrates: - Parameterized testing - Testing multiple scenarios - Data-driven testing """ # Arrange arguments = {"limit": limit} # Act result = await hosts.handle_list_hosts( arguments, mock_netbox_client, mock_state_client ) # Assert assert len(result) == 1 assert expected_text in result[0].text class TestAdvancedPatterns: """Advanced testing patterns and techniques.""" # Example 11: Context Manager Testing @pytest.mark.unit @pytest.mark.asyncio async def test_with_context_manager(self): """Example: Testing with context managers and resource management.""" with patch('src.tools.hosts.VaultClient') as mock_vault_class: # Setup mock mock_vault_instance = Mock() mock_vault_class.return_value = mock_vault_instance mock_vault_instance.mint_netbox_token.return_value = "test-token" # Test code that uses the context manager # (This would be actual implementation code) pass # Example 12: Exception Testing @pytest.mark.unit @pytest.mark.error_handling @pytest.mark.asyncio async def test_specific_exception_handling(self): """Example: Testing specific exception types.""" mock_client = Mock() mock_client.list_devices.side_effect = ConnectionError("Network unreachable") # Test that specific exceptions are handled appropriately result = await hosts.handle_list_hosts( {"limit": 10}, mock_client, None ) assert "Error" in result[0].text assert "Connection" in result[0].text or "Network" in result[0].text # Example 13: Custom Assertions def assert_valid_mcp_response(self, result): """Custom assertion helper for MCP responses.""" assert isinstance(result, list), "Result should be a list" assert len(result) > 0, "Result should not be empty" assert all(isinstance(item, TextContent) for item in result), "All items should be TextContent" assert all(len(item.text) > 0 for item in result), "All text content should be non-empty" @pytest.mark.unit @pytest.mark.asyncio async def test_with_custom_assertions(self, mock_netbox_client, mock_state_client): """Example: Using custom assertion helpers.""" result = await hosts.handle_list_hosts( {"limit": 10}, mock_netbox_client, mock_state_client ) # Use custom assertion self.assert_valid_mcp_response(result) # Example 14: Test Configuration and Setup class TestConfiguration: """Example: Test configuration and setup patterns.""" @pytest.fixture(scope="class") def class_setup(self): """Class-level setup for expensive operations.""" # Setup that runs once per test class return "class_setup_complete" @pytest.fixture(autouse=True) def auto_setup(self): """Auto-use fixture that runs for every test.""" # Setup that runs before every test yield # Test runs here # Cleanup that runs after every test def test_with_class_setup(self, class_setup): """Test using class-level setup.""" assert class_setup == "class_setup_complete" # Example 15: Test Documentation class TestDocumentation: """Example: Well-documented test class. This class demonstrates how to write well-documented tests that serve as both tests and documentation. """ @pytest.mark.unit @pytest.mark.asyncio async def test_well_documented_example(self): """Test with comprehensive documentation. This test demonstrates a well-documented test that explains: - What is being tested - Why it's important - What the expected behavior is - Any special considerations The test verifies that the list_hosts tool correctly handles the case where no hosts are found, which is important for user experience and error handling. Expected behavior: - Should return a single TextContent response - Should indicate that 0 hosts were found - Should not raise an exception - Should be user-friendly in the message Special considerations: - Empty results are common in new NetBox installations - Users should get clear feedback about empty results - The response should be consistent with other list operations """ # This would be the actual test implementation # with detailed comments explaining each step pass

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/fringemonkey/mcp-dc'

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