mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +254 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +36 -30
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +159 -2
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +235 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +186 -0
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,522 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests for API handlers module.
|
3
|
-
|
4
|
-
This module contains comprehensive tests for the handlers module
|
5
|
-
to ensure 90%+ code coverage.
|
6
|
-
"""
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
import json
|
10
|
-
from unittest.mock import Mock, patch, MagicMock, AsyncMock
|
11
|
-
from typing import Dict, Any, List
|
12
|
-
|
13
|
-
from fastapi import Request
|
14
|
-
|
15
|
-
from mcp_proxy_adapter.api.handlers import (
|
16
|
-
execute_command, handle_batch_json_rpc, handle_json_rpc,
|
17
|
-
_create_error_response, get_server_health, get_commands_list
|
18
|
-
)
|
19
|
-
from mcp_proxy_adapter.core.errors import (
|
20
|
-
NotFoundError, MethodNotFoundError, InvalidRequestError,
|
21
|
-
InternalError, MicroserviceError, InvalidParamsError
|
22
|
-
)
|
23
|
-
from mcp_proxy_adapter.commands.command_registry import registry
|
24
|
-
|
25
|
-
|
26
|
-
class TestExecuteCommand:
|
27
|
-
"""Test cases for execute_command function."""
|
28
|
-
|
29
|
-
@pytest.fixture
|
30
|
-
def mock_registry(self):
|
31
|
-
"""Create a mock registry for testing."""
|
32
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_reg:
|
33
|
-
yield mock_reg
|
34
|
-
|
35
|
-
@pytest.fixture
|
36
|
-
def mock_command_class(self):
|
37
|
-
"""Create a mock command class for testing."""
|
38
|
-
class MockCommand:
|
39
|
-
name = "test_command"
|
40
|
-
|
41
|
-
@classmethod
|
42
|
-
def get_schema(cls):
|
43
|
-
return {"type": "object", "properties": {}}
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
async def run(cls, **kwargs):
|
47
|
-
return MockResult()
|
48
|
-
|
49
|
-
async def execute(self, **kwargs):
|
50
|
-
return {"result": "test"}
|
51
|
-
|
52
|
-
class MockResult:
|
53
|
-
def to_dict(self):
|
54
|
-
return {"result": "test"}
|
55
|
-
|
56
|
-
return MockCommand
|
57
|
-
|
58
|
-
@pytest.mark.asyncio
|
59
|
-
async def test_execute_command_success(self, mock_registry, mock_command_class):
|
60
|
-
"""Test successful command execution."""
|
61
|
-
mock_registry.get_command.return_value = mock_command_class
|
62
|
-
|
63
|
-
result = await execute_command("test_command", {"param": "value"})
|
64
|
-
|
65
|
-
assert result == {"result": "test"}
|
66
|
-
mock_registry.get_command.assert_called_once_with("test_command")
|
67
|
-
|
68
|
-
@pytest.mark.asyncio
|
69
|
-
async def test_execute_command_with_request_id(self, mock_registry, mock_command_class):
|
70
|
-
"""Test command execution with request ID."""
|
71
|
-
mock_registry.get_command.return_value = mock_command_class
|
72
|
-
|
73
|
-
result = await execute_command("test_command", {"param": "value"}, "req_123")
|
74
|
-
|
75
|
-
assert result == {"result": "test"}
|
76
|
-
|
77
|
-
@pytest.mark.asyncio
|
78
|
-
async def test_execute_command_not_found(self, mock_registry):
|
79
|
-
"""Test command execution when command not found."""
|
80
|
-
mock_registry.get_command.side_effect = NotFoundError("Command not found")
|
81
|
-
|
82
|
-
with pytest.raises(MethodNotFoundError):
|
83
|
-
await execute_command("nonexistent_command", {})
|
84
|
-
|
85
|
-
@pytest.mark.asyncio
|
86
|
-
async def test_execute_command_microservice_error(self, mock_registry):
|
87
|
-
"""Test command execution with microservice error."""
|
88
|
-
mock_registry.get_command.side_effect = InvalidRequestError("Invalid parameters")
|
89
|
-
|
90
|
-
with pytest.raises(InvalidRequestError):
|
91
|
-
await execute_command("test_command", {})
|
92
|
-
|
93
|
-
@pytest.mark.asyncio
|
94
|
-
async def test_execute_command_general_exception(self, mock_registry):
|
95
|
-
"""Test command execution with general exception."""
|
96
|
-
mock_registry.get_command.side_effect = Exception("Unexpected error")
|
97
|
-
|
98
|
-
with pytest.raises(InternalError):
|
99
|
-
await execute_command("test_command", {})
|
100
|
-
|
101
|
-
@pytest.mark.asyncio
|
102
|
-
async def test_execute_command_execution_time_logging(self, mock_registry, mock_command_class):
|
103
|
-
"""Test that execution time is logged."""
|
104
|
-
mock_registry.get_command.return_value = mock_command_class
|
105
|
-
|
106
|
-
with patch('mcp_proxy_adapter.api.handlers.logger') as mock_logger:
|
107
|
-
await execute_command("test_command", {})
|
108
|
-
|
109
|
-
# Check that timing was logged
|
110
|
-
mock_logger.info.assert_called()
|
111
|
-
call_args = mock_logger.info.call_args[0][0]
|
112
|
-
assert "executed in" in call_args
|
113
|
-
|
114
|
-
|
115
|
-
class TestHandleBatchJsonRpc:
|
116
|
-
"""Test cases for handle_batch_json_rpc function."""
|
117
|
-
|
118
|
-
@pytest.fixture
|
119
|
-
def mock_request(self):
|
120
|
-
"""Create a mock request object."""
|
121
|
-
request = Mock(spec=Request)
|
122
|
-
request.state.request_id = "batch_123"
|
123
|
-
return request
|
124
|
-
|
125
|
-
@pytest.mark.asyncio
|
126
|
-
async def test_handle_batch_json_rpc_success(self, mock_request):
|
127
|
-
"""Test successful batch JSON-RPC handling."""
|
128
|
-
batch_requests = [
|
129
|
-
{"jsonrpc": "2.0", "method": "help", "id": 1},
|
130
|
-
{"jsonrpc": "2.0", "method": "config", "id": 2}
|
131
|
-
]
|
132
|
-
|
133
|
-
with patch('mcp_proxy_adapter.api.handlers.handle_json_rpc') as mock_handle:
|
134
|
-
mock_handle.side_effect = [
|
135
|
-
{"jsonrpc": "2.0", "result": "help_result", "id": 1},
|
136
|
-
{"jsonrpc": "2.0", "result": "config_result", "id": 2}
|
137
|
-
]
|
138
|
-
|
139
|
-
responses = await handle_batch_json_rpc(batch_requests, mock_request)
|
140
|
-
|
141
|
-
assert len(responses) == 2
|
142
|
-
assert responses[0]["id"] == 1
|
143
|
-
assert responses[1]["id"] == 2
|
144
|
-
|
145
|
-
@pytest.mark.asyncio
|
146
|
-
async def test_handle_batch_json_rpc_without_request(self):
|
147
|
-
"""Test batch JSON-RPC handling without request object."""
|
148
|
-
batch_requests = [
|
149
|
-
{"jsonrpc": "2.0", "method": "help", "id": 1}
|
150
|
-
]
|
151
|
-
|
152
|
-
with patch('mcp_proxy_adapter.api.handlers.handle_json_rpc') as mock_handle:
|
153
|
-
mock_handle.return_value = {"jsonrpc": "2.0", "result": "help_result", "id": 1}
|
154
|
-
|
155
|
-
responses = await handle_batch_json_rpc(batch_requests)
|
156
|
-
|
157
|
-
assert len(responses) == 1
|
158
|
-
|
159
|
-
@pytest.mark.asyncio
|
160
|
-
async def test_handle_batch_json_rpc_empty_batch(self):
|
161
|
-
"""Test batch JSON-RPC handling with empty batch."""
|
162
|
-
responses = await handle_batch_json_rpc([])
|
163
|
-
|
164
|
-
assert responses == []
|
165
|
-
|
166
|
-
|
167
|
-
class TestHandleJsonRpc:
|
168
|
-
"""Test cases for handle_json_rpc function."""
|
169
|
-
|
170
|
-
@pytest.mark.asyncio
|
171
|
-
async def test_handle_json_rpc_success(self):
|
172
|
-
"""Test successful JSON-RPC handling."""
|
173
|
-
request_data = {
|
174
|
-
"jsonrpc": "2.0",
|
175
|
-
"method": "help",
|
176
|
-
"id": 1
|
177
|
-
}
|
178
|
-
|
179
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
180
|
-
mock_execute.return_value = {"result": "help_result"}
|
181
|
-
|
182
|
-
response = await handle_json_rpc(request_data)
|
183
|
-
|
184
|
-
assert response["jsonrpc"] == "2.0"
|
185
|
-
assert response["result"] == {"result": "help_result"}
|
186
|
-
assert response["id"] == 1
|
187
|
-
|
188
|
-
@pytest.mark.asyncio
|
189
|
-
async def test_handle_json_rpc_with_request_id(self):
|
190
|
-
"""Test JSON-RPC handling with request ID."""
|
191
|
-
request_data = {
|
192
|
-
"jsonrpc": "2.0",
|
193
|
-
"method": "help",
|
194
|
-
"id": 1
|
195
|
-
}
|
196
|
-
|
197
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
198
|
-
mock_execute.return_value = {"result": "help_result"}
|
199
|
-
|
200
|
-
response = await handle_json_rpc(request_data, "req_123")
|
201
|
-
|
202
|
-
assert response["jsonrpc"] == "2.0"
|
203
|
-
assert response["result"] == {"result": "help_result"}
|
204
|
-
|
205
|
-
@pytest.mark.asyncio
|
206
|
-
async def test_handle_json_rpc_invalid_version(self):
|
207
|
-
"""Test JSON-RPC handling with invalid version."""
|
208
|
-
request_data = {
|
209
|
-
"jsonrpc": "1.0", # Invalid version
|
210
|
-
"method": "help",
|
211
|
-
"id": 1
|
212
|
-
}
|
213
|
-
|
214
|
-
response = await handle_json_rpc(request_data)
|
215
|
-
|
216
|
-
assert response["jsonrpc"] == "2.0"
|
217
|
-
assert "error" in response
|
218
|
-
assert response["error"]["code"] == -32600 # Invalid Request
|
219
|
-
|
220
|
-
@pytest.mark.asyncio
|
221
|
-
async def test_handle_json_rpc_missing_method(self):
|
222
|
-
"""Test JSON-RPC handling with missing method."""
|
223
|
-
request_data = {
|
224
|
-
"jsonrpc": "2.0",
|
225
|
-
"id": 1
|
226
|
-
# Missing method
|
227
|
-
}
|
228
|
-
|
229
|
-
response = await handle_json_rpc(request_data)
|
230
|
-
|
231
|
-
assert response["jsonrpc"] == "2.0"
|
232
|
-
assert "error" in response
|
233
|
-
assert response["error"]["code"] == -32600 # Invalid Request
|
234
|
-
|
235
|
-
@pytest.mark.asyncio
|
236
|
-
async def test_handle_json_rpc_microservice_error(self):
|
237
|
-
"""Test JSON-RPC handling with microservice error."""
|
238
|
-
request_data = {
|
239
|
-
"jsonrpc": "2.0",
|
240
|
-
"method": "help",
|
241
|
-
"id": 1
|
242
|
-
}
|
243
|
-
|
244
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
245
|
-
mock_execute.side_effect = InvalidRequestError("Invalid parameters")
|
246
|
-
|
247
|
-
response = await handle_json_rpc(request_data)
|
248
|
-
|
249
|
-
assert response["jsonrpc"] == "2.0"
|
250
|
-
assert "error" in response
|
251
|
-
assert response["error"]["code"] == -32600 # Invalid Request
|
252
|
-
|
253
|
-
@pytest.mark.asyncio
|
254
|
-
async def test_handle_json_rpc_unhandled_exception(self):
|
255
|
-
"""Test JSON-RPC handling with unhandled exception."""
|
256
|
-
request_data = {
|
257
|
-
"jsonrpc": "2.0",
|
258
|
-
"method": "help",
|
259
|
-
"id": 1
|
260
|
-
}
|
261
|
-
|
262
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
263
|
-
mock_execute.side_effect = Exception("Unexpected error")
|
264
|
-
|
265
|
-
response = await handle_json_rpc(request_data)
|
266
|
-
|
267
|
-
assert response["jsonrpc"] == "2.0"
|
268
|
-
assert "error" in response
|
269
|
-
assert response["error"]["code"] == -32603 # Internal error
|
270
|
-
|
271
|
-
@pytest.mark.asyncio
|
272
|
-
async def test_handle_json_rpc_without_params(self):
|
273
|
-
"""Test JSON-RPC handling without parameters."""
|
274
|
-
request_data = {
|
275
|
-
"jsonrpc": "2.0",
|
276
|
-
"method": "help",
|
277
|
-
"id": 1
|
278
|
-
# No params
|
279
|
-
}
|
280
|
-
|
281
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
282
|
-
mock_execute.return_value = {"result": "help_result"}
|
283
|
-
|
284
|
-
response = await handle_json_rpc(request_data)
|
285
|
-
|
286
|
-
assert response["jsonrpc"] == "2.0"
|
287
|
-
assert response["result"] == {"result": "help_result"}
|
288
|
-
|
289
|
-
|
290
|
-
class TestCreateErrorResponse:
|
291
|
-
"""Test cases for _create_error_response function."""
|
292
|
-
|
293
|
-
def test_create_error_response(self):
|
294
|
-
"""Test creating error response."""
|
295
|
-
error = InvalidRequestError("Test error")
|
296
|
-
request_id = 123
|
297
|
-
|
298
|
-
response = _create_error_response(error, request_id)
|
299
|
-
|
300
|
-
assert response["jsonrpc"] == "2.0"
|
301
|
-
assert response["error"]["code"] == -32600
|
302
|
-
assert response["id"] == 123
|
303
|
-
|
304
|
-
def test_create_error_response_with_none_id(self):
|
305
|
-
"""Test creating error response with None ID."""
|
306
|
-
error = InvalidRequestError("Test error")
|
307
|
-
|
308
|
-
response = _create_error_response(error, None)
|
309
|
-
|
310
|
-
assert response["jsonrpc"] == "2.0"
|
311
|
-
assert response["error"]["code"] == -32600
|
312
|
-
assert response["id"] is None
|
313
|
-
|
314
|
-
|
315
|
-
class TestGetServerHealth:
|
316
|
-
"""Test cases for get_server_health function."""
|
317
|
-
|
318
|
-
@pytest.mark.asyncio
|
319
|
-
async def test_get_server_health(self):
|
320
|
-
"""Test getting server health information."""
|
321
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
322
|
-
mock_registry.get_all_commands.return_value = {"help": Mock(), "config": Mock()}
|
323
|
-
|
324
|
-
health = await get_server_health()
|
325
|
-
|
326
|
-
assert health["status"] == "ok"
|
327
|
-
assert health["version"] == "1.0.0"
|
328
|
-
assert "uptime" in health
|
329
|
-
assert "components" in health
|
330
|
-
assert health["components"]["commands"]["registered_count"] == 2
|
331
|
-
|
332
|
-
@pytest.mark.asyncio
|
333
|
-
async def test_get_server_health_with_empty_registry(self):
|
334
|
-
"""Test getting server health with empty registry."""
|
335
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
336
|
-
mock_registry.get_all_commands.return_value = {}
|
337
|
-
|
338
|
-
health = await get_server_health()
|
339
|
-
|
340
|
-
assert health["status"] == "ok"
|
341
|
-
assert health["components"]["commands"]["registered_count"] == 0
|
342
|
-
|
343
|
-
@pytest.mark.asyncio
|
344
|
-
async def test_get_server_health_system_info(self):
|
345
|
-
"""Test that system information is included."""
|
346
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
347
|
-
mock_registry.get_all_commands.return_value = {}
|
348
|
-
|
349
|
-
health = await get_server_health()
|
350
|
-
|
351
|
-
assert "system" in health["components"]
|
352
|
-
assert "process" in health["components"]
|
353
|
-
assert "python_version" in health["components"]["system"]
|
354
|
-
assert "platform" in health["components"]["system"]
|
355
|
-
assert "cpu_count" in health["components"]["system"]
|
356
|
-
|
357
|
-
|
358
|
-
class TestGetCommandsList:
|
359
|
-
"""Test cases for get_commands_list function."""
|
360
|
-
|
361
|
-
@pytest.mark.asyncio
|
362
|
-
async def test_get_commands_list(self):
|
363
|
-
"""Test getting commands list."""
|
364
|
-
mock_command1 = Mock()
|
365
|
-
mock_command1.get_schema.return_value = {
|
366
|
-
"description": "Test command 1",
|
367
|
-
"properties": {}
|
368
|
-
}
|
369
|
-
|
370
|
-
mock_command2 = Mock()
|
371
|
-
mock_command2.get_schema.return_value = {
|
372
|
-
"description": "Test command 2",
|
373
|
-
"properties": {}
|
374
|
-
}
|
375
|
-
|
376
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
377
|
-
mock_registry.get_all_commands.return_value = {
|
378
|
-
"help": mock_command1,
|
379
|
-
"config": mock_command2
|
380
|
-
}
|
381
|
-
|
382
|
-
commands = await get_commands_list()
|
383
|
-
|
384
|
-
assert len(commands) == 2
|
385
|
-
assert "help" in commands
|
386
|
-
assert "config" in commands
|
387
|
-
assert commands["help"]["name"] == "help"
|
388
|
-
assert commands["help"]["description"] == "Test command 1"
|
389
|
-
|
390
|
-
@pytest.mark.asyncio
|
391
|
-
async def test_get_commands_list_empty_registry(self):
|
392
|
-
"""Test getting commands list with empty registry."""
|
393
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
394
|
-
mock_registry.get_all_commands.return_value = {}
|
395
|
-
|
396
|
-
commands = await get_commands_list()
|
397
|
-
|
398
|
-
assert commands == {}
|
399
|
-
|
400
|
-
@pytest.mark.asyncio
|
401
|
-
async def test_get_commands_list_command_without_description(self):
|
402
|
-
"""Test getting commands list with command without description."""
|
403
|
-
mock_command = Mock()
|
404
|
-
mock_command.get_schema.return_value = {
|
405
|
-
"properties": {}
|
406
|
-
# No description
|
407
|
-
}
|
408
|
-
|
409
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
410
|
-
mock_registry.get_all_commands.return_value = {"test": mock_command}
|
411
|
-
|
412
|
-
commands = await get_commands_list()
|
413
|
-
|
414
|
-
assert "test" in commands
|
415
|
-
assert commands["test"]["description"] == ""
|
416
|
-
|
417
|
-
|
418
|
-
class TestHandlersIntegration:
|
419
|
-
"""Integration tests for handlers."""
|
420
|
-
|
421
|
-
@pytest.mark.asyncio
|
422
|
-
async def test_full_json_rpc_workflow(self):
|
423
|
-
"""Test complete JSON-RPC workflow."""
|
424
|
-
request_data = {
|
425
|
-
"jsonrpc": "2.0",
|
426
|
-
"method": "help",
|
427
|
-
"id": 1
|
428
|
-
}
|
429
|
-
|
430
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
431
|
-
mock_execute.return_value = {"result": "help_result"}
|
432
|
-
|
433
|
-
response = await handle_json_rpc(request_data)
|
434
|
-
|
435
|
-
assert response["jsonrpc"] == "2.0"
|
436
|
-
assert response["result"] == {"result": "help_result"}
|
437
|
-
assert response["id"] == 1
|
438
|
-
|
439
|
-
@pytest.mark.asyncio
|
440
|
-
async def test_batch_json_rpc_workflow(self):
|
441
|
-
"""Test complete batch JSON-RPC workflow."""
|
442
|
-
batch_requests = [
|
443
|
-
{"jsonrpc": "2.0", "method": "help", "id": 1},
|
444
|
-
{"jsonrpc": "2.0", "method": "config", "id": 2}
|
445
|
-
]
|
446
|
-
|
447
|
-
with patch('mcp_proxy_adapter.api.handlers.handle_json_rpc') as mock_handle:
|
448
|
-
mock_handle.side_effect = [
|
449
|
-
{"jsonrpc": "2.0", "result": "help_result", "id": 1},
|
450
|
-
{"jsonrpc": "2.0", "result": "config_result", "id": 2}
|
451
|
-
]
|
452
|
-
|
453
|
-
responses = await handle_batch_json_rpc(batch_requests)
|
454
|
-
|
455
|
-
assert len(responses) == 2
|
456
|
-
assert responses[0]["id"] == 1
|
457
|
-
assert responses[1]["id"] == 2
|
458
|
-
|
459
|
-
|
460
|
-
class TestHandlersEdgeCases:
|
461
|
-
"""Test edge cases for handlers."""
|
462
|
-
|
463
|
-
@pytest.mark.asyncio
|
464
|
-
async def test_execute_command_with_complex_params(self):
|
465
|
-
"""Test command execution with complex parameters."""
|
466
|
-
class MockCommandClass:
|
467
|
-
@classmethod
|
468
|
-
async def run(cls, **kwargs):
|
469
|
-
return MockResult()
|
470
|
-
|
471
|
-
class MockResult:
|
472
|
-
def to_dict(self):
|
473
|
-
return {"result": "complex"}
|
474
|
-
|
475
|
-
with patch('mcp_proxy_adapter.api.handlers.registry') as mock_registry:
|
476
|
-
mock_registry.get_command.return_value = MockCommandClass
|
477
|
-
|
478
|
-
complex_params = {
|
479
|
-
"nested": {"key": "value"},
|
480
|
-
"list": [1, 2, 3],
|
481
|
-
"boolean": True
|
482
|
-
}
|
483
|
-
|
484
|
-
result = await execute_command("test_command", complex_params)
|
485
|
-
|
486
|
-
assert result == {"result": "complex"}
|
487
|
-
|
488
|
-
@pytest.mark.asyncio
|
489
|
-
async def test_json_rpc_with_null_params(self):
|
490
|
-
"""Test JSON-RPC handling with null parameters."""
|
491
|
-
request_data = {
|
492
|
-
"jsonrpc": "2.0",
|
493
|
-
"method": "help",
|
494
|
-
"params": None,
|
495
|
-
"id": 1
|
496
|
-
}
|
497
|
-
|
498
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
499
|
-
mock_execute.return_value = {"result": "help_result"}
|
500
|
-
|
501
|
-
response = await handle_json_rpc(request_data)
|
502
|
-
|
503
|
-
assert response["jsonrpc"] == "2.0"
|
504
|
-
assert response["result"] == {"result": "help_result"}
|
505
|
-
|
506
|
-
@pytest.mark.asyncio
|
507
|
-
async def test_json_rpc_without_id(self):
|
508
|
-
"""Test JSON-RPC handling without ID."""
|
509
|
-
request_data = {
|
510
|
-
"jsonrpc": "2.0",
|
511
|
-
"method": "help"
|
512
|
-
# No id
|
513
|
-
}
|
514
|
-
|
515
|
-
with patch('mcp_proxy_adapter.api.handlers.execute_command') as mock_execute:
|
516
|
-
mock_execute.return_value = {"result": "help_result"}
|
517
|
-
|
518
|
-
response = await handle_json_rpc(request_data)
|
519
|
-
|
520
|
-
assert response["jsonrpc"] == "2.0"
|
521
|
-
assert response["result"] == {"result": "help_result"}
|
522
|
-
assert response["id"] is None
|