Initial implementation
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
"""Tests for modules/containers.py."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
|
||||
def make_mock_mcp():
|
||||
tools: dict = {}
|
||||
|
||||
class MockMCP:
|
||||
def tool(self):
|
||||
def decorator(fn):
|
||||
tools[fn.__name__] = fn
|
||||
return fn
|
||||
return decorator
|
||||
|
||||
return MockMCP(), tools
|
||||
|
||||
|
||||
def make_config():
|
||||
from mcp_synology_container.config import AppConfig, ConnectionConfig
|
||||
return AppConfig(
|
||||
schema_version=1,
|
||||
connection=ConnectionConfig(host="nas.local", port=443, https=True, verify_ssl=True),
|
||||
)
|
||||
|
||||
|
||||
SAMPLE_CONTAINERS_DATA = {
|
||||
"containers": [
|
||||
{
|
||||
"name": "myapp_web",
|
||||
"status": "running",
|
||||
"image": "nginx:alpine",
|
||||
"project_name": "myapp",
|
||||
},
|
||||
{
|
||||
"name": "myapp_db",
|
||||
"status": "running",
|
||||
"image": "postgres:15",
|
||||
"project_name": "myapp",
|
||||
},
|
||||
{
|
||||
"name": "other_svc",
|
||||
"status": "stopped",
|
||||
"image": "redis:7",
|
||||
"project_name": "other",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
SAMPLE_LOGS_DATA = {
|
||||
"logs": [
|
||||
{
|
||||
"created": "2025-01-01T10:00:00Z",
|
||||
"stream": "stdout",
|
||||
"text": "Server started",
|
||||
"docid": "1",
|
||||
},
|
||||
{
|
||||
"created": "2025-01-01T10:00:01Z",
|
||||
"stream": "stderr",
|
||||
"text": "Warning: deprecated option",
|
||||
"docid": "2",
|
||||
},
|
||||
],
|
||||
"total": 2,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_containers_all():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = SAMPLE_CONTAINERS_DATA
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["list_containers"]()
|
||||
assert "myapp_web" in result
|
||||
assert "myapp_db" in result
|
||||
assert "other_svc" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_containers_filtered_by_project():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = SAMPLE_CONTAINERS_DATA
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["list_containers"](project_name="myapp")
|
||||
assert "myapp_web" in result
|
||||
assert "myapp_db" in result
|
||||
assert "other_svc" not in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_containers_empty():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = {"containers": []}
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["list_containers"]()
|
||||
assert "No containers found" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_container_logs():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = SAMPLE_LOGS_DATA
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["get_container_logs"]("myapp_web", tail=50)
|
||||
assert "myapp_web" in result
|
||||
assert "Server started" in result
|
||||
assert "Warning: deprecated option" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_container_logs_with_keyword():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = SAMPLE_LOGS_DATA
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
await tools["get_container_logs"]("myapp_web", tail=100, keyword="error")
|
||||
call_params = client.request.call_args[1]["params"]
|
||||
assert call_params["keyword"] == "error"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_exec_in_container_requires_confirmation():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["exec_in_container"]("myapp_web", "ls /app", confirmed=False)
|
||||
assert "confirmed=True" in result
|
||||
client.request.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_exec_in_container_confirmed():
|
||||
from mcp_synology_container.modules.containers import register_containers
|
||||
|
||||
client = AsyncMock()
|
||||
client.request.return_value = {"output": "file1.py\nfile2.py", "exit_code": 0}
|
||||
|
||||
mcp, tools = make_mock_mcp()
|
||||
register_containers(mcp, make_config(), client)
|
||||
|
||||
result = await tools["exec_in_container"]("myapp_web", "ls /app", confirmed=True)
|
||||
assert "file1.py" in result
|
||||
assert "Exit code: 0" in result
|
||||
Reference in New Issue
Block a user