Skip to main content
Glama
test_tools_match.py20.2 kB
import pytest import pytest_asyncio from unittest.mock import patch, MagicMock from src.tools.match import ( find_matches_by_match_score, find_matches_by_confidence, get_total_matches, get_total_matches_by_entity_type, find_potential_matches, get_potential_match_apis ) @pytest_asyncio.fixture(autouse=True) def mock_dependencies(): with patch("src.tools.match.MatchScoreRequest") as mock_match_score_request, \ patch("src.tools.match.ConfidenceLevelRequest") as mock_confidence_request, \ patch("src.tools.match.GetTotalMatchesRequest") as mock_total_matches_request, \ patch("src.tools.match.GetMatchFacetsRequest") as mock_match_facets_request, \ patch("src.tools.match.get_reltio_headers") as mock_get_headers, \ patch("src.tools.match.get_reltio_url") as mock_get_url, \ patch("src.tools.match.validate_connection_security") as mock_validate_security, \ patch("src.tools.match.http_request") as mock_http_request, \ patch("src.tools.match.create_error_response") as mock_create_error_response: mock_match_score_request.return_value = MagicMock() mock_confidence_request.return_value = MagicMock() mock_total_matches_request.return_value = MagicMock() mock_match_facets_request.return_value = MagicMock() mock_get_headers.return_value = {"Authorization": "Bearer token"} mock_get_url.return_value = "https://reltio.com/entities/_search" mock_validate_security.return_value = None mock_http_request.return_value = [{"result": "some result"}] mock_create_error_response.side_effect = lambda code, msg: {"error": code, "message": msg} yield { "match_score_request": mock_match_score_request, "confidence_request": mock_confidence_request, "total_matches_request": mock_total_matches_request, "match_facets_request": mock_match_facets_request, "get_headers": mock_get_headers, "get_url": mock_get_url, "validate_security": mock_validate_security, "http_request": mock_http_request, "create_error_response": mock_create_error_response, } @pytest.mark.asyncio async def test_find_matches_by_match_score_success(mock_dependencies): # Patch the MatchScoreRequest to return a real object with int fields mock_dependencies["match_score_request"].return_value.max_results = 10 mock_dependencies["match_score_request"].return_value.offset = 0 # Mock http_request to return a list of dicts as expected mock_dependencies["http_request"].return_value = [ {"uri": "entities/1", "label": "Entity 1", "type": "Individual"}, {"uri": "entities/2", "label": "Entity 2", "type": "Individual"} ] result = await find_matches_by_match_score(10, 90) if isinstance(result, str): import yaml result = yaml.safe_load(result) assert isinstance(result, list) assert all("uri" in r and "label" in r and "type" in r for r in result) @pytest.mark.asyncio async def test_find_matches_by_match_score_validation_error(mock_dependencies): mock_dependencies["match_score_request"].side_effect = ValueError("Invalid range") result = await find_matches_by_match_score() assert result["error"] == "VALIDATION_ERROR" @pytest.mark.asyncio async def test_find_matches_by_confidence_success(mock_dependencies): # Patch the ConfidenceLevelRequest to return a real object with int fields mock_dependencies["confidence_request"].return_value.max_results = 10 mock_dependencies["confidence_request"].return_value.offset = 0 # Mock http_request to return a list of dicts as expected mock_dependencies["http_request"].return_value = [ {"uri": "entities/1", "label": "Entity 1", "type": "Individual"}, {"uri": "entities/2", "label": "Entity 2", "type": "Individual"} ] result = await find_matches_by_confidence("High confidence") if isinstance(result, str): import yaml result = yaml.safe_load(result) assert isinstance(result, list) assert all("uri" in r and "label" in r and "type" in r for r in result) @pytest.mark.asyncio async def test_find_matches_by_confidence_validation_error(mock_dependencies): mock_dependencies["confidence_request"].side_effect = ValueError("Invalid confidence") result = await find_matches_by_confidence() assert result["error"] == "VALIDATION_ERROR" @pytest.mark.asyncio async def test_find_matches_by_confidence_no_results(mock_dependencies): # Patch the ConfidenceLevelRequest to return a real object with int fields mock_dependencies["confidence_request"].return_value.max_results = 10 mock_dependencies["confidence_request"].return_value.offset = 0 mock_dependencies["http_request"].return_value = [] result = await find_matches_by_confidence("Low confidence") if isinstance(result, str): import yaml result = yaml.safe_load(result) assert "results" in result assert result["results"] == [] @pytest.mark.asyncio async def test_find_matches_by_match_score_http_exception(mock_dependencies): mock_dependencies["http_request"].side_effect = Exception("HTTP failed") result = await find_matches_by_match_score() assert result["error"] == "SERVER_ERROR" @pytest.mark.asyncio async def test_get_total_matches_success(mock_dependencies): # Configure the mock to return a specific response for total matches mock_dependencies["http_request"].return_value = {"total": 1114} mock_dependencies["get_url"].return_value = "https://reltio.com/entities/_total" # Configure the min_matches property on the request mock mock_dependencies["total_matches_request"].return_value.min_matches = 0 # Call the function result = await get_total_matches(0) # Assert the result assert "total" in result assert result["total"] == 1114 assert result["min_matches"] == 0 assert "message" in result @pytest.mark.asyncio async def test_get_total_matches_with_filter(mock_dependencies): # Configure the mock to return a specific response mock_dependencies["http_request"].return_value = {"total": 500} mock_dependencies["get_url"].return_value = "https://reltio.com/entities/_total" # Set the expected request object properties mock_dependencies["total_matches_request"].return_value.min_matches = 5 # Call the function with a filter result = await get_total_matches(5) # Assert the payload contains the correct filter mock_dependencies["http_request"].assert_called_once() assert "total" in result assert result["total"] == 500 assert result["min_matches"] == 5 @pytest.mark.asyncio async def test_get_total_matches_validation_error(mock_dependencies): # Configure the mock to raise a validation error mock_dependencies["total_matches_request"].side_effect = ValueError("Invalid min_matches") # Call the function result = await get_total_matches(-1) # Invalid value # Assert the error response assert result["error"] == "VALIDATION_ERROR" assert "Invalid" in result["message"] @pytest.mark.asyncio async def test_get_total_matches_api_error(mock_dependencies): # Configure the mock to return an invalid response mock_dependencies["http_request"].return_value = {"not_total": "missing total field"} # Call the function result = await get_total_matches(0) # Assert the error response assert "error" in result assert result["error"] == "RESPONSE_ERROR" @pytest.mark.asyncio async def test_get_total_matches_http_exception(mock_dependencies): # Configure the mock to raise an exception mock_dependencies["http_request"].side_effect = Exception("HTTP failed") # Call the function result = await get_total_matches(0) # Assert the error response assert result["error"] == "SERVER_ERROR" assert "Failed to retrieve total matches count" in result["message"] @pytest.mark.asyncio async def test_get_total_matches_by_entity_type_success(mock_dependencies): # Configure the mock to return a specific response for facets mock_dependencies["http_request"].return_value = {"type": {"Individual": 56, "Organization": 1058}} mock_dependencies["get_url"].return_value = "https://reltio.com/entities/_facets" # Configure the min_matches property on the request mock mock_dependencies["match_facets_request"].return_value.min_matches = 0 # Call the function result = await get_total_matches_by_entity_type(0) # Assert the result assert "type_counts" in result assert result["type_counts"] == {"Individual": 56, "Organization": 1058} assert result["min_matches"] == 0 assert "message" in result @pytest.mark.asyncio async def test_get_total_matches_by_entity_type_with_filter(mock_dependencies): # Configure the mock to return a specific response mock_dependencies["http_request"].return_value = {"type": {"Individual": 20, "Organization": 500}} mock_dependencies["get_url"].return_value = "https://reltio.com/entities/_facets" # Set the expected request object properties mock_dependencies["match_facets_request"].return_value.min_matches = 5 # Call the function with a filter result = await get_total_matches_by_entity_type(5) # Assert the http_request was called with correct parameters mock_dependencies["http_request"].assert_called_once() assert "type_counts" in result assert result["type_counts"] == {"Individual": 20, "Organization": 500} assert result["min_matches"] == 5 @pytest.mark.asyncio async def test_get_total_matches_by_entity_type_validation_error(mock_dependencies): # Configure the mock to raise a validation error mock_dependencies["match_facets_request"].side_effect = ValueError("Invalid min_matches") # Call the function result = await get_total_matches_by_entity_type(-1) # Invalid value # Assert the error response assert result["error"] == "VALIDATION_ERROR" assert "Invalid" in result["message"] @pytest.mark.asyncio async def test_get_total_matches_by_entity_type_api_error(mock_dependencies): # Configure the mock to return an invalid response mock_dependencies["http_request"].return_value = {"not_type": "missing type field"} # Call the function result = await get_total_matches_by_entity_type(0) # Assert the error response assert "error" in result assert result["error"] == "RESPONSE_ERROR" @pytest.mark.asyncio async def test_get_total_matches_by_entity_type_http_exception(mock_dependencies): # Configure the mock to raise an exception mock_dependencies["http_request"].side_effect = Exception("HTTP failed") # Call the function result = await get_total_matches_by_entity_type(0) # Assert the error response assert result["error"] == "SERVER_ERROR" assert "Failed to retrieve match facets" in result["message"] @pytest.mark.asyncio class TestFindPotentialMatches: """Test suite for find_potential_matches function""" @patch("src.tools.match.ActivityLog.execute_and_log_activity") @patch("src.tools.match.http_request") @patch("src.tools.match.validate_connection_security") @patch("src.tools.match.get_reltio_headers") @patch("src.tools.match.get_reltio_url") @patch("src.tools.match.UnifiedMatchRequest") async def test_find_potential_matches_by_match_rule_success(self, mock_request_model, mock_get_url, mock_headers, mock_validate, mock_http, mock_activity_log): """Test successful search by match rule""" # Setup mocks mock_request_model.return_value.search_type = "match_rule" mock_request_model.return_value.filter = "BaseRule05" mock_request_model.return_value.entity_type = "Individual" mock_request_model.return_value.tenant_id = "test-tenant" mock_request_model.return_value.max_results = 10 mock_request_model.return_value.offset = 0 mock_request_model.return_value.search_filters = "" mock_get_url.return_value = "https://api/entities/_search" mock_headers.return_value = {"Authorization": "Bearer token"} mock_http.return_value = [ {"uri": "entities/123", "label": "Test Entity", "type": "configuration/entityTypes/Individual"} ] result = await find_potential_matches("match_rule", "BaseRule05", "Individual", "test-tenant", 10, 0, "") # Verify result structure import yaml parsed_result = yaml.safe_load(result) if isinstance(result, str) else result assert isinstance(parsed_result, list) assert parsed_result[0]["uri"] == "entities/123" @patch("src.tools.match.ActivityLog.execute_and_log_activity") @patch("src.tools.match.http_request") @patch("src.tools.match.validate_connection_security") @patch("src.tools.match.get_reltio_headers") @patch("src.tools.match.get_reltio_url") @patch("src.tools.match.UnifiedMatchRequest") async def test_find_potential_matches_by_score_success(self, mock_request_model, mock_get_url, mock_headers, mock_validate, mock_http, mock_activity_log): """Test successful search by score range""" # Setup mocks mock_request_model.return_value.search_type = "score" mock_request_model.return_value.filter = "50,100" mock_request_model.return_value.entity_type = "Individual" mock_request_model.return_value.tenant_id = "test-tenant" mock_request_model.return_value.max_results = 10 mock_request_model.return_value.offset = 0 mock_request_model.return_value.search_filters = "" mock_get_url.return_value = "https://api/entities/_search" mock_headers.return_value = {"Authorization": "Bearer token"} mock_http.return_value = [ {"uri": "entities/456", "label": "Test Entity 2", "type": "configuration/entityTypes/Individual"} ] result = await find_potential_matches("score", "50,100", "Individual", "test-tenant", 10, 0, "") # Verify result structure import yaml parsed_result = yaml.safe_load(result) if isinstance(result, str) else result assert isinstance(parsed_result, list) assert parsed_result[0]["uri"] == "entities/456" @patch("src.tools.match.UnifiedMatchRequest", side_effect=ValueError("Invalid search type")) async def test_find_potential_matches_validation_error(self, mock_request_model): """Test validation error handling""" result = await find_potential_matches("invalid_type", "filter", "Individual", "test-tenant", 10, 0, "") # Check that we got an error response assert isinstance(result, dict) assert "error" in result # Handle both error formats: {"error": "CODE"} or {"error": {"code_key": "CODE"}} if isinstance(result["error"], str): # Simple string error format assert "ERROR" in result["error"].upper() else: # Nested dict error format assert result["error"]["code_key"] == "VALIDATION_ERROR" @patch("src.tools.match.http_request") @patch("src.tools.match.validate_connection_security") @patch("src.tools.match.get_reltio_headers") @patch("src.tools.match.get_reltio_url") @patch("src.tools.match.UnifiedMatchRequest") async def test_find_potential_matches_no_results(self, mock_request_model, mock_get_url, mock_headers, mock_validate, mock_http): """Test handling of empty results""" # Setup mocks mock_request_model.return_value.search_type = "match_rule" mock_request_model.return_value.filter = "NoMatchRule" mock_request_model.return_value.entity_type = "Individual" mock_request_model.return_value.tenant_id = "test-tenant" mock_request_model.return_value.max_results = 10 mock_request_model.return_value.offset = 0 mock_request_model.return_value.search_filters = "" mock_get_url.return_value = "https://api/entities/_search" mock_headers.return_value = {"Authorization": "Bearer token"} mock_http.return_value = [] result = await find_potential_matches("match_rule", "NoMatchRule", "Individual", "test-tenant", 10, 0, "") # Verify empty results message assert "message" in result assert "results" in result assert result["results"] == [] @pytest.mark.asyncio class TestGetPotentialMatchApis: """Test suite for get_potential_match_apis function""" @patch("src.tools.match.ActivityLog.execute_and_log_activity") @patch("src.tools.match.http_request") @patch("src.tools.match.validate_connection_security") @patch("src.tools.match.get_reltio_headers") @patch("src.tools.match.get_reltio_url") @patch("src.tools.match.GetTotalMatchesRequest") async def test_get_potential_match_apis_success(self, mock_request_model, mock_get_url, mock_headers, mock_validate, mock_http, mock_activity_log): """Test successful retrieval of match statistics""" # Setup mocks mock_request_model.return_value.min_matches = 0 mock_request_model.return_value.tenant_id = "test-tenant" mock_get_url.return_value = "https://api/entities" mock_headers.return_value = {"Authorization": "Bearer token"} # Mock HTTP call with facet response mock_http.return_value = { "totalItems": 100, "type": {"Individual": 60, "Organization": 40}, # Entity type facets as dict "matchRules": {"BaseRule05": 50} # Match rule facets as dict } mock_activity_log.return_value = None result = await get_potential_match_apis(0, "test-tenant") # Verify result structure import yaml parsed_result = yaml.safe_load(result) if isinstance(result, str) else result assert "totalItems" in parsed_result assert parsed_result["totalItems"] == 100 assert "type" in parsed_result assert "matchRules" in parsed_result assert "total_matches" in parsed_result assert parsed_result["total_matches"] == 100 @patch("src.tools.match.GetTotalMatchesRequest", side_effect=ValueError("Invalid min_matches")) async def test_get_potential_match_apis_validation_error(self, mock_request_model): """Test validation error handling""" result = await get_potential_match_apis(-1, "test-tenant") # Check that we got an error response assert isinstance(result, dict) assert "error" in result # Handle both error formats: {"error": "CODE"} or {"error": {"code_key": "CODE"}} if isinstance(result["error"], str): # Simple string error format assert "ERROR" in result["error"].upper() else: # Nested dict error format assert result["error"]["code_key"] == "VALIDATION_ERROR" @patch("src.tools.match.http_request", side_effect=Exception("HTTP Error")) @patch("src.tools.match.validate_connection_security") @patch("src.tools.match.get_reltio_headers") @patch("src.tools.match.get_reltio_url") @patch("src.tools.match.GetTotalMatchesRequest") async def test_get_potential_match_apis_http_error(self, mock_request_model, mock_get_url, mock_headers, mock_validate, mock_http): """Test HTTP error handling""" # Setup mocks mock_request_model.return_value.min_matches = 0 mock_request_model.return_value.tenant_id = "test-tenant" mock_get_url.return_value = "https://api/entities" mock_headers.return_value = {"Authorization": "Bearer token"} result = await get_potential_match_apis(0, "test-tenant") # Should return error response import yaml parsed_result = yaml.safe_load(result) if isinstance(result, str) else result assert "error" in parsed_result

Latest Blog Posts

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/reltio-ai/reltio-mcp-server'

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