mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.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 (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.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