import pytest
import json
from unittest.mock import MagicMock, AsyncMock, patch
from app.db import models
def test_mcp_list_tools_discovery(client):
"""Tests that the new file explorer tools are present in tools/list."""
test_client, mock_services = client
# Ensure all required services are mocked
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
mock_services.user_service = MagicMock()
mock_services.stt_service = MagicMock()
mock_services.tts_service = MagicMock()
mock_services.tool_service = MagicMock()
mock_services.browser_service = MagicMock()
# Mocking the discovery process
# The route calls _execute for tools/list
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_body = response.json()
assert "result" in res_body
tools = res_body["result"].get("tools", [])
tool_names = [t["name"] for t in tools]
assert "list_files" in tool_names
assert "upload_file" in tool_names
assert "download_file" in tool_names
assert "remove_file" in tool_names
assert "create_file" in tool_names
def test_mcp_list_files_success(client):
"""Tests list_files tool via MCP dispatch."""
test_client, mock_services = client
# Mock services
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
# Mock node access and orchestrator assistant
mock_services.mesh_service.require_node_access.return_value = True
mock_services.orchestrator.assistant.ls.return_value = {
"files": [{"path": "test.txt", "is_dir": False, "size": 123}]
}
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_files",
"arguments": {
"node_id": "test-node",
"path": "."
}
}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_body = response.json()
assert "result" in res_body
res_data = res_body["result"]
assert "content" in res_data
content_text = res_data["content"][0]["text"]
data = json.loads(content_text)
assert "files" in data
assert data["files"][0]["path"] == "test.txt"
mock_services.mesh_service.require_node_access.assert_called_once()
mock_services.orchestrator.assistant.ls.assert_called_once_with("test-node", ".", session_id="__fs_explorer__")
def test_mcp_upload_file_success(client):
"""Tests upload_file tool via MCP dispatch (aliased to write_file)."""
test_client, mock_services = client
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
mock_services.mesh_service.require_node_access.return_value = True
mock_services.orchestrator.assistant.write.return_value = {"status": "success"}
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "upload_file",
"arguments": {
"node_id": "test-node",
"path": "hello.txt",
"content": "hello world"
}
}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_data = response.json()["result"]
assert "content" in res_data
content_text = res_data["content"][0]["text"]
assert json.loads(content_text) == {"status": "success"}
mock_services.orchestrator.assistant.write.assert_called_once_with(
"test-node", "hello.txt", "hello world", False, session_id="__fs_explorer__"
)
def test_mcp_download_file_success(client):
"""Tests download_file tool via MCP dispatch (aliased to cat)."""
test_client, mock_services = client
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
mock_services.mesh_service.require_node_access.return_value = True
mock_services.orchestrator.assistant.cat.return_value = "file content"
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "download_file",
"arguments": {
"node_id": "test-node",
"path": "hello.txt"
}
}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_data = response.json()["result"]
assert "content" in res_data
assert res_data["content"][0]["text"] == "file content"
mock_services.orchestrator.assistant.cat.assert_called_once_with(
"test-node", "hello.txt", session_id="__fs_explorer__"
)
def test_mcp_remove_file_success(client):
"""Tests remove_file tool via MCP dispatch (aliased to delete_file)."""
test_client, mock_services = client
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
mock_services.mesh_service.require_node_access.return_value = True
mock_services.orchestrator.assistant.rm.return_value = {"status": "success"}
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "remove_file",
"arguments": {
"node_id": "test-node",
"path": "old.txt"
}
}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_data = response.json()["result"]
assert "content" in res_data
content_text = res_data["content"][0]["text"]
assert json.loads(content_text) == {"status": "success"}
mock_services.orchestrator.assistant.rm.assert_called_once_with(
"test-node", "old.txt", session_id="__fs_explorer__"
)
def test_mcp_create_file_success(client):
"""Tests create_file tool via MCP dispatch (empty content)."""
test_client, mock_services = client
mock_services.mesh_service = MagicMock()
mock_services.orchestrator = MagicMock()
mock_services.mesh_service.require_node_access.return_value = True
mock_services.orchestrator.assistant.write.return_value = {"status": "success"}
response = test_client.post("/mcp?token=test_user", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_file",
"arguments": {
"node_id": "test-node",
"path": "new_dir",
"is_dir": True
}
}
}, headers={"X-User-ID": "test_user"})
assert response.status_code == 200
res_data = response.json()["result"]
assert "content" in res_data
content_text = res_data["content"][0]["text"]
assert json.loads(content_text) == {"status": "success"}
mock_services.orchestrator.assistant.write.assert_called_once_with(
"test-node", "new_dir", "", True, session_id="__fs_explorer__"
)