mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.1__py3-none-any.whl

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