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.
- mcp_proxy_adapter/api/app.py +174 -80
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +7 -2
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +81 -21
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +9 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +21 -10
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {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()
|