mirror of
https://github.com/latinogino/dolibarr-mcp.git
synced 2026-04-15 07:05:34 +02:00
376 lines
13 KiB
Python
376 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Comprehensive test suite for Dolibarr MCP Server."""
|
|
|
|
import asyncio
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
from dotenv import load_dotenv
|
|
|
|
# Add parent directory to path
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from src.dolibarr_mcp.config import Config
|
|
from src.dolibarr_mcp.dolibarr_client import DolibarrClient, DolibarrAPIError
|
|
|
|
# Load environment variables
|
|
load_dotenv()
|
|
|
|
|
|
class TestColors:
|
|
"""ANSI color codes for terminal output."""
|
|
HEADER = '\033[95m'
|
|
OKBLUE = '\033[94m'
|
|
OKCYAN = '\033[96m'
|
|
OKGREEN = '\033[92m'
|
|
WARNING = '\033[93m'
|
|
FAIL = '\033[91m'
|
|
ENDC = '\033[0m'
|
|
BOLD = '\033[1m'
|
|
UNDERLINE = '\033[4m'
|
|
|
|
|
|
def print_header(text):
|
|
"""Print a formatted header."""
|
|
print(f"\n{TestColors.HEADER}{TestColors.BOLD}{'=' * 60}{TestColors.ENDC}")
|
|
print(f"{TestColors.HEADER}{TestColors.BOLD}{text}{TestColors.ENDC}")
|
|
print(f"{TestColors.HEADER}{TestColors.BOLD}{'=' * 60}{TestColors.ENDC}")
|
|
|
|
|
|
def print_test(name, result, details=""):
|
|
"""Print test result."""
|
|
if result:
|
|
status = f"{TestColors.OKGREEN}✅ PASS{TestColors.ENDC}"
|
|
else:
|
|
status = f"{TestColors.FAIL}❌ FAIL{TestColors.ENDC}"
|
|
|
|
print(f" {status} - {name}")
|
|
if details:
|
|
print(f" {TestColors.OKCYAN}{details}{TestColors.ENDC}")
|
|
|
|
|
|
async def test_connection(client):
|
|
"""Test basic API connection."""
|
|
print_header("🔌 CONNECTION TEST")
|
|
|
|
try:
|
|
result = await client.get_status()
|
|
print_test("API Connection", True, f"Dolibarr v{result.get('dolibarr_version', 'unknown')}")
|
|
return True
|
|
except Exception as e:
|
|
print_test("API Connection", False, str(e))
|
|
return False
|
|
|
|
|
|
async def test_customers(client):
|
|
"""Test customer CRUD operations."""
|
|
print_header("👥 CUSTOMER MANAGEMENT")
|
|
|
|
test_customer_id = None
|
|
|
|
try:
|
|
# List customers
|
|
customers = await client.get_customers(limit=5)
|
|
print_test("List Customers", True, f"Found {len(customers)} customers")
|
|
|
|
# Create customer
|
|
new_customer = await client.create_customer(
|
|
name=f"Test Customer {datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
email="test@example.com",
|
|
phone="+1234567890",
|
|
address="123 Test Street",
|
|
town="Test City",
|
|
zip="12345"
|
|
)
|
|
test_customer_id = new_customer if isinstance(new_customer, int) else new_customer.get('id')
|
|
print_test("Create Customer", True, f"Created ID: {test_customer_id}")
|
|
|
|
# Get customer by ID
|
|
if test_customer_id:
|
|
customer = await client.get_customer_by_id(test_customer_id)
|
|
print_test("Get Customer by ID", True, f"Retrieved: {customer.get('name', 'Unknown')}")
|
|
|
|
# Update customer
|
|
updated = await client.update_customer(
|
|
test_customer_id,
|
|
email="updated@example.com"
|
|
)
|
|
print_test("Update Customer", True, "Email updated")
|
|
|
|
# Delete customer
|
|
deleted = await client.delete_customer(test_customer_id)
|
|
print_test("Delete Customer", True, f"Deleted ID: {test_customer_id}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Customer Operations", False, str(e))
|
|
# Try to clean up
|
|
if test_customer_id:
|
|
try:
|
|
await client.delete_customer(test_customer_id)
|
|
except:
|
|
pass
|
|
return False
|
|
|
|
|
|
async def test_products(client):
|
|
"""Test product CRUD operations."""
|
|
print_header("📦 PRODUCT MANAGEMENT")
|
|
|
|
test_product_id = None
|
|
|
|
try:
|
|
# List products
|
|
products = await client.get_products(limit=5)
|
|
print_test("List Products", True, f"Found {len(products)} products")
|
|
|
|
# Create product
|
|
new_product = await client.create_product(
|
|
label=f"Test Product {datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
price=99.99,
|
|
description="Test product description",
|
|
stock=100
|
|
)
|
|
test_product_id = new_product if isinstance(new_product, int) else new_product.get('id')
|
|
print_test("Create Product", True, f"Created ID: {test_product_id}")
|
|
|
|
# Get product by ID
|
|
if test_product_id:
|
|
product = await client.get_product_by_id(test_product_id)
|
|
print_test("Get Product by ID", True, f"Retrieved: {product.get('label', 'Unknown')}")
|
|
|
|
# Update product
|
|
updated = await client.update_product(
|
|
test_product_id,
|
|
price=149.99
|
|
)
|
|
print_test("Update Product", True, "Price updated")
|
|
|
|
# Delete product
|
|
deleted = await client.delete_product(test_product_id)
|
|
print_test("Delete Product", True, f"Deleted ID: {test_product_id}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Product Operations", False, str(e))
|
|
# Try to clean up
|
|
if test_product_id:
|
|
try:
|
|
await client.delete_product(test_product_id)
|
|
except:
|
|
pass
|
|
return False
|
|
|
|
|
|
async def test_users(client):
|
|
"""Test user operations."""
|
|
print_header("👤 USER MANAGEMENT")
|
|
|
|
try:
|
|
# List users
|
|
users = await client.get_users(limit=5)
|
|
print_test("List Users", True, f"Found {len(users)} users")
|
|
|
|
if users:
|
|
# Get first user details
|
|
first_user = users[0]
|
|
user_id = first_user.get('id')
|
|
if user_id:
|
|
user = await client.get_user_by_id(user_id)
|
|
print_test("Get User by ID", True, f"Retrieved: {user.get('login', 'Unknown')}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("User Operations", False, str(e))
|
|
return False
|
|
|
|
|
|
async def test_invoices(client):
|
|
"""Test invoice operations."""
|
|
print_header("📄 INVOICE MANAGEMENT")
|
|
|
|
try:
|
|
# List invoices
|
|
invoices = await client.get_invoices(limit=5)
|
|
print_test("List Invoices", True, f"Found {len(invoices)} invoices")
|
|
|
|
if invoices:
|
|
# Get first invoice details
|
|
first_invoice = invoices[0]
|
|
invoice_id = first_invoice.get('id')
|
|
if invoice_id:
|
|
invoice = await client.get_invoice_by_id(invoice_id)
|
|
print_test("Get Invoice by ID", True, f"Retrieved Invoice #{invoice.get('ref', 'Unknown')}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Invoice Operations", False, str(e))
|
|
return False
|
|
|
|
|
|
async def test_orders(client):
|
|
"""Test order operations."""
|
|
print_header("📋 ORDER MANAGEMENT")
|
|
|
|
try:
|
|
# List orders
|
|
orders = await client.get_orders(limit=5)
|
|
print_test("List Orders", True, f"Found {len(orders)} orders")
|
|
|
|
if orders:
|
|
# Get first order details
|
|
first_order = orders[0]
|
|
order_id = first_order.get('id')
|
|
if order_id:
|
|
order = await client.get_order_by_id(order_id)
|
|
print_test("Get Order by ID", True, f"Retrieved Order #{order.get('ref', 'Unknown')}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Order Operations", False, str(e))
|
|
return False
|
|
|
|
|
|
async def test_contacts(client):
|
|
"""Test contact operations."""
|
|
print_header("📇 CONTACT MANAGEMENT")
|
|
|
|
test_contact_id = None
|
|
|
|
try:
|
|
# List contacts
|
|
contacts = await client.get_contacts(limit=5)
|
|
print_test("List Contacts", True, f"Found {len(contacts)} contacts")
|
|
|
|
# Create contact
|
|
new_contact = await client.create_contact(
|
|
firstname="Test",
|
|
lastname=f"Contact {datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
email="testcontact@example.com"
|
|
)
|
|
test_contact_id = new_contact if isinstance(new_contact, int) else new_contact.get('id')
|
|
print_test("Create Contact", True, f"Created ID: {test_contact_id}")
|
|
|
|
# Get contact by ID
|
|
if test_contact_id:
|
|
contact = await client.get_contact_by_id(test_contact_id)
|
|
name = f"{contact.get('firstname', '')} {contact.get('lastname', '')}"
|
|
print_test("Get Contact by ID", True, f"Retrieved: {name.strip()}")
|
|
|
|
# Update contact
|
|
updated = await client.update_contact(
|
|
test_contact_id,
|
|
email="updatedcontact@example.com"
|
|
)
|
|
print_test("Update Contact", True, "Email updated")
|
|
|
|
# Delete contact
|
|
deleted = await client.delete_contact(test_contact_id)
|
|
print_test("Delete Contact", True, f"Deleted ID: {test_contact_id}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Contact Operations", False, str(e))
|
|
# Try to clean up
|
|
if test_contact_id:
|
|
try:
|
|
await client.delete_contact(test_contact_id)
|
|
except:
|
|
pass
|
|
return False
|
|
|
|
|
|
async def test_raw_api(client):
|
|
"""Test raw API access."""
|
|
print_header("🔧 RAW API ACCESS")
|
|
|
|
try:
|
|
# Test raw GET request
|
|
result = await client.dolibarr_raw_api(
|
|
method="GET",
|
|
endpoint="/setup/modules",
|
|
params={"limit": 5}
|
|
)
|
|
print_test("Raw GET Request", True, f"Retrieved {len(result) if isinstance(result, list) else 'data'}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print_test("Raw API Access", False, str(e))
|
|
return False
|
|
|
|
|
|
async def main():
|
|
"""Run all tests."""
|
|
print(f"{TestColors.BOLD}{TestColors.HEADER}")
|
|
print("╔══════════════════════════════════════════════════════════╗")
|
|
print("║ 🚀 DOLIBARR MCP SERVER - COMPREHENSIVE TEST SUITE ║")
|
|
print("╚══════════════════════════════════════════════════════════╝")
|
|
print(f"{TestColors.ENDC}")
|
|
|
|
# Initialize configuration and client
|
|
try:
|
|
config = Config()
|
|
print(f"\n{TestColors.OKGREEN}✅ Configuration loaded successfully{TestColors.ENDC}")
|
|
print(f" URL: {config.dolibarr_url}")
|
|
print(f" API Key: {'*' * 20}{config.api_key[-4:]}")
|
|
except Exception as e:
|
|
print(f"\n{TestColors.FAIL}❌ Configuration failed: {e}{TestColors.ENDC}")
|
|
return
|
|
|
|
# Run tests
|
|
async with DolibarrClient(config) as client:
|
|
results = []
|
|
|
|
# Run each test suite
|
|
results.append(("Connection", await test_connection(client)))
|
|
results.append(("Users", await test_users(client)))
|
|
results.append(("Customers", await test_customers(client)))
|
|
results.append(("Products", await test_products(client)))
|
|
results.append(("Invoices", await test_invoices(client)))
|
|
results.append(("Orders", await test_orders(client)))
|
|
results.append(("Contacts", await test_contacts(client)))
|
|
results.append(("Raw API", await test_raw_api(client)))
|
|
|
|
# Print summary
|
|
print_header("📊 TEST SUMMARY")
|
|
|
|
total_tests = len(results)
|
|
passed_tests = sum(1 for _, passed in results if passed)
|
|
failed_tests = total_tests - passed_tests
|
|
|
|
print(f"\n Total Tests: {total_tests}")
|
|
print(f" {TestColors.OKGREEN}Passed: {passed_tests}{TestColors.ENDC}")
|
|
print(f" {TestColors.FAIL}Failed: {failed_tests}{TestColors.ENDC}")
|
|
|
|
if failed_tests == 0:
|
|
print(f"\n{TestColors.OKGREEN}{TestColors.BOLD}🎉 ALL TESTS PASSED! 🎉{TestColors.ENDC}")
|
|
print(f"\n{TestColors.OKCYAN}The Dolibarr MCP server is fully operational!{TestColors.ENDC}")
|
|
print(f"{TestColors.OKCYAN}You can now use it with Claude Desktop.{TestColors.ENDC}")
|
|
else:
|
|
print(f"\n{TestColors.WARNING}⚠️ Some tests failed. Check the details above.{TestColors.ENDC}")
|
|
print(f"{TestColors.WARNING}The server may still work for successful operations.{TestColors.ENDC}")
|
|
|
|
print(f"\n{TestColors.BOLD}Next steps:{TestColors.ENDC}")
|
|
print("1. Ensure Claude Desktop configuration includes this server")
|
|
print("2. Restart Claude Desktop to reload MCP servers")
|
|
print("3. Test by asking Claude to interact with your Dolibarr system")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(main())
|
|
except KeyboardInterrupt:
|
|
print(f"\n\n{TestColors.WARNING}Test suite interrupted by user{TestColors.ENDC}")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"\n{TestColors.FAIL}Unexpected error: {e}{TestColors.ENDC}")
|
|
sys.exit(1)
|