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