mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__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.
Files changed (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.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