- Add Dockerfile using uv for dependency management - Add docker-compose.yaml for container orchestration - Add .dockerignore to optimize builds - Update taskipy start task with http transport and port 3001 - Update README.md and AGENTS.md with Docker documentation
206 lines
6.8 KiB
Python
206 lines
6.8 KiB
Python
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client():
|
|
return MagicMock()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_service_provider(mock_client):
|
|
provider_mock = MagicMock()
|
|
mock_response = MagicMock()
|
|
mock_response.is_error = False
|
|
mock_response.data = []
|
|
|
|
provider_mock.party.find_by_identifier.return_value = mock_response
|
|
provider_mock.party.create.return_value = mock_response
|
|
provider_mock.schedule.create.return_value = mock_response
|
|
provider_mock.service_center.find_all.return_value = mock_response
|
|
provider_mock.product.find_salable.return_value = mock_response
|
|
|
|
return provider_mock
|
|
|
|
|
|
@pytest.fixture
|
|
def server_with_mocks(mock_service_provider):
|
|
with patch("tryton_mcp.server._service_provider", mock_service_provider):
|
|
yield mock_service_provider
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestFindCustomerByIdentifier:
|
|
async def test_find_customer_success(self, server_with_mocks):
|
|
from tryton_mcp.server import find_customer_by_identifier
|
|
|
|
server_with_mocks.party.find_by_identifier.return_value.data = [
|
|
{"id": 1, "party": {"name": "Test Customer"}}
|
|
]
|
|
|
|
result = find_customer_by_identifier("12345")
|
|
|
|
assert result == [{"id": 1, "party": {"name": "Test Customer"}}]
|
|
server_with_mocks.party.find_by_identifier.assert_called_once_with("12345")
|
|
|
|
async def test_find_customer_not_found(self, server_with_mocks):
|
|
from tryton_mcp.server import find_customer_by_identifier
|
|
|
|
server_with_mocks.party.find_by_identifier.return_value.is_error = True
|
|
|
|
with pytest.raises(Exception, match="Customer with identifier"):
|
|
find_customer_by_identifier("invalid")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestCreateCustomer:
|
|
async def test_create_customer_success(self, server_with_mocks):
|
|
from tryton_mcp.server import create_customer
|
|
|
|
server_with_mocks.party.create.return_value.data = [1]
|
|
|
|
result = create_customer("New Customer", [])
|
|
|
|
assert result == [1]
|
|
|
|
async def test_create_customer_invalid_name(self, server_with_mocks):
|
|
from tryton_mcp.server import create_customer
|
|
from pydantic import ValidationError
|
|
|
|
with pytest.raises(ValidationError):
|
|
create_customer("", [])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestCreateSchedule:
|
|
async def test_create_schedule_success(self, server_with_mocks):
|
|
from tryton_mcp.server import create_schedule
|
|
|
|
server_with_mocks.schedule.create.return_value.data = [1]
|
|
|
|
result = create_schedule(
|
|
professional=6,
|
|
description="Test appointment",
|
|
customer=4,
|
|
date="2026-03-15T10:00:00",
|
|
service_center=11,
|
|
)
|
|
|
|
assert result == [1]
|
|
|
|
async def test_create_schedule_failure(self, server_with_mocks):
|
|
from tryton_mcp.server import create_schedule
|
|
|
|
server_with_mocks.schedule.create.return_value.is_error = True
|
|
server_with_mocks.schedule.create.return_value.error = "Create failed"
|
|
|
|
with pytest.raises(Exception, match="Failed to create schedule"):
|
|
create_schedule(
|
|
professional=6,
|
|
description="Test appointment",
|
|
customer=4,
|
|
date="2026-03-15T10:00:00",
|
|
service_center=11,
|
|
)
|
|
|
|
async def test_create_schedule_invalid_date_format(self, server_with_mocks):
|
|
from tryton_mcp.server import create_schedule
|
|
from pydantic import ValidationError
|
|
|
|
with pytest.raises(ValidationError):
|
|
create_schedule(
|
|
professional=6,
|
|
description="Test appointment",
|
|
customer=4,
|
|
date="invalid-date",
|
|
service_center=11,
|
|
)
|
|
|
|
async def test_create_schedule_invalid_professional(self, server_with_mocks):
|
|
from tryton_mcp.server import create_schedule
|
|
from pydantic import ValidationError
|
|
|
|
with pytest.raises(ValidationError):
|
|
create_schedule(
|
|
professional=0,
|
|
description="Test appointment",
|
|
customer=4,
|
|
date="2026-03-15T10:00:00",
|
|
service_center=11,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestFindServiceCenters:
|
|
async def test_find_service_centers_success(self, server_with_mocks):
|
|
from tryton_mcp.server import find_service_centers
|
|
|
|
server_with_mocks.service_center.find_all.return_value.data = [
|
|
{"id": 11, "name": "Center 1", "address.street": "Street 1"}
|
|
]
|
|
|
|
result = find_service_centers()
|
|
|
|
assert result == [{"id": 11, "name": "Center 1", "address.street": "Street 1"}]
|
|
server_with_mocks.service_center.find_all.assert_called_once_with(
|
|
offset=0, limit=None
|
|
)
|
|
|
|
async def test_find_service_centers_with_pagination(self, server_with_mocks):
|
|
from tryton_mcp.server import find_service_centers
|
|
|
|
find_service_centers(offset=10, limit=5)
|
|
|
|
server_with_mocks.service_center.find_all.assert_called_once_with(
|
|
offset=10, limit=5
|
|
)
|
|
|
|
async def test_find_service_centers_failure(self, server_with_mocks):
|
|
from tryton_mcp.server import find_service_centers
|
|
|
|
server_with_mocks.service_center.find_all.return_value.is_error = True
|
|
server_with_mocks.service_center.find_all.return_value.error = "Search failed"
|
|
|
|
with pytest.raises(Exception, match="Failed to find service centers"):
|
|
find_service_centers()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestFindProductsAndServices:
|
|
async def test_find_products_and_services_success(self, server_with_mocks):
|
|
from tryton_mcp.server import find_products_and_services
|
|
|
|
server_with_mocks.product.find_salable.return_value.data = [
|
|
{"id": 1, "name": "Product 1", "list_price": 100.0}
|
|
]
|
|
|
|
result = find_products_and_services()
|
|
|
|
assert result == [{"id": 1, "name": "Product 1", "list_price": 100.0}]
|
|
server_with_mocks.product.find_salable.assert_called_once_with(
|
|
offset=0, limit=None
|
|
)
|
|
|
|
async def test_find_products_with_pagination(self, server_with_mocks):
|
|
from tryton_mcp.server import find_products_and_services
|
|
|
|
find_products_and_services(offset=5, limit=10)
|
|
|
|
server_with_mocks.product.find_salable.assert_called_once_with(
|
|
offset=5, limit=10
|
|
)
|
|
|
|
async def test_find_products_failure(self, server_with_mocks):
|
|
from tryton_mcp.server import find_products_and_services
|
|
|
|
server_with_mocks.product.find_salable.return_value.is_error = True
|
|
server_with_mocks.product.find_salable.return_value.error = "Search failed"
|
|
|
|
with pytest.raises(Exception, match="Failed to find products"):
|
|
find_products_and_services()
|