Skip to main content
Glama

MCP Server for Odoo

by ivnvxd
Mozilla Public License 2.0
88
  • Apple
  • Linux
test_odoo_connection_basic.py•11.7 kB
"""Tests for basic Odoo XML-RPC connection infrastructure. These tests use a real Odoo server at localhost:8069 to test connection management and error handling. """ import os import socket from unittest.mock import MagicMock, patch import pytest from mcp_server_odoo.config import OdooConfig from mcp_server_odoo.odoo_connection import OdooConnection, OdooConnectionError, create_connection @pytest.fixture def test_config(): """Create test configuration.""" return OdooConfig( url=os.getenv("ODOO_URL", "http://localhost:8069"), api_key="test_api_key", database=os.getenv("ODOO_DB"), log_level="INFO", default_limit=10, max_limit=100, ) @pytest.fixture def invalid_config(): """Create configuration with invalid URL.""" return OdooConfig( url="http://invalid.host.nowhere:9999", api_key="test_api_key", database=os.getenv("ODOO_DB"), log_level="INFO", default_limit=10, max_limit=100, ) class TestOdooConnectionInit: """Test OdooConnection initialization.""" def test_init_valid_config(self, test_config): """Test initialization with valid configuration.""" conn = OdooConnection(test_config) assert conn.config == test_config assert conn.timeout == OdooConnection.DEFAULT_TIMEOUT assert not conn.is_connected # Parse expected values from config URL from urllib.parse import urlparse parsed = urlparse(test_config.url) expected_host = parsed.hostname or "localhost" expected_port = parsed.port or (443 if parsed.scheme == "https" else 80) assert conn._url_components["host"] == expected_host assert conn._url_components["port"] == expected_port assert conn._url_components["scheme"] == parsed.scheme def test_init_custom_timeout(self, test_config): """Test initialization with custom timeout.""" conn = OdooConnection(test_config, timeout=60) assert conn.timeout == 60 def test_parse_url_https(self): """Test URL parsing for HTTPS URLs.""" config = OdooConfig( url="https://odoo.example.com", api_key="test", database=os.getenv("ODOO_DB") ) conn = OdooConnection(config) assert conn._url_components["scheme"] == "https" assert conn._url_components["host"] == "odoo.example.com" assert conn._url_components["port"] == 443 def test_parse_url_with_path(self): """Test URL parsing with path.""" config = OdooConfig( url="http://localhost:8069/custom/path", api_key="test", database=os.getenv("ODOO_DB") ) conn = OdooConnection(config) assert conn._url_components["path"] == "/custom/path" assert conn._url_components["base_url"] == "http://localhost:8069/custom/path" def test_parse_url_invalid_scheme(self): """Test URL parsing with invalid scheme.""" with pytest.raises(ValueError, match="ODOO_URL must start with http:// or https://"): config = OdooConfig( url="ftp://localhost:8069", api_key="test", database=os.getenv("ODOO_DB") ) OdooConnection(config) def test_build_endpoint_url(self, test_config): """Test endpoint URL building.""" conn = OdooConnection(test_config) # Get the endpoints from config endpoints = test_config.get_endpoint_paths() db_url = conn._build_endpoint_url(endpoints["db"]) # Build expected URL from config from urllib.parse import urlparse parsed = urlparse(test_config.url) expected_url = f"{parsed.scheme}://{parsed.netloc}{endpoints['db']}" assert db_url == expected_url common_url = conn._build_endpoint_url(endpoints["common"]) expected_common_url = f"{parsed.scheme}://{parsed.netloc}{endpoints['common']}" assert common_url == expected_common_url object_url = conn._build_endpoint_url(endpoints["object"]) expected_object_url = f"{parsed.scheme}://{parsed.netloc}{endpoints['object']}" assert object_url == expected_object_url class TestOdooConnectionConnect: """Test connection establishment.""" @pytest.mark.odoo_required def test_connect_success(self, test_config): """Test successful connection to real Odoo server.""" conn = OdooConnection(test_config) try: conn.connect() assert conn.is_connected assert conn._db_proxy is not None assert conn._common_proxy is not None assert conn._object_proxy is not None finally: conn.disconnect() @pytest.mark.odoo_required def test_connect_already_connected(self, test_config, caplog): """Test connecting when already connected.""" conn = OdooConnection(test_config) try: conn.connect() assert conn.is_connected # Try to connect again conn.connect() assert "Already connected to Odoo" in caplog.text finally: conn.disconnect() def test_connect_invalid_host(self, invalid_config): """Test connection to invalid host.""" conn = OdooConnection(invalid_config) with pytest.raises(OdooConnectionError) as exc_info: conn.connect() assert "Connection failed" in str(exc_info.value) def test_connect_timeout(self, test_config): """Test connection timeout handling.""" # Use very short timeout conn = OdooConnection(test_config, timeout=0.001) # Mock socket to simulate timeout with patch("socket.socket") as mock_socket: mock_socket.side_effect = socket.timeout("Timeout") with pytest.raises(OdooConnectionError) as exc_info: conn.connect() assert "Connection failed" in str(exc_info.value) class TestOdooConnectionDisconnect: """Test connection cleanup.""" @pytest.mark.odoo_required def test_disconnect_when_connected(self, test_config): """Test normal disconnect.""" conn = OdooConnection(test_config) conn.connect() assert conn.is_connected conn.disconnect() assert not conn.is_connected assert conn._db_proxy is None assert conn._common_proxy is None assert conn._object_proxy is None def test_disconnect_when_not_connected(self, test_config, caplog): """Test disconnect when not connected.""" conn = OdooConnection(test_config) conn.disconnect() assert "Not connected to Odoo" in caplog.text @pytest.mark.odoo_required def test_disconnect_cleanup_on_del(self, test_config): """Test cleanup on object deletion.""" conn = OdooConnection(test_config) conn.connect() # Delete should trigger disconnect del conn class TestOdooConnectionHealth: """Test health checking.""" @pytest.mark.odoo_required def test_check_health_connected(self, test_config): """Test health check when connected.""" conn = OdooConnection(test_config) try: conn.connect() is_healthy, message = conn.check_health() assert is_healthy assert "Connected to Odoo" in message finally: conn.disconnect() def test_check_health_not_connected(self, test_config): """Test health check when not connected.""" conn = OdooConnection(test_config) is_healthy, message = conn.check_health() assert not is_healthy assert message == "Not connected" @pytest.mark.odoo_required def test_check_health_error(self, test_config): """Test health check with connection error.""" conn = OdooConnection(test_config) conn.connect() # Mock common proxy to simulate error conn._common_proxy = MagicMock() conn._common_proxy.version.side_effect = Exception("Server error") is_healthy, message = conn.check_health() assert not is_healthy assert "Health check failed" in message conn.disconnect() class TestOdooConnectionProxies: """Test proxy access.""" @pytest.mark.odoo_required def test_proxy_access_when_connected(self, test_config): """Test accessing proxies when connected.""" conn = OdooConnection(test_config) try: conn.connect() # Should not raise db_proxy = conn.db_proxy common_proxy = conn.common_proxy object_proxy = conn.object_proxy assert db_proxy is not None assert common_proxy is not None assert object_proxy is not None finally: conn.disconnect() def test_proxy_access_when_not_connected(self, test_config): """Test accessing proxies when not connected.""" conn = OdooConnection(test_config) with pytest.raises(OdooConnectionError, match="Not connected"): _ = conn.db_proxy with pytest.raises(OdooConnectionError, match="Not connected"): _ = conn.common_proxy with pytest.raises(OdooConnectionError, match="Not connected"): _ = conn.object_proxy class TestOdooConnectionContext: """Test context manager functionality.""" @pytest.mark.odoo_required def test_context_manager_success(self, test_config): """Test using connection as context manager.""" with OdooConnection(test_config) as conn: assert conn.is_connected # Test that we can use the connection is_healthy, _ = conn.check_health() assert is_healthy # Should be disconnected after context assert not conn.is_connected @pytest.mark.odoo_required def test_context_manager_with_error(self, test_config): """Test context manager with error in context.""" conn = OdooConnection(test_config) try: with conn: assert conn.is_connected raise ValueError("Test error") except ValueError: pass # Should still be disconnected assert not conn.is_connected @pytest.mark.odoo_required def test_create_connection_helper(self, test_config): """Test create_connection helper function.""" with create_connection(test_config) as conn: assert conn.is_connected assert isinstance(conn, OdooConnection) assert not conn.is_connected class TestOdooConnectionIntegration: """Integration tests with real Odoo server.""" @pytest.mark.integration def test_real_server_version(self, test_config): """Test getting version from real server.""" with create_connection(test_config) as conn: version = conn.common_proxy.version() assert isinstance(version, dict) assert "server_version" in version assert "protocol_version" in version @pytest.mark.integration def test_real_server_db_list(self, test_config): """Test listing databases from real server.""" with create_connection(test_config) as conn: # Note: This might fail if db listing is disabled try: db_list = conn.db_proxy.list() assert isinstance(db_list, list) except Exception as e: # DB listing might be disabled for security assert "Access Denied" in str(e) or "not allowed" in str(e)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ivnvxd/mcp-server-odoo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server