mcp-proxy-adapter 2.1.17__py3-none-any.whl → 3.0.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.
- examples/__init__.py +19 -0
- examples/anti_patterns/README.md +51 -0
- examples/anti_patterns/__init__.py +9 -0
- examples/anti_patterns/bad_design/README.md +72 -0
- examples/anti_patterns/bad_design/global_state.py +170 -0
- examples/anti_patterns/bad_design/monolithic_command.py +272 -0
- examples/basic_example/README.md +131 -0
- examples/basic_example/__init__.py +8 -0
- examples/basic_example/commands/__init__.py +5 -0
- examples/basic_example/commands/echo_command.py +95 -0
- examples/basic_example/commands/math_command.py +151 -0
- examples/basic_example/commands/time_command.py +152 -0
- examples/basic_example/config.json +21 -0
- examples/basic_example/config.yaml +20 -0
- examples/basic_example/docs/EN/README.md +136 -0
- examples/basic_example/docs/RU/README.md +136 -0
- examples/basic_example/main.py +50 -0
- examples/basic_example/server.py +45 -0
- examples/basic_example/tests/conftest.py +243 -0
- examples/commands/echo_command.py +52 -0
- examples/commands/echo_result.py +65 -0
- examples/commands/get_date_command.py +98 -0
- examples/commands/new_uuid4_command.py +91 -0
- examples/complete_example/Dockerfile +24 -0
- examples/complete_example/README.md +92 -0
- examples/complete_example/__init__.py +8 -0
- examples/complete_example/commands/__init__.py +5 -0
- examples/complete_example/commands/system_command.py +327 -0
- examples/complete_example/config.json +41 -0
- examples/complete_example/configs/config.dev.yaml +40 -0
- examples/complete_example/configs/config.docker.yaml +40 -0
- examples/complete_example/docker-compose.yml +35 -0
- examples/complete_example/main.py +67 -0
- examples/complete_example/requirements.txt +20 -0
- examples/complete_example/server.py +85 -0
- examples/minimal_example/README.md +51 -0
- examples/minimal_example/__init__.py +8 -0
- examples/minimal_example/config.json +21 -0
- examples/minimal_example/config.yaml +26 -0
- examples/minimal_example/main.py +67 -0
- examples/minimal_example/simple_server.py +124 -0
- examples/minimal_example/tests/conftest.py +171 -0
- examples/minimal_example/tests/test_hello_command.py +111 -0
- examples/minimal_example/tests/test_integration.py +183 -0
- examples/server.py +69 -0
- examples/simple_server.py +137 -0
- examples/test_server.py +126 -0
- mcp_proxy_adapter/__init__.py +33 -1
- mcp_proxy_adapter/config.py +186 -0
- mcp_proxy_adapter/custom_openapi.py +125 -0
- mcp_proxy_adapter/framework.py +109 -0
- mcp_proxy_adapter/openapi.py +403 -0
- mcp_proxy_adapter/version.py +3 -0
- mcp_proxy_adapter-3.0.0.dist-info/METADATA +200 -0
- mcp_proxy_adapter-3.0.0.dist-info/RECORD +58 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/top_level.txt +1 -0
- mcp_proxy_adapter/adapter.py +0 -697
- mcp_proxy_adapter/analyzers/__init__.py +0 -1
- mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
- mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
- mcp_proxy_adapter/dispatchers/__init__.py +0 -1
- mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
- mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -262
- mcp_proxy_adapter/examples/analyze_config.py +0 -141
- mcp_proxy_adapter/examples/basic_integration.py +0 -155
- mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
- mcp_proxy_adapter/examples/extension_example.py +0 -72
- mcp_proxy_adapter/examples/help_best_practices.py +0 -67
- mcp_proxy_adapter/examples/help_usage.py +0 -64
- mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
- mcp_proxy_adapter/examples/openapi_server.py +0 -383
- mcp_proxy_adapter/examples/project_structure_example.py +0 -47
- mcp_proxy_adapter/examples/testing_example.py +0 -64
- mcp_proxy_adapter/models.py +0 -47
- mcp_proxy_adapter/registry.py +0 -439
- mcp_proxy_adapter/schema.py +0 -257
- mcp_proxy_adapter/testing_utils.py +0 -112
- mcp_proxy_adapter/validators/__init__.py +0 -1
- mcp_proxy_adapter/validators/docstring_validator.py +0 -75
- mcp_proxy_adapter/validators/metadata_validator.py +0 -76
- mcp_proxy_adapter-2.1.17.dist-info/METADATA +0 -376
- mcp_proxy_adapter-2.1.17.dist-info/RECORD +0 -30
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# Минимальный пример MCP Microservice
|
2
|
+
|
3
|
+
Данный пример демонстрирует минимальную конфигурацию для запуска микросервиса с одной простой командой.
|
4
|
+
|
5
|
+
## Структура примера
|
6
|
+
|
7
|
+
```
|
8
|
+
minimal_example/
|
9
|
+
├── config.yaml # Файл конфигурации
|
10
|
+
├── README.md # Документация
|
11
|
+
└── simple_server.py # Пример сервера с одной командой
|
12
|
+
```
|
13
|
+
|
14
|
+
## Запуск примера
|
15
|
+
|
16
|
+
```bash
|
17
|
+
# Перейти в директорию проекта
|
18
|
+
cd examples/minimal_example
|
19
|
+
|
20
|
+
# Запустить сервер
|
21
|
+
python simple_server.py
|
22
|
+
```
|
23
|
+
|
24
|
+
После запуска сервер будет доступен по адресу [http://localhost:8000](http://localhost:8000).
|
25
|
+
|
26
|
+
## Тестирование API
|
27
|
+
|
28
|
+
### Через веб-интерфейс
|
29
|
+
|
30
|
+
Откройте в браузере [http://localhost:8000/docs](http://localhost:8000/docs) для доступа к интерактивной документации Swagger UI.
|
31
|
+
|
32
|
+
### Через командную строку
|
33
|
+
|
34
|
+
```bash
|
35
|
+
# Вызов команды hello через JSON-RPC
|
36
|
+
curl -X POST "http://localhost:8000/api/jsonrpc" \
|
37
|
+
-H "Content-Type: application/json" \
|
38
|
+
-d '{"jsonrpc": "2.0", "method": "hello", "params": {"name": "User"}, "id": 1}'
|
39
|
+
|
40
|
+
# Вызов команды hello через упрощенный эндпоинт
|
41
|
+
curl -X POST "http://localhost:8000/cmd" \
|
42
|
+
-H "Content-Type: application/json" \
|
43
|
+
-d '{"command": "hello", "params": {"name": "User"}}'
|
44
|
+
```
|
45
|
+
|
46
|
+
## Что демонстрирует этот пример
|
47
|
+
|
48
|
+
1. Создание минимального микросервиса
|
49
|
+
2. Определение простой команды
|
50
|
+
3. Основные эндпоинты API
|
51
|
+
4. Работа с JSON-RPC
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"service": {
|
3
|
+
"name": "Minimal Example",
|
4
|
+
"version": "1.0.0"
|
5
|
+
},
|
6
|
+
"server": {
|
7
|
+
"host": "0.0.0.0",
|
8
|
+
"port": 8000,
|
9
|
+
"debug": true,
|
10
|
+
"log_level": "info"
|
11
|
+
},
|
12
|
+
"logging": {
|
13
|
+
"level": "INFO",
|
14
|
+
"file": "logs/minimal_example.log",
|
15
|
+
"rotation": {
|
16
|
+
"type": "size",
|
17
|
+
"max_bytes": 10485760,
|
18
|
+
"backup_count": 5
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Minimal Example Configuration
|
2
|
+
|
3
|
+
service:
|
4
|
+
name: "Minimal Example"
|
5
|
+
version: "1.0.0"
|
6
|
+
|
7
|
+
server:
|
8
|
+
host: "0.0.0.0"
|
9
|
+
port: 8000
|
10
|
+
debug: true
|
11
|
+
log_level: "info"
|
12
|
+
|
13
|
+
# Uncomment and configure this section if you need authentication
|
14
|
+
# auth:
|
15
|
+
# enabled: false
|
16
|
+
# secret_key: ""
|
17
|
+
# token_expiration: 3600 # seconds
|
18
|
+
|
19
|
+
# Настройки логирования
|
20
|
+
logging:
|
21
|
+
level: "INFO" # Уровень логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
22
|
+
file: "logs/minimal_example.log" # Файл для записи логов
|
23
|
+
rotation:
|
24
|
+
type: "size" # Тип ротации логов (size, time)
|
25
|
+
max_bytes: 10485760 # Максимальный размер файла лога (10 МБ)
|
26
|
+
backup_count: 5 # Количество резервных копий
|
@@ -0,0 +1,67 @@
|
|
1
|
+
"""
|
2
|
+
Main application module.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
import uvicorn
|
8
|
+
import logging
|
9
|
+
|
10
|
+
from mcp_proxy_adapter.api.app import app
|
11
|
+
from mcp_proxy_adapter.config import config
|
12
|
+
from mcp_proxy_adapter.core.logging import logger, setup_logging
|
13
|
+
|
14
|
+
|
15
|
+
def main():
|
16
|
+
"""
|
17
|
+
Main function to run the application.
|
18
|
+
"""
|
19
|
+
# Убедимся что логирование настроено
|
20
|
+
logger.info("Initializing logging configuration")
|
21
|
+
|
22
|
+
try:
|
23
|
+
# Получаем настройки логирования
|
24
|
+
log_level = config.get("logging.level", "INFO")
|
25
|
+
log_file = config.get("logging.file")
|
26
|
+
rotation_type = config.get("logging.rotation.type", "size")
|
27
|
+
|
28
|
+
# Выводим информацию о настройках логирования
|
29
|
+
logger.info(f"Log level: {log_level}")
|
30
|
+
if log_file:
|
31
|
+
logger.info(f"Log file: {log_file}")
|
32
|
+
logger.info(f"Log rotation type: {rotation_type}")
|
33
|
+
|
34
|
+
if rotation_type.lower() == "time":
|
35
|
+
when = config.get("logging.rotation.when", "D")
|
36
|
+
interval = config.get("logging.rotation.interval", 1)
|
37
|
+
logger.info(f"Log rotation: every {interval} {when}")
|
38
|
+
else:
|
39
|
+
max_bytes = config.get("logging.rotation.max_bytes", 10 * 1024 * 1024)
|
40
|
+
logger.info(f"Log rotation: when size reaches {max_bytes / (1024*1024):.1f} MB")
|
41
|
+
|
42
|
+
backup_count = config.get("logging.rotation.backup_count", 5)
|
43
|
+
logger.info(f"Log backups: {backup_count}")
|
44
|
+
else:
|
45
|
+
logger.info("File logging is disabled")
|
46
|
+
|
47
|
+
# Get server settings
|
48
|
+
host = config.get("server.host", "0.0.0.0")
|
49
|
+
port = config.get("server.port", 8000)
|
50
|
+
|
51
|
+
logger.info(f"Starting server on {host}:{port}")
|
52
|
+
|
53
|
+
# Run server
|
54
|
+
uvicorn.run(
|
55
|
+
app,
|
56
|
+
host=host,
|
57
|
+
port=port,
|
58
|
+
reload=True if os.environ.get("DEBUG") else False,
|
59
|
+
log_level=log_level.lower()
|
60
|
+
)
|
61
|
+
except Exception as e:
|
62
|
+
logger.exception(f"Error during application startup: {e}")
|
63
|
+
sys.exit(1)
|
64
|
+
|
65
|
+
|
66
|
+
if __name__ == "__main__":
|
67
|
+
main()
|
@@ -0,0 +1,124 @@
|
|
1
|
+
"""
|
2
|
+
Минимальный пример использования MCP Microservice.
|
3
|
+
|
4
|
+
Этот пример демонстрирует минимальную конфигурацию для запуска микросервиса
|
5
|
+
с одной простой командой.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
from typing import Dict, Any
|
10
|
+
|
11
|
+
import mcp_proxy_adapter as mcp
|
12
|
+
from mcp_proxy_adapter import Command, SuccessResult
|
13
|
+
|
14
|
+
|
15
|
+
class HelloResult(SuccessResult):
|
16
|
+
"""
|
17
|
+
Результат выполнения команды hello.
|
18
|
+
|
19
|
+
Атрибуты:
|
20
|
+
message (str): Приветственное сообщение
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, message: str):
|
24
|
+
"""
|
25
|
+
Инициализация результата команды.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
message: Приветственное сообщение
|
29
|
+
"""
|
30
|
+
self.message = message
|
31
|
+
|
32
|
+
def to_dict(self) -> Dict[str, Any]:
|
33
|
+
"""
|
34
|
+
Преобразование результата в словарь.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
Словарь с результатом выполнения команды
|
38
|
+
"""
|
39
|
+
return {"message": self.message}
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def get_schema(cls) -> Dict[str, Any]:
|
43
|
+
"""
|
44
|
+
Получение JSON-схемы для результата.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
JSON-схема результата
|
48
|
+
"""
|
49
|
+
return {
|
50
|
+
"type": "object",
|
51
|
+
"properties": {
|
52
|
+
"message": {"type": "string", "description": "Приветственное сообщение"}
|
53
|
+
},
|
54
|
+
"required": ["message"]
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
class HelloCommand(Command):
|
59
|
+
"""
|
60
|
+
Команда, возвращающая приветственное сообщение.
|
61
|
+
|
62
|
+
Эта команда принимает имя пользователя и возвращает приветственное сообщение.
|
63
|
+
"""
|
64
|
+
|
65
|
+
# Имя команды для регистрации
|
66
|
+
name = "hello"
|
67
|
+
# Класс результата команды
|
68
|
+
result_class = HelloResult
|
69
|
+
|
70
|
+
async def execute(self, name: str = "World") -> HelloResult:
|
71
|
+
"""
|
72
|
+
Выполнение команды.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
name: Имя для приветствия (по умолчанию "World")
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
Результат выполнения команды с приветственным сообщением
|
79
|
+
"""
|
80
|
+
return HelloResult(f"Hello, {name}!")
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
def get_schema(cls) -> Dict[str, Any]:
|
84
|
+
"""
|
85
|
+
Получение JSON-схемы для параметров команды.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
JSON-схема параметров команды
|
89
|
+
"""
|
90
|
+
return {
|
91
|
+
"type": "object",
|
92
|
+
"properties": {
|
93
|
+
"name": {
|
94
|
+
"type": "string",
|
95
|
+
"description": "Имя для приветствия"
|
96
|
+
}
|
97
|
+
},
|
98
|
+
"additionalProperties": False
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
def main():
|
103
|
+
"""Запуск микросервиса."""
|
104
|
+
# Определяем путь к конфигурационному файлу
|
105
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
106
|
+
config_path = os.path.join(current_dir, "config.yaml")
|
107
|
+
|
108
|
+
# Создаем микросервис
|
109
|
+
service = mcp.MicroService(
|
110
|
+
title="Minimal Example",
|
111
|
+
description="Минимальный пример использования MCP Microservice",
|
112
|
+
version="1.0.0",
|
113
|
+
config_path=config_path
|
114
|
+
)
|
115
|
+
|
116
|
+
# Регистрируем команду
|
117
|
+
service.register_command(HelloCommand)
|
118
|
+
|
119
|
+
# Запускаем сервер
|
120
|
+
service.run(reload=True)
|
121
|
+
|
122
|
+
|
123
|
+
if __name__ == "__main__":
|
124
|
+
main()
|
@@ -0,0 +1,171 @@
|
|
1
|
+
"""
|
2
|
+
Pytest configuration and fixtures for minimal example tests.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
import time
|
8
|
+
import socket
|
9
|
+
import asyncio
|
10
|
+
import threading
|
11
|
+
import multiprocessing
|
12
|
+
from typing import Callable, Tuple, Dict, Any
|
13
|
+
|
14
|
+
import pytest
|
15
|
+
import requests
|
16
|
+
|
17
|
+
# Add parent directory to path for imports
|
18
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
19
|
+
|
20
|
+
# Import the server module from the parent directory
|
21
|
+
import simple_server
|
22
|
+
|
23
|
+
|
24
|
+
def find_free_port() -> int:
|
25
|
+
"""
|
26
|
+
Find a free port on localhost.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
Free port number
|
30
|
+
"""
|
31
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
32
|
+
sock.bind(('localhost', 0))
|
33
|
+
return sock.getsockname()[1]
|
34
|
+
|
35
|
+
|
36
|
+
class ServerProcess:
|
37
|
+
"""Helper class to manage server process."""
|
38
|
+
|
39
|
+
def __init__(self, port: int):
|
40
|
+
"""
|
41
|
+
Initialize server process with a specified port.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
port: Port number to use
|
45
|
+
"""
|
46
|
+
self.port = port
|
47
|
+
self.process = None
|
48
|
+
|
49
|
+
def start(self) -> None:
|
50
|
+
"""Start the server in a separate process."""
|
51
|
+
def run_server():
|
52
|
+
# Mock the configuration to use the test port
|
53
|
+
os.environ["TEST_SERVER_PORT"] = str(self.port)
|
54
|
+
simple_server.main()
|
55
|
+
|
56
|
+
self.process = multiprocessing.Process(target=run_server)
|
57
|
+
self.process.daemon = True
|
58
|
+
self.process.start()
|
59
|
+
|
60
|
+
# Wait for server to start
|
61
|
+
self._wait_for_server()
|
62
|
+
|
63
|
+
def stop(self) -> None:
|
64
|
+
"""Stop the server process."""
|
65
|
+
if self.process and self.process.is_alive():
|
66
|
+
self.process.terminate()
|
67
|
+
self.process.join(timeout=2)
|
68
|
+
|
69
|
+
def _wait_for_server(self, max_attempts: int = 10) -> None:
|
70
|
+
"""
|
71
|
+
Wait for the server to become available.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
max_attempts: Maximum number of connection attempts
|
75
|
+
"""
|
76
|
+
for i in range(max_attempts):
|
77
|
+
try:
|
78
|
+
response = requests.get(f"http://localhost:{self.port}/health")
|
79
|
+
if response.status_code == 200:
|
80
|
+
return
|
81
|
+
except requests.ConnectionError:
|
82
|
+
pass
|
83
|
+
|
84
|
+
time.sleep(0.5)
|
85
|
+
|
86
|
+
raise TimeoutError(f"Server did not start within {max_attempts * 0.5} seconds")
|
87
|
+
|
88
|
+
|
89
|
+
@pytest.fixture
|
90
|
+
def server_port() -> int:
|
91
|
+
"""
|
92
|
+
Fixture that provides a free port for the test server.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
Port number
|
96
|
+
"""
|
97
|
+
return find_free_port()
|
98
|
+
|
99
|
+
|
100
|
+
@pytest.fixture
|
101
|
+
def server(server_port: int) -> ServerProcess:
|
102
|
+
"""
|
103
|
+
Fixture that provides a running server instance.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
server_port: Port to run the server on
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
Server process object
|
110
|
+
"""
|
111
|
+
server_process = ServerProcess(server_port)
|
112
|
+
server_process.start()
|
113
|
+
|
114
|
+
yield server_process
|
115
|
+
|
116
|
+
server_process.stop()
|
117
|
+
|
118
|
+
|
119
|
+
@pytest.fixture
|
120
|
+
def api_url(server_port: int) -> str:
|
121
|
+
"""
|
122
|
+
Fixture that provides the base API URL.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
server_port: Server port
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
Base API URL
|
129
|
+
"""
|
130
|
+
return f"http://localhost:{server_port}"
|
131
|
+
|
132
|
+
|
133
|
+
@pytest.fixture
|
134
|
+
def jsonrpc_client(api_url: str) -> Callable:
|
135
|
+
"""
|
136
|
+
Fixture that provides a JSON-RPC client function.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
api_url: Base API URL
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
Function to make JSON-RPC requests
|
143
|
+
"""
|
144
|
+
def make_request(method: str, params: Dict[str, Any], request_id: int = 1) -> Dict[str, Any]:
|
145
|
+
"""
|
146
|
+
Make a JSON-RPC request.
|
147
|
+
|
148
|
+
Args:
|
149
|
+
method: Method name
|
150
|
+
params: Method parameters
|
151
|
+
request_id: Request ID
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
JSON-RPC response
|
155
|
+
"""
|
156
|
+
payload = {
|
157
|
+
"jsonrpc": "2.0",
|
158
|
+
"method": method,
|
159
|
+
"params": params,
|
160
|
+
"id": request_id
|
161
|
+
}
|
162
|
+
|
163
|
+
response = requests.post(
|
164
|
+
f"{api_url}/api/jsonrpc",
|
165
|
+
json=payload,
|
166
|
+
headers={"Content-Type": "application/json"}
|
167
|
+
)
|
168
|
+
|
169
|
+
return response.json()
|
170
|
+
|
171
|
+
return make_request
|
@@ -0,0 +1,111 @@
|
|
1
|
+
"""
|
2
|
+
Unit tests for the HelloCommand in the minimal example.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
from simple_server import HelloCommand, HelloResult
|
8
|
+
|
9
|
+
|
10
|
+
class TestHelloCommand:
|
11
|
+
"""Tests for HelloCommand class."""
|
12
|
+
|
13
|
+
@pytest.mark.asyncio
|
14
|
+
async def test_execute_with_default_name(self):
|
15
|
+
"""Test HelloCommand.execute with default name parameter."""
|
16
|
+
# Create command instance
|
17
|
+
command = HelloCommand()
|
18
|
+
|
19
|
+
# Execute command with default parameter
|
20
|
+
result = await command.execute()
|
21
|
+
|
22
|
+
# Check result type
|
23
|
+
assert isinstance(result, HelloResult)
|
24
|
+
|
25
|
+
# Check message content
|
26
|
+
assert result.message == "Hello, World!"
|
27
|
+
|
28
|
+
# Check serialization
|
29
|
+
assert result.to_dict() == {"message": "Hello, World!"}
|
30
|
+
|
31
|
+
@pytest.mark.asyncio
|
32
|
+
async def test_execute_with_custom_name(self):
|
33
|
+
"""Test HelloCommand.execute with custom name parameter."""
|
34
|
+
# Create command instance
|
35
|
+
command = HelloCommand()
|
36
|
+
|
37
|
+
# Execute command with custom parameter
|
38
|
+
result = await command.execute(name="Test")
|
39
|
+
|
40
|
+
# Check result
|
41
|
+
assert isinstance(result, HelloResult)
|
42
|
+
assert result.message == "Hello, Test!"
|
43
|
+
|
44
|
+
# Check serialization
|
45
|
+
assert result.to_dict() == {"message": "Hello, Test!"}
|
46
|
+
|
47
|
+
@pytest.mark.asyncio
|
48
|
+
async def test_execute_with_empty_name(self):
|
49
|
+
"""Test HelloCommand.execute with empty name parameter."""
|
50
|
+
# Create command instance
|
51
|
+
command = HelloCommand()
|
52
|
+
|
53
|
+
# Execute command with empty parameter
|
54
|
+
result = await command.execute(name="")
|
55
|
+
|
56
|
+
# Check result
|
57
|
+
assert isinstance(result, HelloResult)
|
58
|
+
assert result.message == "Hello, !"
|
59
|
+
|
60
|
+
# Check serialization
|
61
|
+
assert result.to_dict() == {"message": "Hello, !"}
|
62
|
+
|
63
|
+
|
64
|
+
class TestHelloResult:
|
65
|
+
"""Tests for HelloResult class."""
|
66
|
+
|
67
|
+
def test_init(self):
|
68
|
+
"""Test HelloResult initialization."""
|
69
|
+
# Create result instance
|
70
|
+
result = HelloResult("Hello, Test!")
|
71
|
+
|
72
|
+
# Check attributes
|
73
|
+
assert result.message == "Hello, Test!"
|
74
|
+
|
75
|
+
def test_to_dict(self):
|
76
|
+
"""Test HelloResult.to_dict method."""
|
77
|
+
# Create result instance
|
78
|
+
result = HelloResult("Hello, Test!")
|
79
|
+
|
80
|
+
# Check serialization
|
81
|
+
assert result.to_dict() == {"message": "Hello, Test!"}
|
82
|
+
|
83
|
+
def test_get_schema(self):
|
84
|
+
"""Test HelloResult.get_schema method."""
|
85
|
+
# Get schema
|
86
|
+
schema = HelloResult.get_schema()
|
87
|
+
|
88
|
+
# Check schema structure
|
89
|
+
assert schema["type"] == "object"
|
90
|
+
assert "properties" in schema
|
91
|
+
assert "message" in schema["properties"]
|
92
|
+
assert schema["properties"]["message"]["type"] == "string"
|
93
|
+
assert "required" in schema
|
94
|
+
assert "message" in schema["required"]
|
95
|
+
|
96
|
+
|
97
|
+
class TestHelloCommandSchema:
|
98
|
+
"""Tests for HelloCommand schema."""
|
99
|
+
|
100
|
+
def test_get_schema(self):
|
101
|
+
"""Test HelloCommand.get_schema method."""
|
102
|
+
# Get schema
|
103
|
+
schema = HelloCommand.get_schema()
|
104
|
+
|
105
|
+
# Check schema structure
|
106
|
+
assert schema["type"] == "object"
|
107
|
+
assert "properties" in schema
|
108
|
+
assert "name" in schema["properties"]
|
109
|
+
assert schema["properties"]["name"]["type"] == "string"
|
110
|
+
assert "additionalProperties" in schema
|
111
|
+
assert schema["additionalProperties"] is False
|