mcp-proxy-adapter 3.0.0__py3-none-any.whl → 3.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.
- examples/basic_example/README.md +123 -9
- examples/basic_example/config.json +4 -0
- examples/basic_example/docs/EN/README.md +46 -5
- examples/basic_example/docs/RU/README.md +46 -5
- examples/basic_example/server.py +127 -21
- examples/complete_example/commands/system_command.py +1 -0
- examples/complete_example/server.py +65 -11
- examples/minimal_example/README.md +20 -6
- examples/minimal_example/config.json +7 -14
- examples/minimal_example/main.py +109 -40
- examples/minimal_example/simple_server.py +53 -14
- examples/minimal_example/tests/conftest.py +1 -1
- examples/minimal_example/tests/test_integration.py +8 -10
- examples/simple_server.py +12 -21
- examples/test_server.py +22 -14
- examples/tool_description_example.py +82 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +391 -0
- mcp_proxy_adapter/api/handlers.py +229 -0
- mcp_proxy_adapter/api/middleware/__init__.py +49 -0
- mcp_proxy_adapter/api/middleware/auth.py +146 -0
- mcp_proxy_adapter/api/middleware/base.py +79 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
- mcp_proxy_adapter/api/middleware/logging.py +96 -0
- mcp_proxy_adapter/api/middleware/performance.py +83 -0
- mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
- mcp_proxy_adapter/api/schemas.py +305 -0
- mcp_proxy_adapter/api/tool_integration.py +223 -0
- mcp_proxy_adapter/api/tools.py +198 -0
- mcp_proxy_adapter/commands/__init__.py +19 -0
- mcp_proxy_adapter/commands/base.py +301 -0
- mcp_proxy_adapter/commands/command_registry.py +231 -0
- mcp_proxy_adapter/commands/config_command.py +113 -0
- mcp_proxy_adapter/commands/health_command.py +136 -0
- mcp_proxy_adapter/commands/help_command.py +193 -0
- mcp_proxy_adapter/commands/result.py +215 -0
- mcp_proxy_adapter/config.py +9 -0
- mcp_proxy_adapter/core/__init__.py +0 -0
- mcp_proxy_adapter/core/errors.py +173 -0
- mcp_proxy_adapter/core/logging.py +205 -0
- mcp_proxy_adapter/core/utils.py +138 -0
- mcp_proxy_adapter/py.typed +0 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +3 -0
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
- mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
- mcp_proxy_adapter/tests/commands/__init__.py +3 -0
- mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
- mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
- mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
- mcp_proxy_adapter/tests/conftest.py +131 -0
- mcp_proxy_adapter/tests/functional/__init__.py +3 -0
- mcp_proxy_adapter/tests/functional/test_api.py +235 -0
- mcp_proxy_adapter/tests/integration/__init__.py +3 -0
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
- mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
- mcp_proxy_adapter/tests/performance/__init__.py +3 -0
- mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
- mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
- mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
- mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
- mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
- mcp_proxy_adapter/tests/test_base_command.py +123 -0
- mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
- mcp_proxy_adapter/tests/test_command_registry.py +245 -0
- mcp_proxy_adapter/tests/test_config.py +127 -0
- mcp_proxy_adapter/tests/test_utils.py +65 -0
- mcp_proxy_adapter/tests/unit/__init__.py +3 -0
- mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
- mcp_proxy_adapter/tests/unit/test_config.py +217 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
- examples/basic_example/config.yaml +0 -20
- examples/basic_example/main.py +0 -50
- examples/complete_example/main.py +0 -67
- examples/minimal_example/config.yaml +0 -26
- mcp_proxy_adapter/framework.py +0 -109
- mcp_proxy_adapter-3.0.0.dist-info/RECORD +0 -58
- {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
"""
|
2
|
+
Tests for the help command.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from unittest.mock import patch, MagicMock
|
7
|
+
|
8
|
+
from mcp_proxy_adapter.commands.help_command import HelpCommand, HelpResult
|
9
|
+
from mcp_proxy_adapter.core.errors import NotFoundError
|
10
|
+
|
11
|
+
|
12
|
+
@pytest.fixture
|
13
|
+
def mock_registry():
|
14
|
+
"""Mock for command registry."""
|
15
|
+
with patch("mcp_proxy_adapter.commands.help_command.registry") as mock_reg:
|
16
|
+
yield mock_reg
|
17
|
+
|
18
|
+
|
19
|
+
async def test_help_command_without_params(mock_registry):
|
20
|
+
"""Test help command without parameters."""
|
21
|
+
# Setup mocks
|
22
|
+
mock_registry.get_all_metadata.return_value = {
|
23
|
+
"help": {
|
24
|
+
"name": "help",
|
25
|
+
"summary": "Get help information",
|
26
|
+
"description": "Get help information",
|
27
|
+
"params": {},
|
28
|
+
"examples": []
|
29
|
+
},
|
30
|
+
"health": {
|
31
|
+
"name": "health",
|
32
|
+
"summary": "Check server health",
|
33
|
+
"description": "Check server health",
|
34
|
+
"params": {},
|
35
|
+
"examples": []
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
# Execute command
|
40
|
+
command = HelpCommand()
|
41
|
+
result = await command.execute()
|
42
|
+
|
43
|
+
# Check result
|
44
|
+
assert isinstance(result, HelpResult)
|
45
|
+
assert result.commands_info is not None
|
46
|
+
assert result.command_info is None
|
47
|
+
|
48
|
+
# Check content
|
49
|
+
commands_dict = result.to_dict()
|
50
|
+
assert "commands" in commands_dict
|
51
|
+
assert "help" in commands_dict["commands"]
|
52
|
+
assert "health" in commands_dict["commands"]
|
53
|
+
assert "summary" in commands_dict["commands"]["help"]
|
54
|
+
assert "Get help information" in commands_dict["commands"]["help"]["summary"]
|
55
|
+
|
56
|
+
|
57
|
+
async def test_help_command_with_cmdname(mock_registry):
|
58
|
+
"""Test help command with cmdname parameter."""
|
59
|
+
# Setup mocks
|
60
|
+
mock_registry.get_command_metadata.return_value = {
|
61
|
+
"name": "health",
|
62
|
+
"description": "Check server health",
|
63
|
+
"summary": "Check server health",
|
64
|
+
"params": {
|
65
|
+
"check_type": {
|
66
|
+
"type": "string",
|
67
|
+
"description": "Type of health check",
|
68
|
+
"required": False,
|
69
|
+
"default": "basic"
|
70
|
+
}
|
71
|
+
},
|
72
|
+
"examples": []
|
73
|
+
}
|
74
|
+
|
75
|
+
# Execute command
|
76
|
+
command = HelpCommand()
|
77
|
+
result = await command.execute(cmdname="health")
|
78
|
+
|
79
|
+
# Check result
|
80
|
+
assert isinstance(result, HelpResult)
|
81
|
+
assert result.commands_info is None
|
82
|
+
assert result.command_info is not None
|
83
|
+
|
84
|
+
# Check content
|
85
|
+
command_dict = result.to_dict()
|
86
|
+
assert "cmdname" in command_dict
|
87
|
+
assert command_dict["cmdname"] == "health"
|
88
|
+
assert "info" in command_dict
|
89
|
+
assert "description" in command_dict["info"]
|
90
|
+
assert "Check server health" in command_dict["info"]["description"]
|
91
|
+
assert "params" in command_dict["info"]
|
92
|
+
assert "check_type" in command_dict["info"]["params"]
|
93
|
+
|
94
|
+
|
95
|
+
async def test_help_command_with_invalid_cmdname(mock_registry):
|
96
|
+
"""Test help command with invalid cmdname parameter."""
|
97
|
+
# Setup mocks
|
98
|
+
mock_registry.get_command_metadata.side_effect = NotFoundError("Command not found")
|
99
|
+
|
100
|
+
# Execute command and check exception
|
101
|
+
command = HelpCommand()
|
102
|
+
with pytest.raises(NotFoundError) as excinfo:
|
103
|
+
await command.execute(cmdname="non_existent")
|
104
|
+
|
105
|
+
assert "not found" in str(excinfo.value)
|
106
|
+
|
107
|
+
|
108
|
+
def test_help_result_schema():
|
109
|
+
"""Test help command result schema."""
|
110
|
+
schema = HelpResult.get_schema()
|
111
|
+
|
112
|
+
assert schema["type"] == "object"
|
113
|
+
assert "oneOf" in schema
|
114
|
+
assert len(schema["oneOf"]) == 2
|
115
|
+
|
116
|
+
commands_schema = schema["oneOf"][0]
|
117
|
+
assert "properties" in commands_schema
|
118
|
+
assert "commands" in commands_schema["properties"]
|
119
|
+
|
120
|
+
command_schema = schema["oneOf"][1]
|
121
|
+
assert "properties" in command_schema
|
122
|
+
assert "cmdname" in command_schema["properties"]
|
123
|
+
assert "info" in command_schema["properties"]
|
124
|
+
|
125
|
+
|
126
|
+
def test_help_command_schema():
|
127
|
+
"""Test help command schema."""
|
128
|
+
schema = HelpCommand.get_schema()
|
129
|
+
|
130
|
+
assert schema["type"] == "object"
|
131
|
+
assert "properties" in schema
|
132
|
+
assert "cmdname" in schema["properties"]
|
133
|
+
assert schema["properties"]["cmdname"]["type"] == "string"
|
@@ -0,0 +1,131 @@
|
|
1
|
+
"""
|
2
|
+
Module with fixtures and configuration for tests.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import json
|
6
|
+
import os
|
7
|
+
import tempfile
|
8
|
+
from typing import Any, Dict, Generator
|
9
|
+
|
10
|
+
import pytest
|
11
|
+
from fastapi.testclient import TestClient
|
12
|
+
|
13
|
+
from mcp_proxy_adapter.api.app import create_app
|
14
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
15
|
+
from mcp_proxy_adapter.config import Config
|
16
|
+
|
17
|
+
|
18
|
+
@pytest.fixture
|
19
|
+
def temp_config_file() -> Generator[str, None, None]:
|
20
|
+
"""
|
21
|
+
Creates temporary configuration file for tests.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
Path to temporary configuration file.
|
25
|
+
"""
|
26
|
+
# Create temporary file
|
27
|
+
fd, path = tempfile.mkstemp(suffix=".json")
|
28
|
+
|
29
|
+
# Write test configuration
|
30
|
+
test_config = {
|
31
|
+
"server": {
|
32
|
+
"host": "127.0.0.1",
|
33
|
+
"port": 8888
|
34
|
+
},
|
35
|
+
"logging": {
|
36
|
+
"level": "DEBUG",
|
37
|
+
"file": None
|
38
|
+
},
|
39
|
+
# Отключаем аутентификацию и ограничение скорости для тестов
|
40
|
+
"auth_enabled": False,
|
41
|
+
"rate_limit_enabled": False
|
42
|
+
}
|
43
|
+
|
44
|
+
with os.fdopen(fd, "w") as f:
|
45
|
+
json.dump(test_config, f)
|
46
|
+
|
47
|
+
yield path
|
48
|
+
|
49
|
+
# Remove temporary file after tests
|
50
|
+
os.unlink(path)
|
51
|
+
|
52
|
+
|
53
|
+
@pytest.fixture
|
54
|
+
def test_config(temp_config_file: str) -> Config:
|
55
|
+
"""
|
56
|
+
Creates test configuration instance.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
temp_config_file: Path to temporary configuration file.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
Test configuration instance.
|
63
|
+
"""
|
64
|
+
return Config(temp_config_file)
|
65
|
+
|
66
|
+
|
67
|
+
@pytest.fixture
|
68
|
+
def test_client() -> TestClient:
|
69
|
+
"""
|
70
|
+
Creates test client for FastAPI application.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
FastAPI test client.
|
74
|
+
"""
|
75
|
+
app = create_app()
|
76
|
+
return TestClient(app)
|
77
|
+
|
78
|
+
|
79
|
+
@pytest.fixture
|
80
|
+
def clean_registry() -> Generator[None, None, None]:
|
81
|
+
"""
|
82
|
+
Cleans command registry before test and restores it after.
|
83
|
+
|
84
|
+
Yields:
|
85
|
+
None
|
86
|
+
"""
|
87
|
+
# Save current commands
|
88
|
+
original_commands = dict(registry._commands)
|
89
|
+
|
90
|
+
# Clear registry
|
91
|
+
registry.clear()
|
92
|
+
|
93
|
+
yield
|
94
|
+
|
95
|
+
# Restore registry
|
96
|
+
registry.clear()
|
97
|
+
for name, command in original_commands.items():
|
98
|
+
registry._commands[name] = command
|
99
|
+
|
100
|
+
|
101
|
+
@pytest.fixture
|
102
|
+
def json_rpc_request() -> Dict[str, Any]:
|
103
|
+
"""
|
104
|
+
Creates base JSON-RPC request.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
Dictionary with JSON-RPC request data.
|
108
|
+
"""
|
109
|
+
return {
|
110
|
+
"jsonrpc": "2.0",
|
111
|
+
"method": "test_command",
|
112
|
+
"params": {},
|
113
|
+
"id": "test-id"
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
@pytest.fixture(autouse=True)
|
118
|
+
def register_test_commands():
|
119
|
+
"""
|
120
|
+
Регистрирует тестовые команды в registry для всех тестов.
|
121
|
+
"""
|
122
|
+
from mcp_proxy_adapter.tests.stubs.echo_command import EchoCommand
|
123
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
124
|
+
|
125
|
+
# Регистрируем команды для тестирования
|
126
|
+
registry.register(EchoCommand)
|
127
|
+
|
128
|
+
yield
|
129
|
+
|
130
|
+
# Очищаем registry после тестов
|
131
|
+
registry.clear()
|
@@ -0,0 +1,235 @@
|
|
1
|
+
"""
|
2
|
+
Functional tests for the API.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from typing import Dict, Any
|
7
|
+
|
8
|
+
from fastapi.testclient import TestClient
|
9
|
+
|
10
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
11
|
+
from mcp_proxy_adapter.tests.stubs.echo_command import EchoCommand
|
12
|
+
|
13
|
+
|
14
|
+
@pytest.fixture
|
15
|
+
def register_echo_command(clean_registry):
|
16
|
+
"""
|
17
|
+
Fixture to register the Echo command for testing.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
clean_registry: Fixture to clean registry before and after test.
|
21
|
+
"""
|
22
|
+
registry.register(EchoCommand)
|
23
|
+
yield
|
24
|
+
registry.clear()
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.mark.functional
|
28
|
+
def test_execute_command(test_client: TestClient, json_rpc_request: Dict[str, Any], register_echo_command):
|
29
|
+
"""
|
30
|
+
Test execution of command via API.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
test_client: FastAPI test client.
|
34
|
+
json_rpc_request: Base JSON-RPC request.
|
35
|
+
register_echo_command: Fixture to register test command.
|
36
|
+
"""
|
37
|
+
# Create JSON-RPC request
|
38
|
+
request_data = json_rpc_request.copy()
|
39
|
+
request_data["method"] = "echo"
|
40
|
+
request_data["params"] = {"test_key": "test_value"}
|
41
|
+
|
42
|
+
# Send request
|
43
|
+
response = test_client.post("/api/jsonrpc", json=request_data)
|
44
|
+
|
45
|
+
# Check response
|
46
|
+
assert response.status_code == 200
|
47
|
+
assert response.headers["content-type"] == "application/json"
|
48
|
+
|
49
|
+
# Check response structure
|
50
|
+
data = response.json()
|
51
|
+
assert "jsonrpc" in data and data["jsonrpc"] == "2.0"
|
52
|
+
assert "result" in data
|
53
|
+
assert "id" in data and data["id"] == request_data["id"]
|
54
|
+
|
55
|
+
# Check result content
|
56
|
+
assert "params" in data["result"]
|
57
|
+
assert data["result"]["params"] == {"test_key": "test_value"}
|
58
|
+
|
59
|
+
|
60
|
+
@pytest.mark.functional
|
61
|
+
def test_execute_nonexistent_command(test_client: TestClient, json_rpc_request: Dict[str, Any]):
|
62
|
+
"""
|
63
|
+
Test execution of nonexistent command.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
test_client: FastAPI test client.
|
67
|
+
json_rpc_request: Base JSON-RPC request.
|
68
|
+
"""
|
69
|
+
# Create JSON-RPC request with nonexistent command
|
70
|
+
request_data = json_rpc_request.copy()
|
71
|
+
request_data["method"] = "nonexistent_command"
|
72
|
+
|
73
|
+
# Send request
|
74
|
+
response = test_client.post("/api/jsonrpc", json=request_data)
|
75
|
+
|
76
|
+
# Check response
|
77
|
+
assert response.status_code == 400 or response.status_code == 200
|
78
|
+
|
79
|
+
# Check response structure
|
80
|
+
data = response.json()
|
81
|
+
assert "jsonrpc" in data and data["jsonrpc"] == "2.0"
|
82
|
+
assert "error" in data
|
83
|
+
assert "id" in data and data["id"] == request_data["id"]
|
84
|
+
|
85
|
+
# Check error content
|
86
|
+
assert "code" in data["error"]
|
87
|
+
assert "message" in data["error"]
|
88
|
+
assert data["error"]["code"] == -32601 # Method not found
|
89
|
+
assert "not found" in data["error"]["message"].lower()
|
90
|
+
|
91
|
+
|
92
|
+
@pytest.mark.functional
|
93
|
+
def test_invalid_json_rpc_request(test_client: TestClient):
|
94
|
+
"""
|
95
|
+
Test invalid JSON-RPC request.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
test_client: FastAPI test client.
|
99
|
+
"""
|
100
|
+
# Create invalid JSON-RPC request
|
101
|
+
request_data = {
|
102
|
+
"method": "echo",
|
103
|
+
"params": {}
|
104
|
+
# Missing jsonrpc and id fields
|
105
|
+
}
|
106
|
+
|
107
|
+
# Send request
|
108
|
+
response = test_client.post("/api/jsonrpc", json=request_data)
|
109
|
+
|
110
|
+
# Check response
|
111
|
+
assert response.status_code == 400 or response.status_code == 200
|
112
|
+
|
113
|
+
# Check response structure
|
114
|
+
data = response.json()
|
115
|
+
assert "jsonrpc" in data and data["jsonrpc"] == "2.0"
|
116
|
+
assert "error" in data
|
117
|
+
|
118
|
+
# Check error content
|
119
|
+
assert "code" in data["error"]
|
120
|
+
assert "message" in data["error"]
|
121
|
+
assert data["error"]["code"] == -32600 # Invalid Request
|
122
|
+
|
123
|
+
|
124
|
+
@pytest.mark.functional
|
125
|
+
def test_get_commands(test_client: TestClient, register_echo_command):
|
126
|
+
"""
|
127
|
+
Test getting list of commands.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
test_client: FastAPI test client.
|
131
|
+
register_echo_command: Fixture to register test command.
|
132
|
+
"""
|
133
|
+
# Send request
|
134
|
+
response = test_client.get("/api/commands")
|
135
|
+
|
136
|
+
# Check response
|
137
|
+
assert response.status_code == 200
|
138
|
+
|
139
|
+
# Check response structure
|
140
|
+
data = response.json()
|
141
|
+
assert "commands" in data
|
142
|
+
assert isinstance(data["commands"], dict)
|
143
|
+
|
144
|
+
# Check that echo command is in the list
|
145
|
+
assert "echo" in data["commands"]
|
146
|
+
assert "description" in data["commands"]["echo"]
|
147
|
+
assert "schema" in data["commands"]["echo"]
|
148
|
+
|
149
|
+
|
150
|
+
@pytest.mark.functional
|
151
|
+
def test_health_check(test_client: TestClient):
|
152
|
+
"""
|
153
|
+
Test health check endpoint.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
test_client: FastAPI test client.
|
157
|
+
"""
|
158
|
+
# Send request
|
159
|
+
response = test_client.get("/health")
|
160
|
+
|
161
|
+
# Check response
|
162
|
+
assert response.status_code == 200
|
163
|
+
|
164
|
+
# Check response structure
|
165
|
+
data = response.json()
|
166
|
+
assert "status" in data
|
167
|
+
assert data["status"] == "ok"
|
168
|
+
|
169
|
+
|
170
|
+
@pytest.mark.functional
|
171
|
+
def test_openapi_schema(test_client: TestClient):
|
172
|
+
"""
|
173
|
+
Test OpenAPI schema endpoint.
|
174
|
+
|
175
|
+
Args:
|
176
|
+
test_client: FastAPI test client.
|
177
|
+
"""
|
178
|
+
# Send request
|
179
|
+
response = test_client.get("/openapi.json")
|
180
|
+
|
181
|
+
# Check response
|
182
|
+
assert response.status_code == 200
|
183
|
+
|
184
|
+
# Check response content
|
185
|
+
data = response.json()
|
186
|
+
assert "openapi" in data
|
187
|
+
assert "info" in data
|
188
|
+
assert "paths" in data
|
189
|
+
|
190
|
+
# Check that API endpoints are in schema
|
191
|
+
assert "/cmd" in data["paths"]
|
192
|
+
assert "/api/commands" in data["paths"]
|
193
|
+
assert "/health" in data["paths"]
|
194
|
+
|
195
|
+
|
196
|
+
@pytest.mark.functional
|
197
|
+
def test_batch_requests(test_client: TestClient, json_rpc_request: Dict[str, Any], register_echo_command):
|
198
|
+
"""
|
199
|
+
Test batch requests processing.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
test_client: FastAPI test client.
|
203
|
+
json_rpc_request: Base JSON-RPC request.
|
204
|
+
register_echo_command: Fixture to register test command.
|
205
|
+
"""
|
206
|
+
# Create batch request
|
207
|
+
request1 = json_rpc_request.copy()
|
208
|
+
request1["method"] = "echo"
|
209
|
+
request1["params"] = {"request_id": "1"}
|
210
|
+
request1["id"] = "1"
|
211
|
+
|
212
|
+
request2 = json_rpc_request.copy()
|
213
|
+
request2["method"] = "echo"
|
214
|
+
request2["params"] = {"request_id": "2"}
|
215
|
+
request2["id"] = "2"
|
216
|
+
|
217
|
+
batch_request = [request1, request2]
|
218
|
+
|
219
|
+
# Send request
|
220
|
+
response = test_client.post("/api/jsonrpc", json=batch_request)
|
221
|
+
|
222
|
+
# Check response
|
223
|
+
assert response.status_code == 200
|
224
|
+
|
225
|
+
# Check response structure
|
226
|
+
data = response.json()
|
227
|
+
assert isinstance(data, list)
|
228
|
+
assert len(data) == 2
|
229
|
+
|
230
|
+
# Check individual responses
|
231
|
+
assert data[0]["result"]["params"] == {"request_id": "1"}
|
232
|
+
assert data[0]["id"] == "1"
|
233
|
+
|
234
|
+
assert data[1]["result"]["params"] == {"request_id": "2"}
|
235
|
+
assert data[1]["id"] == "2"
|
@@ -0,0 +1,130 @@
|
|
1
|
+
"""
|
2
|
+
Integration tests for /cmd endpoint and help command.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from fastapi.testclient import TestClient
|
7
|
+
|
8
|
+
from mcp_proxy_adapter.api.app import app
|
9
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
10
|
+
from mcp_proxy_adapter.commands.help_command import HelpCommand
|
11
|
+
|
12
|
+
|
13
|
+
@pytest.fixture(autouse=True)
|
14
|
+
def setup_registry():
|
15
|
+
"""Setup command registry for tests."""
|
16
|
+
# Store original commands
|
17
|
+
original_commands = dict(registry._commands)
|
18
|
+
|
19
|
+
# Clear registry
|
20
|
+
registry._commands.clear()
|
21
|
+
|
22
|
+
# Register help command
|
23
|
+
registry.register(HelpCommand)
|
24
|
+
|
25
|
+
yield
|
26
|
+
|
27
|
+
# Restore original commands
|
28
|
+
registry._commands.clear()
|
29
|
+
for name, command in original_commands.items():
|
30
|
+
registry._commands[name] = command
|
31
|
+
|
32
|
+
|
33
|
+
@pytest.fixture
|
34
|
+
def client():
|
35
|
+
"""Test client for FastAPI app."""
|
36
|
+
return TestClient(app)
|
37
|
+
|
38
|
+
|
39
|
+
def test_cmd_help_without_params(client):
|
40
|
+
"""Test /cmd endpoint with help command without parameters."""
|
41
|
+
response = client.post(
|
42
|
+
"/cmd",
|
43
|
+
json={"command": "help"}
|
44
|
+
)
|
45
|
+
|
46
|
+
assert response.status_code == 200
|
47
|
+
assert "result" in response.json()
|
48
|
+
result = response.json()["result"]
|
49
|
+
|
50
|
+
assert "commands" in result
|
51
|
+
assert "help" in result["commands"]
|
52
|
+
assert "summary" in result["commands"]["help"]
|
53
|
+
|
54
|
+
|
55
|
+
def test_cmd_help_with_cmdname(client):
|
56
|
+
"""Test /cmd endpoint with help command with cmdname parameter."""
|
57
|
+
response = client.post(
|
58
|
+
"/cmd",
|
59
|
+
json={
|
60
|
+
"command": "help",
|
61
|
+
"params": {
|
62
|
+
"cmdname": "help"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
)
|
66
|
+
|
67
|
+
assert response.status_code == 200
|
68
|
+
assert "result" in response.json()
|
69
|
+
result = response.json()["result"]
|
70
|
+
|
71
|
+
assert "cmdname" in result
|
72
|
+
assert result["cmdname"] == "help"
|
73
|
+
assert "info" in result
|
74
|
+
assert "description" in result["info"]
|
75
|
+
assert "params" in result["info"]
|
76
|
+
assert "cmdname" in result["info"]["params"]
|
77
|
+
|
78
|
+
|
79
|
+
def test_cmd_help_unknown_command(client):
|
80
|
+
"""Test /cmd endpoint with help command for unknown command."""
|
81
|
+
response = client.post(
|
82
|
+
"/cmd",
|
83
|
+
json={
|
84
|
+
"command": "help",
|
85
|
+
"params": {
|
86
|
+
"cmdname": "unknown_command"
|
87
|
+
}
|
88
|
+
}
|
89
|
+
)
|
90
|
+
|
91
|
+
assert response.status_code == 200
|
92
|
+
assert "result" in response.json()
|
93
|
+
result = response.json()["result"]
|
94
|
+
|
95
|
+
assert "success" in result and result["success"] is False
|
96
|
+
assert "error" in result
|
97
|
+
error = result["error"]
|
98
|
+
|
99
|
+
assert error["code"] == -32601
|
100
|
+
assert "not found" in error["message"]
|
101
|
+
|
102
|
+
|
103
|
+
def test_cmd_unknown_command(client):
|
104
|
+
"""Test /cmd endpoint with unknown command."""
|
105
|
+
response = client.post(
|
106
|
+
"/cmd",
|
107
|
+
json={"command": "unknown_command"}
|
108
|
+
)
|
109
|
+
|
110
|
+
assert response.status_code == 200
|
111
|
+
assert "error" in response.json()
|
112
|
+
error = response.json()["error"]
|
113
|
+
|
114
|
+
assert error["code"] == -32601
|
115
|
+
assert "не найдена" in error["message"]
|
116
|
+
|
117
|
+
|
118
|
+
def test_cmd_invalid_request(client):
|
119
|
+
"""Test /cmd endpoint with invalid request format."""
|
120
|
+
response = client.post(
|
121
|
+
"/cmd",
|
122
|
+
json={"invalid": "request"}
|
123
|
+
)
|
124
|
+
|
125
|
+
assert response.status_code == 200
|
126
|
+
assert "error" in response.json()
|
127
|
+
error = response.json()["error"]
|
128
|
+
|
129
|
+
assert error["code"] == -32600
|
130
|
+
assert "Отсутствует обязательное поле 'command'" in error["message"]
|