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,436 +0,0 @@
1
- """
2
- Tests for base command module.
3
-
4
- This module contains comprehensive tests for the base Command class
5
- to ensure 90%+ code coverage.
6
- """
7
-
8
- import pytest
9
- import inspect
10
- from unittest.mock import Mock, patch, MagicMock, AsyncMock
11
- from typing import Dict, Any
12
-
13
- from mcp_proxy_adapter.commands.base import Command
14
- from mcp_proxy_adapter.commands.result import SuccessResult, ErrorResult
15
- from mcp_proxy_adapter.core.errors import (
16
- ValidationError, InvalidParamsError, NotFoundError,
17
- TimeoutError, CommandError, InternalError
18
- )
19
-
20
-
21
- class MockResultClass:
22
- """Mock result class for testing."""
23
- def to_dict(self):
24
- return {"status": "success", "data": "test_data"}
25
-
26
- @classmethod
27
- def get_schema(cls):
28
- return {"type": "object", "properties": {"data": {"type": "string"}}}
29
-
30
-
31
- class TestCommand(Command):
32
- """Test command class for testing."""
33
- name = "test_command"
34
- result_class = MockResultClass
35
-
36
- async def execute(self, **kwargs):
37
- return SuccessResult(data=kwargs)
38
-
39
-
40
- def test_success_result():
41
- """Test success result creation."""
42
- result = SuccessResult(data="test_data")
43
- assert result.data == "test_data"
44
- result_dict = result.to_dict()
45
- assert result_dict["success"] is True
46
-
47
-
48
- def test_error_result():
49
- """Test error result creation."""
50
- result = ErrorResult(message="Test error", code=400)
51
- assert result.message == "Test error"
52
- assert result.code == 400
53
-
54
-
55
- class TestCommandClass:
56
- """Test cases for Command class."""
57
-
58
- @pytest.mark.asyncio
59
- async def test_execute(self):
60
- """Test execute method."""
61
- command = TestCommand()
62
- result = await command.execute(test_param="value")
63
- assert isinstance(result, SuccessResult)
64
-
65
- @pytest.mark.asyncio
66
- async def test_run(self):
67
- """Test run method (with validation)."""
68
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
69
- mock_registry.get_priority_command.return_value = TestCommand
70
- mock_registry.has_instance.return_value = False
71
-
72
- # Mock the command execution to avoid registry lookup
73
- with patch.object(TestCommand, 'execute', return_value=SuccessResult(data={"value": "test_value"})):
74
- result = await TestCommand.run(value="test_value")
75
- assert isinstance(result, SuccessResult)
76
- assert result.data == {"value": "test_value"}
77
-
78
- def test_get_schema(self):
79
- """Test get_schema method."""
80
- schema = TestCommand.get_schema()
81
- assert schema["type"] == "object"
82
- assert "properties" in schema
83
-
84
- def test_get_result_schema(self):
85
- """Test get_result_schema method."""
86
- schema = TestCommand.get_result_schema()
87
- assert schema["type"] == "object"
88
- assert "properties" in schema
89
-
90
- def test_get_param_info(self):
91
- """Test get_param_info method."""
92
- param_info = TestCommand.get_param_info()
93
- assert isinstance(param_info, dict)
94
-
95
- def test_validate_params_none(self):
96
- """Test validate_params with None."""
97
- params = TestCommand.validate_params(None)
98
- assert params == {}
99
-
100
- def test_validate_params_empty_dict(self):
101
- """Test validate_params with empty dict."""
102
- params = TestCommand.validate_params({})
103
- assert params == {}
104
-
105
- def test_validate_params_with_none_values(self):
106
- """Test validate_params with None values."""
107
- params = TestCommand.validate_params({"param1": None, "param2": "value"})
108
- assert "param1" not in params
109
- assert params["param2"] == "value"
110
-
111
- def test_validate_params_with_empty_strings(self):
112
- """Test validate_params with empty strings."""
113
- params = TestCommand.validate_params({"param1": "", "param2": "null", "param3": "value"})
114
- assert "param1" not in params
115
- assert "param2" not in params
116
- assert params["param3"] == "value"
117
-
118
- def test_validate_params_with_cmdname_none(self):
119
- """Test validate_params with cmdname parameter."""
120
- params = TestCommand.validate_params({"cmdname": None, "other": "value"})
121
- assert params["cmdname"] is None
122
- assert params["other"] == "value"
123
-
124
- def test_validate_params_copy_input(self):
125
- """Test that validate_params doesn't modify input."""
126
- input_params = {"param1": "value1", "param2": None}
127
- result = TestCommand.validate_params(input_params)
128
- assert input_params == {"param1": "value1", "param2": None} # Input unchanged
129
- assert "param2" not in result # Result filtered
130
-
131
- @pytest.mark.asyncio
132
- async def test_run_with_hooks_skip_processing(self):
133
- """Test run method when hooks skip standard processing."""
134
- with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
135
- mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=False)
136
-
137
- result = await TestCommand.run(test_param="value")
138
- assert isinstance(result, SuccessResult)
139
- assert result.data == {"test_param": "value"}
140
-
141
- @pytest.mark.asyncio
142
- async def test_run_command_not_found(self):
143
- """Test run method when command not found."""
144
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
145
- mock_registry.get_priority_command.return_value = None
146
-
147
- result = await TestCommand.run(test_param="value")
148
- assert isinstance(result, ErrorResult)
149
- assert "not found" in result.message
150
-
151
- @pytest.mark.asyncio
152
- async def test_run_with_registry_instance(self):
153
- """Test run method with existing registry instance."""
154
- mock_command = Mock()
155
- mock_command.execute = AsyncMock(return_value=SuccessResult(data="test"))
156
-
157
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
158
- mock_registry.get_priority_command.return_value = TestCommand
159
- mock_registry.has_instance.return_value = True
160
- mock_registry.get_command_instance.return_value = mock_command
161
-
162
- result = await TestCommand.run(test_param="value")
163
- assert isinstance(result, SuccessResult)
164
-
165
- @pytest.mark.asyncio
166
- async def test_run_validation_error(self):
167
- """Test run method with validation error."""
168
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
169
- mock_registry.get_priority_command.return_value = TestCommand
170
- mock_registry.has_instance.return_value = False
171
-
172
- # Mock TestCommand to raise ValidationError
173
- with patch.object(TestCommand, 'execute', side_effect=ValidationError("Invalid params")):
174
- result = await TestCommand.run(test_param="value")
175
- assert isinstance(result, ErrorResult)
176
- assert "Invalid params" in result.message
177
-
178
- @pytest.mark.asyncio
179
- async def test_run_invalid_params_error(self):
180
- """Test run method with invalid params error."""
181
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
182
- mock_registry.get_priority_command.return_value = TestCommand
183
- mock_registry.has_instance.return_value = False
184
-
185
- with patch.object(TestCommand, 'execute', side_effect=InvalidParamsError("Invalid params")):
186
- result = await TestCommand.run(test_param="value")
187
- assert isinstance(result, ErrorResult)
188
- assert "Invalid params" in result.message
189
-
190
- @pytest.mark.asyncio
191
- async def test_run_not_found_error(self):
192
- """Test run method with not found error."""
193
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
194
- mock_registry.get_priority_command.return_value = TestCommand
195
- mock_registry.has_instance.return_value = False
196
-
197
- with patch.object(TestCommand, 'execute', side_effect=NotFoundError("Not found")):
198
- result = await TestCommand.run(test_param="value")
199
- assert isinstance(result, ErrorResult)
200
- assert "Not found" in result.message
201
-
202
- @pytest.mark.asyncio
203
- async def test_run_timeout_error(self):
204
- """Test run method with timeout error."""
205
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
206
- mock_registry.get_priority_command.return_value = TestCommand
207
- mock_registry.has_instance.return_value = False
208
-
209
- with patch.object(TestCommand, 'execute', side_effect=TimeoutError("Timeout")):
210
- result = await TestCommand.run(test_param="value")
211
- assert isinstance(result, ErrorResult)
212
- assert "Timeout" in result.message
213
-
214
- @pytest.mark.asyncio
215
- async def test_run_command_error(self):
216
- """Test run method with command error."""
217
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
218
- mock_registry.get_priority_command.return_value = TestCommand
219
- mock_registry.has_instance.return_value = False
220
-
221
- with patch.object(TestCommand, 'execute', side_effect=CommandError("Command error")):
222
- result = await TestCommand.run(test_param="value")
223
- assert isinstance(result, ErrorResult)
224
- assert "Command error" in result.message
225
-
226
- @pytest.mark.asyncio
227
- async def test_run_unexpected_exception(self):
228
- """Test run method with unexpected exception."""
229
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
230
- mock_registry.get_priority_command.return_value = TestCommand
231
- mock_registry.has_instance.return_value = False
232
-
233
- with patch.object(TestCommand, 'execute', side_effect=Exception("Unexpected error")):
234
- result = await TestCommand.run(test_param="value")
235
- assert isinstance(result, ErrorResult)
236
- assert "Command execution error" in result.message
237
-
238
- @pytest.mark.asyncio
239
- async def test_run_with_none_kwargs(self):
240
- """Test run method with None kwargs."""
241
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
242
- mock_registry.get_priority_command.return_value = TestCommand
243
- mock_registry.has_instance.return_value = False
244
-
245
- result = await TestCommand.run()
246
- assert isinstance(result, SuccessResult)
247
-
248
- def test_get_metadata(self):
249
- """Test get_metadata method."""
250
- metadata = TestCommand.get_metadata()
251
- assert isinstance(metadata, dict)
252
- assert "name" in metadata
253
- assert "summary" in metadata
254
- assert "params" in metadata
255
-
256
- def test_get_metadata_with_schema(self):
257
- """Test get_metadata method with schema."""
258
- with patch.object(TestCommand, 'get_param_info') as mock_param_info:
259
- mock_param_info.return_value = {
260
- "param1": {"type": "string", "description": "Test param"}
261
- }
262
-
263
- metadata = TestCommand.get_metadata()
264
- assert "params" in metadata
265
- assert "param1" in metadata["params"]
266
-
267
- def test_generate_examples(self):
268
- """Test _generate_examples method."""
269
- params = {
270
- "param1": {"type": "string", "description": "Test param"}
271
- }
272
-
273
- examples = TestCommand._generate_examples(params)
274
- assert isinstance(examples, list)
275
- assert len(examples) > 0
276
-
277
- def test_generate_examples_empty_params(self):
278
- """Test _generate_examples with empty params."""
279
- examples = TestCommand._generate_examples({})
280
- assert isinstance(examples, list)
281
-
282
- def test_get_param_info_with_annotations(self):
283
- """Test get_param_info with type annotations."""
284
- class AnnotatedCommand(Command):
285
- name = "annotated_command"
286
- result_class = MockResultClass
287
-
288
- async def execute(self, param1: str, param2: int = 42):
289
- return SuccessResult(data={"param1": param1, "param2": param2})
290
-
291
- param_info = AnnotatedCommand.get_param_info()
292
- assert "param1" in param_info
293
- assert "param2" in param_info
294
- assert param_info["param1"]["required"] is True
295
- assert param_info["param2"]["required"] is False
296
-
297
- def test_get_param_info_without_annotations(self):
298
- """Test get_param_info without type annotations."""
299
- class NoAnnotationCommand(Command):
300
- name = "no_annotation_command"
301
- result_class = MockResultClass
302
-
303
- async def execute(self, param1, param2=42):
304
- return SuccessResult(data={"param1": param1, "param2": param2})
305
-
306
- param_info = NoAnnotationCommand.get_param_info()
307
- assert "param1" in param_info
308
- assert "param2" in param_info
309
- assert param_info["param1"]["required"] is True
310
- assert param_info["param2"]["required"] is False
311
-
312
- def test_get_result_schema_with_result_class(self):
313
- """Test get_result_schema with result class."""
314
- class MockResultClassWithSchema:
315
- @classmethod
316
- def get_schema(cls):
317
- return {"type": "object", "properties": {"data": {"type": "string"}}}
318
-
319
- class CommandWithResultClass(Command):
320
- name = "command_with_result"
321
- result_class = MockResultClassWithSchema
322
-
323
- async def execute(self, **kwargs):
324
- return SuccessResult(data="test")
325
-
326
- schema = CommandWithResultClass.get_result_schema()
327
- assert schema["type"] == "object"
328
- assert "properties" in schema
329
-
330
- def test_get_result_schema_without_result_class(self):
331
- """Test get_result_schema without result class."""
332
- class CommandWithoutResultClass(Command):
333
- name = "command_without_result"
334
-
335
- async def execute(self, **kwargs):
336
- return SuccessResult(data="test")
337
-
338
- schema = CommandWithoutResultClass.get_result_schema()
339
- assert schema == {}
340
-
341
- def test_command_name_generation(self):
342
- """Test command name generation from class name."""
343
- class TestCommandName(Command):
344
- result_class = MockResultClass
345
-
346
- async def execute(self, **kwargs):
347
- return SuccessResult(data="test")
348
-
349
- # Test that name is generated from class name
350
- # The name will be generated dynamically in the run method
351
- # So we test that the class can be instantiated
352
- command = TestCommandName()
353
- assert command is not None
354
-
355
- def test_command_name_override(self):
356
- """Test command name override."""
357
- class CustomNamedCommand(Command):
358
- name = "custom_name"
359
- result_class = MockResultClass
360
-
361
- async def execute(self, **kwargs):
362
- return SuccessResult(data="test")
363
-
364
- assert CustomNamedCommand.name == "custom_name"
365
-
366
-
367
- class TestCommandEdgeCases:
368
- """Test edge cases for Command class."""
369
-
370
- def test_validate_params_with_various_none_values(self):
371
- """Test validate_params with various None-like values."""
372
- params = {
373
- "null_str": "null",
374
- "none_str": "none",
375
- "empty_str": "",
376
- "real_none": None,
377
- "valid_value": "test"
378
- }
379
-
380
- result = TestCommand.validate_params(params)
381
- assert "null_str" not in result
382
- assert "none_str" not in result
383
- assert "empty_str" not in result
384
- assert "real_none" not in result
385
- assert result["valid_value"] == "test"
386
-
387
- def test_validate_params_case_insensitive(self):
388
- """Test validate_params case insensitive handling."""
389
- params = {
390
- "null_upper": "NULL",
391
- "none_upper": "NONE",
392
- "valid_value": "test"
393
- }
394
-
395
- result = TestCommand.validate_params(params)
396
- assert "null_upper" not in result
397
- assert "none_upper" not in result
398
- assert result["valid_value"] == "test"
399
-
400
- @pytest.mark.asyncio
401
- async def test_run_with_complex_hook_context(self):
402
- """Test run method with complex hook context."""
403
- mock_hook_context = Mock()
404
- mock_hook_context.standard_processing = False
405
-
406
- with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
407
- mock_hooks.execute_before_hooks.return_value = mock_hook_context
408
-
409
- result = await TestCommand.run(complex_param={"nested": "value"})
410
- assert isinstance(result, SuccessResult)
411
-
412
- @pytest.mark.asyncio
413
- async def test_run_with_after_hooks(self):
414
- """Test run method with after hooks execution."""
415
- with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
416
- mock_registry.get_priority_command.return_value = TestCommand
417
- mock_registry.has_instance.return_value = False
418
-
419
- with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
420
- mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=True)
421
-
422
- result = await TestCommand.run(test_param="value")
423
-
424
- # Verify after hooks were called
425
- mock_hooks.execute_after_hooks.assert_called_once()
426
-
427
- def test_get_metadata_with_examples(self):
428
- """Test get_metadata method with examples generation."""
429
- with patch.object(TestCommand, 'get_param_info') as mock_param_info:
430
- mock_param_info.return_value = {
431
- "param1": {"type": "string", "description": "Test param"}
432
- }
433
-
434
- metadata = TestCommand.get_metadata()
435
- assert "examples" in metadata
436
- assert isinstance(metadata["examples"], list)