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,340 +0,0 @@
1
- """
2
- Tests for middleware components.
3
-
4
- These tests verify that middleware components work as expected.
5
- """
6
-
7
- import pytest
8
- from unittest.mock import patch, MagicMock, AsyncMock
9
- import json
10
- import time
11
-
12
- from fastapi import FastAPI, Request, Response
13
- from fastapi.testclient import TestClient
14
- from starlette.applications import Starlette
15
- from starlette.responses import JSONResponse
16
- from starlette.routing import Route
17
-
18
- from mcp_proxy_adapter.api.middleware.base import BaseMiddleware
19
- from mcp_proxy_adapter.api.middleware.logging import LoggingMiddleware
20
- from mcp_proxy_adapter.api.middleware.auth import AuthMiddleware
21
- from mcp_proxy_adapter.api.middleware.rate_limit import RateLimitMiddleware
22
- from mcp_proxy_adapter.api.middleware.error_handling import ErrorHandlingMiddleware
23
- from mcp_proxy_adapter.api.middleware.performance import PerformanceMiddleware
24
- from mcp_proxy_adapter.core.errors import MicroserviceError, CommandError, ValidationError, InvalidRequestError
25
-
26
-
27
- # Helper functions
28
- @pytest.mark.asyncio
29
- async def test_endpoint(request):
30
- """Test endpoint for middleware tests."""
31
- return Response(content="Test response", media_type="text/plain")
32
-
33
- @pytest.mark.asyncio
34
- async def error_endpoint(request):
35
- """Test endpoint that raises CommandError."""
36
- raise CommandError("Test error")
37
-
38
- @pytest.mark.asyncio
39
- async def validation_error_endpoint(request):
40
- """Test endpoint that raises ValidationError."""
41
- # Вместо создания pydantic-модели напрямую вызываем нашу ValidationError
42
- raise ValidationError("Validation error", data={"field": "error"})
43
-
44
- @pytest.mark.asyncio
45
- async def json_rpc_error_endpoint(request):
46
- """Test endpoint that raises InvalidRequestError."""
47
- # Возвращаем заранее сформированный JSON-RPC ответ с ошибкой
48
- return JSONResponse(
49
- status_code=400,
50
- content={
51
- "jsonrpc": "2.0",
52
- "error": {
53
- "code": -32000,
54
- "message": "Invalid JSON-RPC request",
55
- "data": {}
56
- },
57
- "id": 1
58
- }
59
- )
60
-
61
- # Test applications
62
- def create_test_app():
63
- """Create a test app with test endpoints."""
64
- app = FastAPI()
65
- app.add_route("/test", test_endpoint)
66
- app.add_route("/error", error_endpoint)
67
- app.add_route("/validation_error", validation_error_endpoint)
68
- app.add_route("/json_rpc_error", json_rpc_error_endpoint)
69
- # Добавим маршрут, имитирующий документацию
70
- @app.get("/docs")
71
- async def docs():
72
- return Response(content="API Documentation", media_type="text/plain")
73
- return app
74
-
75
-
76
- # Tests for BaseMiddleware
77
- def test_base_middleware():
78
- """Test that base middleware works correctly."""
79
- # Create a middleware that overrides methods
80
- class MockMiddleware(BaseMiddleware):
81
- async def before_request(self, request):
82
- request.state.before_called = True
83
-
84
- async def after_response(self, request, response):
85
- response.headers["X-After-Called"] = "True"
86
- return response
87
-
88
- # Create app with middleware
89
- app = create_test_app()
90
- app.add_middleware(MockMiddleware)
91
-
92
- # Test
93
- client = TestClient(app)
94
- response = client.get("/test")
95
-
96
- # Verify
97
- assert response.status_code == 200
98
- assert response.headers.get("X-After-Called") == "True"
99
-
100
-
101
- # Tests for LoggingMiddleware
102
- def test_logging_middleware():
103
- """Test that logging middleware logs requests and responses."""
104
- # Create app with middleware
105
- app = create_test_app()
106
- app.add_middleware(LoggingMiddleware)
107
-
108
- # Test
109
- with patch("mcp_proxy_adapter.api.middleware.logging.RequestLogger") as mock_request_logger:
110
- # Настраиваем мок для RequestLogger
111
- mock_logger_instance = MagicMock()
112
- mock_request_logger.return_value = mock_logger_instance
113
-
114
- client = TestClient(app)
115
- response = client.get("/test")
116
-
117
- # Verify
118
- assert response.status_code == 200
119
- assert "X-Request-ID" in response.headers
120
- assert "X-Process-Time" in response.headers
121
-
122
- # Check that RequestLogger was created and used
123
- mock_request_logger.assert_called_once()
124
- mock_logger_instance.info.assert_called()
125
-
126
-
127
- # Tests for AuthMiddleware
128
- def test_auth_middleware_no_api_key():
129
- """Test that auth middleware blocks requests without API key."""
130
- # Create app with middleware
131
- app = create_test_app()
132
- app.add_middleware(AuthMiddleware, api_keys={"valid-key": "test-user"}, auth_enabled=True)
133
-
134
- # Test
135
- client = TestClient(app)
136
- response = client.get("/test")
137
-
138
- # Verify
139
- assert response.status_code == 401
140
- assert "API key not provided" in response.text
141
-
142
-
143
- def test_auth_middleware_invalid_api_key():
144
- """Test that auth middleware blocks requests with invalid API key."""
145
- # Create app with middleware
146
- app = create_test_app()
147
- app.add_middleware(AuthMiddleware, api_keys={"valid-key": "test-user"}, auth_enabled=True)
148
-
149
- # Test
150
- client = TestClient(app)
151
- response = client.get("/test", headers={"X-API-Key": "invalid-key"})
152
-
153
- # Verify
154
- assert response.status_code == 401
155
- assert "Invalid API key" in response.text
156
-
157
-
158
- def test_auth_middleware_valid_api_key():
159
- """Test that auth middleware allows requests with valid API key."""
160
- # Create app with middleware
161
- app = create_test_app()
162
- app.add_middleware(AuthMiddleware, api_keys={"valid-key": "test-user"}, auth_enabled=True)
163
-
164
- # Test
165
- client = TestClient(app)
166
- response = client.get("/test", headers={"X-API-Key": "valid-key"})
167
-
168
- # Verify
169
- assert response.status_code == 200
170
-
171
-
172
- def test_auth_middleware_public_path():
173
- """Test that auth middleware allows requests to public paths."""
174
- # Create app with middleware
175
- app = create_test_app()
176
- app.add_middleware(AuthMiddleware, api_keys={"valid-key": "test-user"}, auth_enabled=True)
177
-
178
- # Test
179
- client = TestClient(app)
180
- response = client.get("/docs") # Public path
181
-
182
- # Verify
183
- assert response.status_code == 200 # Путь существует и должен быть доступен
184
-
185
-
186
- def test_auth_middleware_disabled():
187
- """Test that auth middleware passes requests when disabled."""
188
- # Create app with middleware but with auth_enabled=False
189
- app = create_test_app()
190
- app.add_middleware(AuthMiddleware, api_keys={"valid-key": "test-user"}, auth_enabled=False)
191
-
192
- # Test
193
- client = TestClient(app)
194
- response = client.get("/test") # No API key provided
195
-
196
- # Verify
197
- assert response.status_code == 200 # Should pass because auth is disabled
198
-
199
-
200
- # Tests for RateLimitMiddleware
201
- def test_rate_limit_middleware_exceeds_limit():
202
- """Test that rate limit middleware blocks requests when limit is exceeded."""
203
- # Create app with middleware (low limit for testing)
204
- app = create_test_app()
205
- app.add_middleware(RateLimitMiddleware, rate_limit=2, time_window=60)
206
-
207
- # Test
208
- client = TestClient(app)
209
-
210
- # First two requests should pass
211
- response1 = client.get("/test")
212
- response2 = client.get("/test")
213
-
214
- # Third request should be rate limited
215
- response3 = client.get("/test")
216
-
217
- # Verify
218
- assert response1.status_code == 200
219
- assert response2.status_code == 200
220
- assert response3.status_code == 429
221
- assert "Rate limit exceeded" in response3.text
222
-
223
-
224
- def test_rate_limit_middleware_public_path():
225
- """Test that rate limit middleware allows requests to public paths regardless of limit."""
226
- # Create app with middleware (low limit for testing)
227
- app = create_test_app()
228
- app.add_middleware(RateLimitMiddleware, rate_limit=1, time_window=60)
229
-
230
- # Test
231
- client = TestClient(app)
232
-
233
- # First request to normal path should pass
234
- response1 = client.get("/test")
235
-
236
- # Second request to normal path should be rate limited
237
- response2 = client.get("/test")
238
-
239
- # Request to public path should pass despite rate limit
240
- response3 = client.get("/health") # Public path
241
-
242
- # Verify
243
- assert response1.status_code == 200
244
- assert response2.status_code == 429
245
- assert response3.status_code == 404 # 404 because path doesn't exist, but rate limit should pass
246
-
247
-
248
- # Tests for ErrorHandlingMiddleware
249
- def test_error_handling_middleware_command_error():
250
- """Test that error handling middleware formats command errors correctly."""
251
- # Create app with middleware
252
- app = create_test_app()
253
- app.add_middleware(ErrorHandlingMiddleware)
254
-
255
- # Test
256
- client = TestClient(app)
257
- response = client.get("/error")
258
-
259
- # Verify
260
- assert response.status_code == 400 # ErrorHandlingMiddleware возвращает 400 для CommandError
261
- result = response.json()
262
- # В новом формате JSON-RPC мы возвращаем непосредственно объект с code и message
263
- assert "code" in result
264
- assert "message" in result
265
- assert result["code"] == -32000 # Код ошибки JSON-RPC
266
- assert result["message"] == "Test error"
267
-
268
-
269
- def test_error_handling_middleware_validation_error():
270
- """Test that error handling middleware formats validation errors correctly."""
271
- # Create app with middleware
272
- app = create_test_app()
273
- app.add_middleware(ErrorHandlingMiddleware)
274
-
275
- # Test
276
- client = TestClient(app)
277
- response = client.get("/validation_error")
278
-
279
- # Verify
280
- assert response.status_code == 400
281
- result = response.json()
282
- # В новом формате JSON-RPC мы возвращаем непосредственно объект с code и message
283
- assert "code" in result
284
- assert "message" in result
285
- assert "data" in result
286
- assert result["code"] == -32602 # Код InvalidParams JSON-RPC
287
- assert result["message"] == "Validation error"
288
- assert result["data"]["field"] == "error"
289
-
290
-
291
- def test_error_handling_middleware_jsonrpc_error():
292
- """Test that error handling middleware formats JSON-RPC errors correctly."""
293
- # Для этого теста мы используем прямой запрос к эндпоинту, который
294
- # возвращает заранее сформированный JSON-RPC ответ с ошибкой
295
- app = create_test_app()
296
- client = TestClient(app)
297
-
298
- # Выполняем запрос к JSON-RPC эндпоинту
299
- response = client.get("/json_rpc_error")
300
-
301
- # Verify
302
- assert response.status_code == 400
303
- assert response.json()["jsonrpc"] == "2.0"
304
- assert "error" in response.json()
305
- assert response.json()["error"]["code"] == -32000 # Обновленный код JSON-RPC
306
- assert response.json()["error"]["message"] == "Invalid JSON-RPC request"
307
- assert response.json()["error"]["data"] == {} # data вместо details
308
- assert response.json()["id"] == 1
309
-
310
-
311
- # Tests for PerformanceMiddleware
312
- @pytest.mark.asyncio
313
- async def test_performance_middleware():
314
- """Test that performance middleware tracks request times."""
315
- # Создаем middleware напрямую для тестирования
316
- middleware = PerformanceMiddleware(None)
317
-
318
- # Создаем мок для запроса
319
- mock_request = MagicMock()
320
- mock_request.url.path = "/test"
321
-
322
- # Создаем мок для call_next
323
- mock_response = JSONResponse({"message": "test"})
324
-
325
- async def mock_call_next(request):
326
- return mock_response
327
-
328
- # Симуляция нескольких запросов без использования кастомного event loop
329
- for _ in range(5):
330
- response = await middleware.dispatch(mock_request, mock_call_next)
331
- assert response == mock_response
332
-
333
- # Проверка, что времена запросов сохранены
334
- assert "/test" in middleware.request_times
335
- assert len(middleware.request_times["/test"]) == 5
336
-
337
- # Тестируем метод логирования статистики
338
- with patch("mcp_proxy_adapter.api.middleware.performance.logger") as mock_logger:
339
- middleware._log_stats()
340
- mock_logger.info.assert_called()