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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +7 -0
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,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()