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.
Files changed (84) hide show
  1. examples/basic_example/README.md +123 -9
  2. examples/basic_example/config.json +4 -0
  3. examples/basic_example/docs/EN/README.md +46 -5
  4. examples/basic_example/docs/RU/README.md +46 -5
  5. examples/basic_example/server.py +127 -21
  6. examples/complete_example/commands/system_command.py +1 -0
  7. examples/complete_example/server.py +65 -11
  8. examples/minimal_example/README.md +20 -6
  9. examples/minimal_example/config.json +7 -14
  10. examples/minimal_example/main.py +109 -40
  11. examples/minimal_example/simple_server.py +53 -14
  12. examples/minimal_example/tests/conftest.py +1 -1
  13. examples/minimal_example/tests/test_integration.py +8 -10
  14. examples/simple_server.py +12 -21
  15. examples/test_server.py +22 -14
  16. examples/tool_description_example.py +82 -0
  17. mcp_proxy_adapter/api/__init__.py +0 -0
  18. mcp_proxy_adapter/api/app.py +391 -0
  19. mcp_proxy_adapter/api/handlers.py +229 -0
  20. mcp_proxy_adapter/api/middleware/__init__.py +49 -0
  21. mcp_proxy_adapter/api/middleware/auth.py +146 -0
  22. mcp_proxy_adapter/api/middleware/base.py +79 -0
  23. mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
  24. mcp_proxy_adapter/api/middleware/logging.py +96 -0
  25. mcp_proxy_adapter/api/middleware/performance.py +83 -0
  26. mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
  27. mcp_proxy_adapter/api/schemas.py +305 -0
  28. mcp_proxy_adapter/api/tool_integration.py +223 -0
  29. mcp_proxy_adapter/api/tools.py +198 -0
  30. mcp_proxy_adapter/commands/__init__.py +19 -0
  31. mcp_proxy_adapter/commands/base.py +301 -0
  32. mcp_proxy_adapter/commands/command_registry.py +231 -0
  33. mcp_proxy_adapter/commands/config_command.py +113 -0
  34. mcp_proxy_adapter/commands/health_command.py +136 -0
  35. mcp_proxy_adapter/commands/help_command.py +193 -0
  36. mcp_proxy_adapter/commands/result.py +215 -0
  37. mcp_proxy_adapter/config.py +9 -0
  38. mcp_proxy_adapter/core/__init__.py +0 -0
  39. mcp_proxy_adapter/core/errors.py +173 -0
  40. mcp_proxy_adapter/core/logging.py +205 -0
  41. mcp_proxy_adapter/core/utils.py +138 -0
  42. mcp_proxy_adapter/py.typed +0 -0
  43. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  44. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  45. mcp_proxy_adapter/tests/__init__.py +0 -0
  46. mcp_proxy_adapter/tests/api/__init__.py +3 -0
  47. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
  48. mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
  49. mcp_proxy_adapter/tests/commands/__init__.py +3 -0
  50. mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
  51. mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
  52. mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
  53. mcp_proxy_adapter/tests/conftest.py +131 -0
  54. mcp_proxy_adapter/tests/functional/__init__.py +3 -0
  55. mcp_proxy_adapter/tests/functional/test_api.py +235 -0
  56. mcp_proxy_adapter/tests/integration/__init__.py +3 -0
  57. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
  58. mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
  59. mcp_proxy_adapter/tests/performance/__init__.py +3 -0
  60. mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
  61. mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
  62. mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
  63. mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
  64. mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
  65. mcp_proxy_adapter/tests/test_base_command.py +123 -0
  66. mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
  67. mcp_proxy_adapter/tests/test_command_registry.py +245 -0
  68. mcp_proxy_adapter/tests/test_config.py +127 -0
  69. mcp_proxy_adapter/tests/test_utils.py +65 -0
  70. mcp_proxy_adapter/tests/unit/__init__.py +3 -0
  71. mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
  72. mcp_proxy_adapter/tests/unit/test_config.py +217 -0
  73. mcp_proxy_adapter/version.py +1 -1
  74. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/METADATA +1 -1
  75. mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
  76. examples/basic_example/config.yaml +0 -20
  77. examples/basic_example/main.py +0 -50
  78. examples/complete_example/main.py +0 -67
  79. examples/minimal_example/config.yaml +0 -26
  80. mcp_proxy_adapter/framework.py +0 -109
  81. mcp_proxy_adapter-3.0.0.dist-info/RECORD +0 -58
  82. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
  83. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
  84. {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,3 @@
1
+ """
2
+ Functional tests for the mcp_microservice package.
3
+ """
@@ -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,3 @@
1
+ """
2
+ Integration tests for the mcp_microservice package.
3
+ """
@@ -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"]