Files
wehub-resource-sync bf2343b7e4
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:35:45 +08:00

289 lines
9.6 KiB
Python

# Copyright 2025 Collate
# Licensed under the Collate Community License, Version 1.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# https://github.com/open-metadata/OpenMetadata/blob/main/ingestion/LICENSE
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Unit tests for MCP connection module
"""
from unittest.mock import MagicMock, patch
import pytest
from metadata.generated.schema.entity.services.connections.mcp.mcpConnection import (
DiscoveryMethod,
McpConnection,
McpServerConfig,
McpType,
TransportType,
)
from metadata.ingestion.source.mcp.client import McpProtocolError, McpServerInfo
from metadata.ingestion.source.mcp.connection import (
McpConnectionManager,
get_connection,
)
@pytest.fixture
def config_file_connection():
"""McpConnection configured for config file discovery"""
return McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.ConfigFile,
configFilePaths=["~/test/config.json"],
connectionTimeout=30,
initializationTimeout=60,
)
@pytest.fixture
def direct_connection():
"""McpConnection configured for direct connection"""
return McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.DirectConnection,
servers=[
McpServerConfig(
name="test-server",
transport=TransportType.Stdio,
command="npx",
args=["-y", "test-package"],
env={"DEBUG": "true"},
),
McpServerConfig(
name="http-server",
transport=TransportType.SSE,
url="http://localhost:8080",
),
],
connectionTimeout=30,
initializationTimeout=60,
)
@pytest.fixture
def registry_connection():
"""McpConnection configured for registry discovery"""
return McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.Registry,
registryUrl="http://registry.example.com",
)
class TestMcpConnectionManager:
"""Tests for McpConnectionManager class"""
def test_initialization(self, config_file_connection):
manager = McpConnectionManager(config_file_connection)
assert manager.connection == config_file_connection
assert manager._discovered_servers is None
def test_discover_servers_caches_result(self, direct_connection):
manager = McpConnectionManager(direct_connection)
servers1 = manager.discover_servers()
servers2 = manager.discover_servers()
assert servers1 is servers2
def test_discover_from_direct_config(self, direct_connection):
manager = McpConnectionManager(direct_connection)
servers = manager.discover_servers()
assert len(servers) == 2
stdio_server = next(s for s in servers if s.name == "test-server")
assert stdio_server.transport == "Stdio"
assert stdio_server.command == "npx"
assert stdio_server.args == ["-y", "test-package"]
assert stdio_server.env == {"DEBUG": "true"}
http_server = next(s for s in servers if s.name == "http-server")
assert http_server.transport == "SSE"
assert http_server.url == "http://localhost:8080"
@patch("metadata.ingestion.source.mcp.connection.discover_servers_from_config_files")
def test_discover_from_config_files(self, mock_discover, config_file_connection):
mock_discover.return_value = [
McpServerInfo(name="server1", command="cmd1"),
McpServerInfo(name="server2", command="cmd2"),
]
manager = McpConnectionManager(config_file_connection)
servers = manager.discover_servers()
assert len(servers) == 2
mock_discover.assert_called_once_with(["~/test/config.json"])
def test_discover_from_config_files_empty_paths(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.ConfigFile,
configFilePaths=[],
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert servers == []
def test_discover_from_registry_not_implemented(self, registry_connection):
manager = McpConnectionManager(registry_connection)
servers = manager.discover_servers()
assert servers == []
def test_discover_from_registry_no_url(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.Registry,
registryUrl=None,
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert servers == []
@patch("metadata.ingestion.source.mcp.connection.McpClient")
def test_connect_to_server(self, mock_client_class, direct_connection):
mock_client = MagicMock()
mock_client_class.return_value = mock_client
manager = McpConnectionManager(direct_connection)
server = McpServerInfo(name="test", command="echo")
client = manager.connect_to_server(server)
mock_client_class.assert_called_once_with(
server_config=server,
connection_timeout=30,
initialization_timeout=60,
)
mock_client.connect.assert_called_once()
mock_client.initialize.assert_called_once()
assert client == mock_client
@patch("metadata.ingestion.source.mcp.connection.McpClient")
def test_test_server_connection_success(self, mock_client_class, direct_connection):
mock_client = MagicMock()
mock_client_class.return_value = mock_client
manager = McpConnectionManager(direct_connection)
server = McpServerInfo(name="test", command="echo")
result = manager.test_server_connection(server)
assert result is True
mock_client.close.assert_called_once()
@patch("metadata.ingestion.source.mcp.connection.McpClient")
def test_test_server_connection_failure(self, mock_client_class, direct_connection):
mock_client = MagicMock()
mock_client.connect.side_effect = McpProtocolError("Connection failed")
mock_client_class.return_value = mock_client
manager = McpConnectionManager(direct_connection)
server = McpServerInfo(name="test", command="echo")
result = manager.test_server_connection(server)
assert result is False
@patch("metadata.ingestion.source.mcp.connection.McpClient")
def test_test_server_connection_closes_on_success(self, mock_client_class, direct_connection):
mock_client = MagicMock()
mock_client_class.return_value = mock_client
manager = McpConnectionManager(direct_connection)
server = McpServerInfo(name="test", command="echo")
result = manager.test_server_connection(server)
assert result is True
mock_client.close.assert_called_once()
class TestGetConnection:
"""Tests for get_connection function"""
def test_get_connection_returns_manager(self, direct_connection):
manager = get_connection(direct_connection)
assert isinstance(manager, McpConnectionManager)
assert manager.connection == direct_connection
def test_get_connection_with_config_file(self, config_file_connection):
manager = get_connection(config_file_connection)
assert isinstance(manager, McpConnectionManager)
assert manager.connection.discoveryMethod == DiscoveryMethod.ConfigFile
class TestDiscoverFromDirectConfig:
"""Tests for _discover_from_direct_config method"""
def test_handles_none_transport(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.DirectConnection,
servers=[
McpServerConfig(
name="test",
transport=None,
command="echo",
),
],
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert len(servers) == 1
assert servers[0].transport == "Stdio"
def test_handles_api_key(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.DirectConnection,
servers=[
McpServerConfig(
name="secure-server",
transport=TransportType.SSE,
url="http://localhost:8080",
apiKey="test-key", # NOSONAR
),
],
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert len(servers) == 1
assert servers[0].api_key == "test-key"
def test_handles_empty_servers_list(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.DirectConnection,
servers=[],
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert servers == []
def test_handles_none_servers(self):
connection = McpConnection(
type=McpType.Mcp,
discoveryMethod=DiscoveryMethod.DirectConnection,
servers=None,
)
manager = McpConnectionManager(connection)
servers = manager.discover_servers()
assert servers == []