import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from dolibarr_mcp.dolibarr_mcp_server import handle_call_tool
from dolibarr_mcp.dolibarr_client import DolibarrClient
@pytest.mark.asyncio
async def test_search_products_by_ref():
# Mock DolibarrClient
with patch("dolibarr_mcp.dolibarr_mcp_server.DolibarrClient") as MockClient:
mock_instance = MockClient.return_value
mock_instance.__aenter__.return_value = mock_instance
# Mock search_products response
mock_instance.search_products = AsyncMock(return_value=[
{"id": 1, "ref": "PRJ-123", "label": "Project 123"}
])
# Call the tool
result = await handle_call_tool("search_products_by_ref", {"ref_prefix": "PRJ"})
# Verify the call - search_products is called with positional args (sqlfilter, limit)
mock_instance.search_products.assert_called_once()
call_args = mock_instance.search_products.call_args
# The SQL filter is passed as first positional argument
assert "(t.ref:like:'PRJ%')" in call_args.args[0]
# Verify result
assert "PRJ-123" in result[0].text
@pytest.mark.asyncio
async def test_resolve_product_ref_exact():
with patch("dolibarr_mcp.dolibarr_mcp_server.DolibarrClient") as MockClient:
mock_instance = MockClient.return_value
mock_instance.__aenter__.return_value = mock_instance
# Mock search_products response (exact match)
mock_instance.search_products = AsyncMock(return_value=[
{"id": 1, "ref": "PRJ-123", "label": "Project 123"}
])
result = await handle_call_tool("resolve_product_ref", {"ref": "PRJ-123"})
mock_instance.search_products.assert_called_once()
assert "ok" in result[0].text
assert "PRJ-123" in result[0].text
@pytest.mark.asyncio
async def test_resolve_product_ref_ambiguous():
with patch("dolibarr_mcp.dolibarr_mcp_server.DolibarrClient") as MockClient:
mock_instance = MockClient.return_value
mock_instance.__aenter__.return_value = mock_instance
# Mock search_products response (multiple matches, none exact)
mock_instance.search_products = AsyncMock(return_value=[
{"id": 1, "ref": "PRJ-123-A", "label": "Project 123 A"},
{"id": 2, "ref": "PRJ-123-B", "label": "Project 123 B"}
])
# Search for "PRJ-123" which matches both partially (hypothetically) but neither exactly
result = await handle_call_tool("resolve_product_ref", {"ref": "PRJ-123"})
assert "ambiguous" in result[0].text
@pytest.mark.asyncio
async def test_search_customers():
with patch("dolibarr_mcp.dolibarr_mcp_server.DolibarrClient") as MockClient:
mock_instance = MockClient.return_value
mock_instance.__aenter__.return_value = mock_instance
mock_instance.search_customers = AsyncMock(return_value=[
{"id": 1, "nom": "Acme Corp"}
])
result = await handle_call_tool("search_customers", {"query": "Acme"})
# Verify the call - search_customers is called with positional args (sqlfilter, limit)
mock_instance.search_customers.assert_called_once()
call_args = mock_instance.search_customers.call_args
# The SQL filter is passed as first positional argument
assert "Acme" in call_args.args[0]