154 lines
4.8 KiB
Python
154 lines
4.8 KiB
Python
"""Tests for embedded bridge implementation."""
|
|
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from freecad_mcp.bridge.embedded import EmbeddedBridge
|
|
|
|
|
|
class TestEmbeddedBridge:
|
|
"""Tests for EmbeddedBridge class."""
|
|
|
|
def test_initialization(self):
|
|
"""Bridge should initialize with correct defaults."""
|
|
bridge = EmbeddedBridge()
|
|
|
|
assert bridge._freecad_path is None
|
|
assert bridge._fc_module is None
|
|
assert bridge._connected is False
|
|
|
|
def test_initialization_with_path(self):
|
|
"""Bridge should accept custom FreeCAD path."""
|
|
bridge = EmbeddedBridge(freecad_path="/custom/path")
|
|
|
|
assert bridge._freecad_path == "/custom/path"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_is_connected_before_connect(self):
|
|
"""is_connected should return False before connect."""
|
|
bridge = EmbeddedBridge()
|
|
|
|
assert await bridge.is_connected() is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_python_when_not_connected(self):
|
|
"""execute_python should return error when not connected."""
|
|
bridge = EmbeddedBridge()
|
|
|
|
result = await bridge.execute_python("x = 1")
|
|
|
|
assert result.success is False
|
|
assert result.error_type == "ConnectionError"
|
|
assert "not connected" in result.stderr
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_disconnect_when_not_connected(self):
|
|
"""disconnect should handle not being connected."""
|
|
bridge = EmbeddedBridge()
|
|
|
|
# Should not raise
|
|
await bridge.disconnect()
|
|
|
|
assert bridge._connected is False
|
|
|
|
|
|
class TestEmbeddedBridgeCodeExecution:
|
|
"""Tests for code execution in embedded bridge."""
|
|
|
|
@pytest.fixture
|
|
def mock_freecad(self):
|
|
"""Create a mock FreeCAD module."""
|
|
mock_fc = mock.MagicMock()
|
|
mock_fc.Version.return_value = ["0", "21", "2", "2024-01-01"]
|
|
mock_fc.listDocuments.return_value = {}
|
|
mock_fc.ActiveDocument = None
|
|
return mock_fc
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_simple_code(self, mock_freecad):
|
|
"""execute_python should execute simple Python code."""
|
|
bridge = EmbeddedBridge()
|
|
bridge._fc_module = mock_freecad
|
|
bridge._connected = True
|
|
|
|
result = await bridge.execute_python("_result_ = 1 + 1")
|
|
|
|
assert result.success is True
|
|
assert result.result == 2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_code_with_print(self, mock_freecad):
|
|
"""execute_python should capture stdout."""
|
|
bridge = EmbeddedBridge()
|
|
bridge._fc_module = mock_freecad
|
|
bridge._connected = True
|
|
|
|
result = await bridge.execute_python("print('hello')")
|
|
|
|
assert result.success is True
|
|
assert "hello" in result.stdout
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_code_with_error(self, mock_freecad):
|
|
"""execute_python should capture exceptions."""
|
|
bridge = EmbeddedBridge()
|
|
bridge._fc_module = mock_freecad
|
|
bridge._connected = True
|
|
|
|
result = await bridge.execute_python("raise ValueError('test error')")
|
|
|
|
assert result.success is False
|
|
assert result.error_type == "ValueError"
|
|
assert result.error_traceback is not None
|
|
assert "test error" in result.error_traceback
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_code_with_syntax_error(self, mock_freecad):
|
|
"""execute_python should handle syntax errors."""
|
|
bridge = EmbeddedBridge()
|
|
bridge._fc_module = mock_freecad
|
|
bridge._connected = True
|
|
|
|
result = await bridge.execute_python("def bad syntax")
|
|
|
|
assert result.success is False
|
|
assert result.error_type == "SyntaxError"
|
|
|
|
|
|
class TestEmbeddedBridgeDocuments:
|
|
"""Tests for document handling in embedded bridge."""
|
|
|
|
@pytest.fixture
|
|
def mock_freecad_with_doc(self):
|
|
"""Create mock FreeCAD with a document."""
|
|
mock_doc = mock.MagicMock()
|
|
mock_doc.Name = "TestDoc"
|
|
mock_doc.Label = "Test Document"
|
|
mock_doc.FileName = "/tmp/test.FCStd"
|
|
mock_doc.Modified = False
|
|
mock_doc.Objects = []
|
|
|
|
mock_fc = mock.MagicMock()
|
|
mock_fc.listDocuments.return_value = {"TestDoc": mock_doc}
|
|
mock_fc.ActiveDocument = mock_doc
|
|
mock_fc.getDocument.return_value = mock_doc
|
|
|
|
return mock_fc
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_documents_empty(self):
|
|
"""get_documents should return empty list when no docs."""
|
|
mock_fc = mock.MagicMock()
|
|
mock_fc.listDocuments.return_value = {}
|
|
|
|
bridge = EmbeddedBridge()
|
|
bridge._fc_module = mock_fc
|
|
bridge._connected = True
|
|
|
|
# Mock the execute_python to return empty list
|
|
result = await bridge.get_documents()
|
|
|
|
# Since we're not mocking exec properly, we expect empty
|
|
assert isinstance(result, list)
|