"""Tests for the MCP Mailu server.""" import pytest import httpx from unittest.mock import AsyncMock, Mock, patch import os from mcp_mailu.server import create_mailu_client, create_mcp_server, MAILU_OPENAPI_SPEC class TestMailuClient: """Test cases for Mailu HTTP client creation.""" def test_create_mailu_client(self): """Test creation of authenticated HTTP client.""" base_url = "https://mail.example.com/api/v1" api_token = "test-token" client = create_mailu_client(base_url, api_token) assert isinstance(client, httpx.AsyncClient) assert client.base_url == base_url assert client.headers["Authorization"] == "Bearer test-token" assert client.headers["Content-Type"] == "application/json" assert client.headers["Accept"] == "application/json" class TestMCPServer: """Test cases for the MCP server initialization.""" @pytest.mark.asyncio @patch.dict(os.environ, { "MAILU_BASE_URL": "https://test.example.com", "MAILU_API_TOKEN": "test-token" }) async def test_create_mcp_server_with_env_vars(self): """Test MCP server creation with environment variables.""" with patch('mcp_mailu.server.FastMCP') as mock_fastmcp: mock_server = Mock() mock_fastmcp.from_openapi.return_value = mock_server server = await create_mcp_server() # Verify FastMCP.from_openapi was called mock_fastmcp.from_openapi.assert_called_once() # Get the call arguments call_args = mock_fastmcp.from_openapi.call_args # Verify client is an httpx.AsyncClient assert isinstance(call_args.kwargs['client'], httpx.AsyncClient) # Verify OpenAPI spec is passed assert call_args.kwargs['openapi_spec'] is not None # Verify route maps are configured assert 'route_maps' in call_args.kwargs assert len(call_args.kwargs['route_maps']) > 0 # Verify server info assert call_args.kwargs['name'] == "Mailu MCP Server" assert call_args.kwargs['version'] == "1.0.0" @pytest.mark.asyncio @patch.dict(os.environ, {}, clear=True) async def test_create_mcp_server_without_env_vars(self): """Test MCP server creation with default values.""" with patch('mcp_mailu.server.FastMCP') as mock_fastmcp: mock_server = Mock() mock_fastmcp.from_openapi.return_value = mock_server server = await create_mcp_server() # Should still create server with defaults mock_fastmcp.from_openapi.assert_called_once() call_args = mock_fastmcp.from_openapi.call_args client = call_args.kwargs['client'] # Should use default base URL assert "mail.example.com" in str(client.base_url) def test_openapi_spec_structure(self): """Test that the OpenAPI spec has required structure.""" spec = MAILU_OPENAPI_SPEC # Basic OpenAPI structure assert spec["swagger"] == "2.0" assert "info" in spec assert "paths" in spec assert "definitions" in spec # Required API info assert spec["info"]["title"] == "Mailu API" assert spec["basePath"] == "/api/v1" # Security configuration assert "securityDefinitions" in spec assert "Bearer" in spec["securityDefinitions"] # Verify key endpoints exist assert "/user" in spec["paths"] assert "/domain" in spec["paths"] assert "/alias" in spec["paths"] # Verify user endpoints have CRUD operations user_path = spec["paths"]["/user"] assert "get" in user_path # List users assert "post" in user_path # Create user user_detail_path = spec["paths"]["/user/{email}"] assert "get" in user_detail_path # Get user assert "patch" in user_detail_path # Update user assert "delete" in user_detail_path # Delete user # Verify definitions exist assert "UserCreate" in spec["definitions"] assert "UserGet" in spec["definitions"] assert "UserUpdate" in spec["definitions"] assert "Domain" in spec["definitions"] assert "Alias" in spec["definitions"] def test_user_create_schema(self): """Test UserCreate schema has required fields.""" spec = MAILU_OPENAPI_SPEC user_create = spec["definitions"]["UserCreate"] # Required fields assert "email" in user_create["required"] assert "raw_password" in user_create["required"] # Properties properties = user_create["properties"] assert "email" in properties assert "raw_password" in properties assert "quota_bytes" in properties assert "enabled" in properties assert "displayed_name" in properties # Field types assert properties["email"]["type"] == "string" assert properties["raw_password"]["type"] == "string" assert properties["quota_bytes"]["type"] == "integer" assert properties["enabled"]["type"] == "boolean" def test_domain_schema(self): """Test Domain schema structure.""" spec = MAILU_OPENAPI_SPEC domain = spec["definitions"]["Domain"] # Required fields assert "name" in domain["required"] # Properties properties = domain["properties"] assert "name" in properties assert "max_users" in properties assert "max_aliases" in properties assert "signup_enabled" in properties # Field types assert properties["name"]["type"] == "string" assert properties["max_users"]["type"] == "integer" assert properties["signup_enabled"]["type"] == "boolean" class TestEnvironmentConfiguration: """Test environment variable handling.""" @patch.dict(os.environ, { "MAILU_BASE_URL": "https://custom.mail.com", "MAILU_API_TOKEN": "custom-token" }) def test_environment_variables_loaded(self): """Test that environment variables are properly loaded.""" # These would be used in create_mcp_server assert os.getenv("MAILU_BASE_URL") == "https://custom.mail.com" assert os.getenv("MAILU_API_TOKEN") == "custom-token" @patch.dict(os.environ, {}, clear=True) def test_default_values_when_no_env_vars(self): """Test default values when environment variables are not set.""" base_url = os.getenv("MAILU_BASE_URL", "https://mail.example.com") api_token = os.getenv("MAILU_API_TOKEN", "") assert base_url == "https://mail.example.com" assert api_token == "" @pytest.mark.asyncio class TestIntegration: """Integration tests for the complete server.""" @patch('mcp_mailu.server.FastMCP') async def test_server_initialization_flow(self, mock_fastmcp): """Test the complete server initialization flow.""" mock_server = Mock() mock_fastmcp.from_openapi.return_value = mock_server with patch.dict(os.environ, { "MAILU_BASE_URL": "https://test.mail.com", "MAILU_API_TOKEN": "integration-token" }): server = await create_mcp_server() # Verify server was created assert server == mock_server # Verify from_openapi was called with correct parameters call_args = mock_fastmcp.from_openapi.call_args # Check client configuration client = call_args.kwargs['client'] assert isinstance(client, httpx.AsyncClient) assert "test.mail.com" in str(client.base_url) assert client.headers["Authorization"] == "Bearer integration-token" # Check OpenAPI spec spec = call_args.kwargs['openapi_spec'] assert spec['host'] == 'test.mail.com' assert spec['schemes'] == ['https'] # Check route mappings route_maps = call_args.kwargs['route_maps'] assert len(route_maps) > 0 # Verify some expected route patterns patterns = [rm.pattern for rm in route_maps] assert any("GET /user$" in pattern for pattern in patterns) assert any("GET /domain$" in pattern for pattern in patterns)