""" Integration tests for the per-user dynamic MCP endpoints. Uses Starlette TestClient to simulate real HTTP requests through the server. """ from unittest.mock import AsyncMock, MagicMock, patch import pytest from server import create_multi_endpoint_app from starlette.testclient import TestClient app = create_multi_endpoint_app() client = TestClient(app) @pytest.fixture def mock_managers(): """Mock the core managers to avoid needing a real database or keys.""" # Mock Site Manager mock_site_manager = MagicMock() mock_site_manager.list_all_sites.return_value = [] # Mock Key Manager mock_key_manager = AsyncMock() mock_key_manager.validate_key.return_value = { "key_id": "test-key-123", "user_id": "user-123", "scopes": "read write", } # Mock Database mock_db = AsyncMock() mock_db.get_site_by_alias.return_value = { "id": "site-123", "user_id": "user-123", "plugin_type": "wordpress", "alias": "myblog", "url": "https://example.com", "credentials": b"encrypted-blob", "status": "active", } with ( patch("server.get_site_manager", return_value=mock_site_manager), patch("core.user_keys.get_user_key_manager", return_value=mock_key_manager), patch("core.database.get_database", return_value=mock_db), ): yield {"key_manager": mock_key_manager, "db": mock_db} @pytest.mark.integration def test_dynamic_endpoint_unauthorized(): """Test that requests without API key are rejected.""" response = client.post( "/u/user-123/myblog/mcp", json={"jsonrpc": "2.0", "id": 1, "method": "initialize"} ) assert response.status_code == 401 @pytest.mark.integration def test_dynamic_endpoint_invalid_method(mock_managers): """Test that unauthorized methods or valid requests with bad structure return JSON-RPC errors.""" response = client.post( "/u/user-123/myblog/mcp", headers={"Authorization": "Bearer mhu_test-key"}, json={"jsonrpc": "2.0", "id": 1, "method": "invalid_method"}, ) assert response.status_code == 200 # JSON-RPC errors are 200 OK data = response.json() assert "error" in data assert "not supported" in data["error"]["message"].lower() @pytest.mark.integration def test_dynamic_endpoint_initialize(mock_managers): """Test standard MCP initialize on the dynamic endpoint.""" response = client.post( "/u/user-123/myblog/mcp", headers={"Authorization": "Bearer mhu_test-key"}, json={ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test-client", "version": "1.0.0"}, }, }, ) assert response.status_code == 200 data = response.json() assert "result" in data assert "capabilities" in data["result"] assert "serverInfo" in data["result"] @pytest.mark.integration def test_dynamic_endpoint_mismatched_user(mock_managers): """Test that if the key belongs to a different user, it's rejected.""" mock_managers["key_manager"].validate_key.return_value = { "key_id": "test-key-123", "user_id": "different-user-456", "scopes": "read write", } response = client.post( "/u/user-123/myblog/mcp", headers={"Authorization": "Bearer mhu_test-key"}, json={"jsonrpc": "2.0", "id": 1, "method": "initialize"}, ) assert response.status_code == 403 data = response.json() assert "does not match" in data["error"]["message"]