mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +7 -0
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,546 +0,0 @@
1
- """
2
- Tests for schemas module.
3
-
4
- This module contains comprehensive tests for API schema definitions
5
- to ensure 90%+ code coverage.
6
- """
7
-
8
- import pytest
9
- import json
10
- from typing import Dict, Any
11
-
12
- from mcp_proxy_adapter.api.schemas import (
13
- ErrorResponse, ErrorWrapper, JsonRpcRequest, JsonRpcError,
14
- JsonRpcSuccessResponse, JsonRpcErrorResponse, CommandResponse,
15
- HealthResponse, CommandListResponse, CommandRequest,
16
- CommandSuccessResponse, CommandErrorResponse, APIToolDescription
17
- )
18
-
19
-
20
- class TestErrorResponse:
21
- """Test cases for ErrorResponse model."""
22
-
23
- def test_error_response_creation(self):
24
- """Test ErrorResponse creation with all fields."""
25
- error = ErrorResponse(
26
- code=404,
27
- message="Not found",
28
- details={"resource": "user", "id": 123}
29
- )
30
-
31
- assert error.code == 404
32
- assert error.message == "Not found"
33
- assert error.details == {"resource": "user", "id": 123}
34
-
35
- def test_error_response_creation_without_details(self):
36
- """Test ErrorResponse creation without optional details."""
37
- error = ErrorResponse(code=500, message="Internal server error")
38
-
39
- assert error.code == 500
40
- assert error.message == "Internal server error"
41
- assert error.details is None
42
-
43
- def test_error_response_to_dict(self):
44
- """Test ErrorResponse serialization to dict."""
45
- error = ErrorResponse(
46
- code=400,
47
- message="Bad request",
48
- details={"field": "email"}
49
- )
50
-
51
- error_dict = error.model_dump()
52
- assert error_dict["code"] == 400
53
- assert error_dict["message"] == "Bad request"
54
- assert error_dict["details"] == {"field": "email"}
55
-
56
-
57
- class TestErrorWrapper:
58
- """Test cases for ErrorWrapper model."""
59
-
60
- def test_error_wrapper_creation(self):
61
- """Test ErrorWrapper creation."""
62
- error_response = ErrorResponse(code=404, message="Not found")
63
- wrapper = ErrorWrapper(error=error_response)
64
-
65
- assert wrapper.error == error_response
66
- assert wrapper.error.code == 404
67
-
68
- def test_error_wrapper_to_dict(self):
69
- """Test ErrorWrapper serialization to dict."""
70
- error_response = ErrorResponse(code=500, message="Server error")
71
- wrapper = ErrorWrapper(error=error_response)
72
-
73
- wrapper_dict = wrapper.model_dump()
74
- assert "error" in wrapper_dict
75
- assert wrapper_dict["error"]["code"] == 500
76
-
77
-
78
- class TestJsonRpcRequest:
79
- """Test cases for JsonRpcRequest model."""
80
-
81
- def test_json_rpc_request_creation(self):
82
- """Test JsonRpcRequest creation with all fields."""
83
- request = JsonRpcRequest(
84
- method="test_method",
85
- params={"param1": "value1", "param2": 42},
86
- id="request_123"
87
- )
88
-
89
- assert request.jsonrpc == "2.0"
90
- assert request.method == "test_method"
91
- assert request.params == {"param1": "value1", "param2": 42}
92
- assert request.id == "request_123"
93
-
94
- def test_json_rpc_request_creation_without_params(self):
95
- """Test JsonRpcRequest creation without params."""
96
- request = JsonRpcRequest(method="simple_method", id=1)
97
-
98
- assert request.method == "simple_method"
99
- assert request.params is None
100
- assert request.id == 1
101
-
102
- def test_json_rpc_request_creation_with_list_params(self):
103
- """Test JsonRpcRequest creation with list params."""
104
- request = JsonRpcRequest(
105
- method="list_method",
106
- params=["item1", "item2", "item3"],
107
- id="list_request"
108
- )
109
-
110
- assert request.params == ["item1", "item2", "item3"]
111
-
112
- def test_json_rpc_request_to_dict(self):
113
- """Test JsonRpcRequest serialization to dict."""
114
- request = JsonRpcRequest(
115
- method="test_method",
116
- params={"test": "value"},
117
- id="test_id"
118
- )
119
-
120
- request_dict = request.model_dump()
121
- assert request_dict["jsonrpc"] == "2.0"
122
- assert request_dict["method"] == "test_method"
123
- assert request_dict["params"] == {"test": "value"}
124
- assert request_dict["id"] == "test_id"
125
-
126
-
127
- class TestJsonRpcError:
128
- """Test cases for JsonRpcError model."""
129
-
130
- def test_json_rpc_error_creation(self):
131
- """Test JsonRpcError creation with all fields."""
132
- error = JsonRpcError(
133
- code=-32601,
134
- message="Method not found",
135
- data={"available_methods": ["help", "config"]}
136
- )
137
-
138
- assert error.code == -32601
139
- assert error.message == "Method not found"
140
- assert error.data == {"available_methods": ["help", "config"]}
141
-
142
- def test_json_rpc_error_creation_without_data(self):
143
- """Test JsonRpcError creation without optional data."""
144
- error = JsonRpcError(code=-32700, message="Parse error")
145
-
146
- assert error.code == -32700
147
- assert error.message == "Parse error"
148
- assert error.data is None
149
-
150
- def test_json_rpc_error_to_dict(self):
151
- """Test JsonRpcError serialization to dict."""
152
- error = JsonRpcError(
153
- code=-32602,
154
- message="Invalid params",
155
- data={"expected": "string", "received": "number"}
156
- )
157
-
158
- error_dict = error.model_dump()
159
- assert error_dict["code"] == -32602
160
- assert error_dict["message"] == "Invalid params"
161
- assert error_dict["data"] == {"expected": "string", "received": "number"}
162
-
163
-
164
- class TestJsonRpcSuccessResponse:
165
- """Test cases for JsonRpcSuccessResponse model."""
166
-
167
- def test_json_rpc_success_response_creation(self):
168
- """Test JsonRpcSuccessResponse creation."""
169
- response = JsonRpcSuccessResponse(
170
- result={"status": "success", "data": "test_data"},
171
- id="response_123"
172
- )
173
-
174
- assert response.jsonrpc == "2.0"
175
- assert response.result == {"status": "success", "data": "test_data"}
176
- assert response.id == "response_123"
177
-
178
- def test_json_rpc_success_response_to_dict(self):
179
- """Test JsonRpcSuccessResponse serialization to dict."""
180
- response = JsonRpcSuccessResponse(
181
- result={"message": "OK"},
182
- id=1
183
- )
184
-
185
- response_dict = response.model_dump()
186
- assert response_dict["jsonrpc"] == "2.0"
187
- assert response_dict["result"] == {"message": "OK"}
188
- assert response_dict["id"] == 1
189
-
190
-
191
- class TestJsonRpcErrorResponse:
192
- """Test cases for JsonRpcErrorResponse model."""
193
-
194
- def test_json_rpc_error_response_creation(self):
195
- """Test JsonRpcErrorResponse creation."""
196
- error = JsonRpcError(code=-32601, message="Method not found")
197
- response = JsonRpcErrorResponse(error=error, id="error_response")
198
-
199
- assert response.jsonrpc == "2.0"
200
- assert response.error == error
201
- assert response.id == "error_response"
202
-
203
- def test_json_rpc_error_response_to_dict(self):
204
- """Test JsonRpcErrorResponse serialization to dict."""
205
- error = JsonRpcError(code=-32700, message="Parse error")
206
- response = JsonRpcErrorResponse(error=error, id=1)
207
-
208
- response_dict = response.model_dump()
209
- assert response_dict["jsonrpc"] == "2.0"
210
- assert response_dict["error"]["code"] == -32700
211
- assert response_dict["error"]["message"] == "Parse error"
212
- assert response_dict["id"] == 1
213
-
214
-
215
- class TestCommandResponse:
216
- """Test cases for CommandResponse model."""
217
-
218
- def test_command_response_success(self):
219
- """Test CommandResponse creation for success case."""
220
- response = CommandResponse(
221
- success=True,
222
- data={"result": "command executed"},
223
- message="Command completed successfully"
224
- )
225
-
226
- assert response.success is True
227
- assert response.data == {"result": "command executed"}
228
- assert response.message == "Command completed successfully"
229
- assert response.error is None
230
-
231
- def test_command_response_error(self):
232
- """Test CommandResponse creation for error case."""
233
- error = ErrorResponse(code=400, message="Bad request")
234
- response = CommandResponse(
235
- success=False,
236
- error=error,
237
- message="Command failed"
238
- )
239
-
240
- assert response.success is False
241
- assert response.data is None
242
- assert response.message == "Command failed"
243
- assert response.error == error
244
-
245
- def test_command_response_to_dict(self):
246
- """Test CommandResponse serialization to dict."""
247
- response = CommandResponse(
248
- success=True,
249
- data={"status": "ok"},
250
- message="Success"
251
- )
252
-
253
- response_dict = response.model_dump()
254
- assert response_dict["success"] is True
255
- assert response_dict["data"] == {"status": "ok"}
256
- assert response_dict["message"] == "Success"
257
- assert response_dict["error"] is None
258
-
259
-
260
- class TestHealthResponse:
261
- """Test cases for HealthResponse model."""
262
-
263
- def test_health_response_creation(self):
264
- """Test HealthResponse creation."""
265
- response = HealthResponse(
266
- status="healthy",
267
- version="1.0.0",
268
- uptime=3600.5,
269
- components={"database": "ok", "cache": "ok"}
270
- )
271
-
272
- assert response.status == "healthy"
273
- assert response.version == "1.0.0"
274
- assert response.uptime == 3600.5
275
- assert response.components == {"database": "ok", "cache": "ok"}
276
-
277
- def test_health_response_to_dict(self):
278
- """Test HealthResponse serialization to dict."""
279
- response = HealthResponse(
280
- status="degraded",
281
- version="2.1.0",
282
- uptime=7200.0,
283
- components={"database": "ok", "cache": "error"}
284
- )
285
-
286
- response_dict = response.model_dump()
287
- assert response_dict["status"] == "degraded"
288
- assert response_dict["version"] == "2.1.0"
289
- assert response_dict["uptime"] == 7200.0
290
- assert response_dict["components"] == {"database": "ok", "cache": "error"}
291
-
292
-
293
- class TestCommandListResponse:
294
- """Test cases for CommandListResponse model."""
295
-
296
- def test_command_list_response_creation(self):
297
- """Test CommandListResponse creation."""
298
- commands = {
299
- "help": {"summary": "Show help", "params": {}},
300
- "config": {"summary": "Get config", "params": {"section": "string"}}
301
- }
302
- response = CommandListResponse(commands=commands)
303
-
304
- assert response.commands == commands
305
- assert len(response.commands) == 2
306
-
307
- def test_command_list_response_to_dict(self):
308
- """Test CommandListResponse serialization to dict."""
309
- commands = {"test": {"summary": "Test command"}}
310
- response = CommandListResponse(commands=commands)
311
-
312
- response_dict = response.model_dump()
313
- assert response_dict["commands"] == commands
314
-
315
-
316
- class TestCommandRequest:
317
- """Test cases for CommandRequest model."""
318
-
319
- def test_command_request_creation(self):
320
- """Test CommandRequest creation with params."""
321
- request = CommandRequest(
322
- command="test_command",
323
- params={"param1": "value1", "param2": 42}
324
- )
325
-
326
- assert request.command == "test_command"
327
- assert request.params == {"param1": "value1", "param2": 42}
328
-
329
- def test_command_request_creation_without_params(self):
330
- """Test CommandRequest creation without params."""
331
- request = CommandRequest(command="simple_command")
332
-
333
- assert request.command == "simple_command"
334
- assert request.params == {}
335
-
336
- def test_command_request_to_dict(self):
337
- """Test CommandRequest serialization to dict."""
338
- request = CommandRequest(
339
- command="test_command",
340
- params={"test": "value"}
341
- )
342
-
343
- request_dict = request.model_dump()
344
- assert request_dict["command"] == "test_command"
345
- assert request_dict["params"] == {"test": "value"}
346
-
347
-
348
- class TestCommandSuccessResponse:
349
- """Test cases for CommandSuccessResponse model."""
350
-
351
- def test_command_success_response_creation(self):
352
- """Test CommandSuccessResponse creation."""
353
- response = CommandSuccessResponse(
354
- result={"status": "success", "data": "test_data"}
355
- )
356
-
357
- assert response.result == {"status": "success", "data": "test_data"}
358
-
359
- def test_command_success_response_with_id(self):
360
- """Test CommandSuccessResponse creation with id."""
361
- response = CommandSuccessResponse(
362
- result={"message": "OK"}
363
- )
364
-
365
- assert response.result == {"message": "OK"}
366
-
367
- def test_command_success_response_to_dict(self):
368
- """Test CommandSuccessResponse serialization to dict."""
369
- response = CommandSuccessResponse(
370
- result={"data": "test"}
371
- )
372
-
373
- response_dict = response.model_dump()
374
- assert response_dict["result"] == {"data": "test"}
375
-
376
-
377
- class TestCommandErrorResponse:
378
- """Test cases for CommandErrorResponse model."""
379
-
380
- def test_command_error_response_creation(self):
381
- """Test CommandErrorResponse creation."""
382
- error = JsonRpcError(code=-32601, message="Method not found")
383
- response = CommandErrorResponse(error=error)
384
-
385
- assert response.error == error
386
-
387
- def test_command_error_response_with_id(self):
388
- """Test CommandErrorResponse creation with id."""
389
- error = JsonRpcError(code=-32700, message="Parse error")
390
- response = CommandErrorResponse(error=error)
391
-
392
- assert response.error == error
393
-
394
- def test_command_error_response_to_dict(self):
395
- """Test CommandErrorResponse serialization to dict."""
396
- error = JsonRpcError(code=-32602, message="Invalid params")
397
- response = CommandErrorResponse(error=error)
398
-
399
- response_dict = response.model_dump()
400
- assert response_dict["error"]["code"] == -32602
401
- assert response_dict["error"]["message"] == "Invalid params"
402
-
403
-
404
- class TestAPIToolDescription:
405
- """Test cases for APIToolDescription class."""
406
-
407
- @pytest.fixture
408
- def mock_registry(self):
409
- """Create a mock command registry for testing."""
410
- from unittest.mock import Mock
411
-
412
- registry = Mock()
413
- registry.get_all_metadata.return_value = {
414
- "help": {
415
- "summary": "Show help information",
416
- "description": "Display help for commands",
417
- "params": {
418
- "command": {
419
- "type": "строка",
420
- "description": "Command name",
421
- "required": False
422
- }
423
- },
424
- "examples": [
425
- {
426
- "command": "help",
427
- "params": {"command": "config"}
428
- }
429
- ]
430
- },
431
- "config": {
432
- "summary": "Get configuration",
433
- "description": "Retrieve configuration settings",
434
- "params": {
435
- "section": {
436
- "type": "строка",
437
- "description": "Configuration section",
438
- "required": True
439
- }
440
- },
441
- "examples": [
442
- {
443
- "command": "config",
444
- "params": {"section": "database"}
445
- }
446
- ]
447
- }
448
- }
449
-
450
- return registry
451
-
452
- def test_generate_tool_description(self, mock_registry):
453
- """Test tool description generation."""
454
- tool_name = "test_tool"
455
-
456
- description = APIToolDescription.generate_tool_description(tool_name, mock_registry)
457
-
458
- assert description["name"] == tool_name
459
- assert "description" in description
460
- assert "supported_commands" in description
461
- assert "examples" in description
462
- assert "help" in description["supported_commands"]
463
- assert "config" in description["supported_commands"]
464
-
465
- def test_generate_tool_description_text(self, mock_registry):
466
- """Test tool description text generation."""
467
- tool_name = "test_tool"
468
-
469
- text = APIToolDescription.generate_tool_description_text(tool_name, mock_registry)
470
-
471
- assert isinstance(text, str)
472
- assert tool_name in text
473
- assert "help" in text
474
- assert "config" in text
475
-
476
- def test_simplify_type_conversion(self):
477
- """Test type simplification for various input types."""
478
- # Test string types
479
- assert APIToolDescription._simplify_type("str") == "строка"
480
- assert APIToolDescription._simplify_type("int") == "целое число"
481
- assert APIToolDescription._simplify_type("float") == "число"
482
- assert APIToolDescription._simplify_type("bool") == "логическое значение"
483
- assert APIToolDescription._simplify_type("List") == "список"
484
- assert APIToolDescription._simplify_type("Dict") == "объект"
485
-
486
- # Test unknown type
487
- assert APIToolDescription._simplify_type("неизвестный") == "значение"
488
-
489
- def test_extract_param_description(self):
490
- """Test parameter description extraction."""
491
- doc_string = """
492
- Test command.
493
-
494
- Args:
495
- param1: First parameter description
496
- param2: Second parameter description
497
- """
498
-
499
- description = APIToolDescription._extract_param_description(doc_string, "param1")
500
- assert "First parameter description" in description
501
-
502
- description = APIToolDescription._extract_param_description(doc_string, "param2")
503
- assert "Second parameter description" in description
504
-
505
- # Test non-existent parameter
506
- description = APIToolDescription._extract_param_description(doc_string, "nonexistent")
507
- assert description == ""
508
-
509
- def test_extract_param_description_no_args_section(self):
510
- """Test parameter description extraction without Args section."""
511
- doc_string = "Simple command without Args section."
512
-
513
- description = APIToolDescription._extract_param_description(doc_string, "param")
514
- assert description == ""
515
-
516
- def test_extract_param_description_empty_docstring(self):
517
- """Test parameter description extraction with empty docstring."""
518
- description = APIToolDescription._extract_param_description("", "param")
519
- assert description == ""
520
-
521
- def test_generate_tool_description_empty_registry(self, mock_registry):
522
- """Test tool description generation with empty registry."""
523
- mock_registry.get_all_metadata.return_value = {}
524
-
525
- description = APIToolDescription.generate_tool_description("empty_tool", mock_registry)
526
-
527
- assert description["name"] == "empty_tool"
528
- assert description["supported_commands"] == {}
529
- assert description["examples"] == []
530
-
531
- def test_generate_tool_description_with_missing_fields(self, mock_registry):
532
- """Test tool description generation with missing metadata fields."""
533
- mock_registry.get_all_metadata.return_value = {
534
- "incomplete": {
535
- "summary": "Incomplete command",
536
- "description": "Incomplete command description",
537
- "params": {}
538
- # Missing examples
539
- }
540
- }
541
-
542
- description = APIToolDescription.generate_tool_description("incomplete_tool", mock_registry)
543
-
544
- assert "incomplete" in description["supported_commands"]
545
- # Should handle missing fields gracefully
546
- assert description["supported_commands"]["incomplete"]["summary"] == "Incomplete command"