Skip to main content
Glama

ConceptNet MCP Server

by infinitnet
test_exceptions.pyโ€ข20.5 kB
""" Comprehensive unit tests for the exceptions module. This test suite verifies all exception classes, error codes, utility functions, and edge cases in the enhanced exceptions module. """ import pytest import sys from unittest.mock import patch from src.conceptnet_mcp.utils.exceptions import ( ConceptNetMCPError, ConceptNetAPIError, ConceptNotFoundError, InvalidConceptURIError, InvalidLanguageError, RateLimitExceededError, NetworkTimeoutError, AuthenticationError, ValidationError, MCPToolError, PaginationError, ConfigurationError, ErrorCode, create_concept_not_found_error, create_validation_error, create_api_error_from_response, get_exception_for_error_code, EXCEPTION_REGISTRY, ) class TestErrorCode: """Test the ErrorCode enumeration.""" def test_error_code_values(self): """Test that error codes have correct values.""" assert ErrorCode.UNKNOWN_ERROR.value == 1000 assert ErrorCode.API_RATE_LIMIT_ERROR.value == 2002 assert ErrorCode.CONCEPT_NOT_FOUND.value == 3000 assert ErrorCode.VALIDATION_FIELD_REQUIRED.value == 4000 assert ErrorCode.TOOL_EXECUTION_ERROR.value == 5000 assert ErrorCode.PAGINATION_INVALID_OFFSET.value == 6000 def test_error_code_names(self): """Test that error codes have correct names.""" assert ErrorCode.CONCEPT_NOT_FOUND.name == "CONCEPT_NOT_FOUND" assert ErrorCode.API_RATE_LIMIT_ERROR.name == "API_RATE_LIMIT_ERROR" class TestConceptNetMCPError: """Test the base ConceptNetMCPError class.""" def test_basic_creation(self): """Test basic error creation.""" error = ConceptNetMCPError("Test error") assert str(error) == "[UNKNOWN_ERROR] Test error" assert error.message == "Test error" assert error.error_code == ErrorCode.UNKNOWN_ERROR assert error.details == {} assert error.suggestions == [] assert error.context == {} def test_creation_with_all_parameters(self): """Test error creation with all parameters.""" details = {"key": "value"} suggestions = ["Try this", "Try that"] context = {"module": "test"} error = ConceptNetMCPError( "Test error", error_code=ErrorCode.CONFIGURATION_ERROR, details=details, suggestions=suggestions, context=context ) assert error.message == "Test error" assert error.error_code == ErrorCode.CONFIGURATION_ERROR assert error.details == details assert error.suggestions == suggestions assert error.context == context def test_add_context(self): """Test adding context information.""" error = ConceptNetMCPError("Test error") result = error.add_context("key", "value") assert result is error # Method chaining assert error.context["key"] == "value" def test_add_suggestion(self): """Test adding suggestion.""" error = ConceptNetMCPError("Test error") result = error.add_suggestion("Try this") assert result is error # Method chaining assert "Try this" in error.suggestions def test_to_dict(self): """Test dictionary serialization.""" error = ConceptNetMCPError( "Test error", error_code=ErrorCode.VALIDATION_FIELD_INVALID, details={"field": "test"}, suggestions=["Fix it"], context={"module": "test"} ) result = error.to_dict() expected = { 'error_type': 'ConceptNetMCPError', 'message': 'Test error', 'error_code': 'VALIDATION_FIELD_INVALID', 'error_code_value': 4001, 'details': {'field': 'test'}, 'suggestions': ['Fix it'], 'context': {'module': 'test'} } # Remove dynamic context information for comparison for key in ['file', 'function', 'line']: result['context'].pop(key, None) expected['context'].pop(key, None) assert result == expected def test_str_with_suggestions(self): """Test string representation with suggestions.""" error = ConceptNetMCPError("Test error") error.add_suggestion("Try this") error.add_suggestion("Try that") result = str(error) assert "[UNKNOWN_ERROR] Test error" in result assert "Suggestions: Try this; Try that" in result class TestConceptNetAPIError: """Test the ConceptNetAPIError class.""" def test_basic_creation(self): """Test basic API error creation.""" error = ConceptNetAPIError("API failed") assert error.status_code is None assert error.response_data == {} assert error.error_code == ErrorCode.API_CONNECTION_ERROR def test_with_status_codes(self): """Test error code determination based on status codes.""" # Rate limit error rate_limit = ConceptNetAPIError("Rate limited", status_code=429) assert rate_limit.error_code == ErrorCode.API_RATE_LIMIT_ERROR # Client error client_error = ConceptNetAPIError("Bad request", status_code=400) assert client_error.error_code == ErrorCode.API_CLIENT_ERROR # Server error server_error = ConceptNetAPIError("Server error", status_code=500) assert server_error.error_code == ErrorCode.API_SERVER_ERROR def test_with_full_context(self): """Test API error with full context.""" error = ConceptNetAPIError( "API failed", status_code=404, response_data={"error": "Not found"}, endpoint="/api/test", method="GET", request_id="123" ) assert error.status_code == 404 assert error.response_data == {"error": "Not found"} assert error.endpoint == "/api/test" assert error.method == "GET" assert error.request_id == "123" assert error.context["api_endpoint"] == "/api/test" assert error.context["status_code"] == 404 def test_suggestions_based_on_status(self): """Test that appropriate suggestions are generated.""" # 404 error not_found = ConceptNetAPIError("Not found", status_code=404) assert any("verify" in s.lower() for s in not_found.suggestions) # 429 error rate_limit = ConceptNetAPIError("Rate limited", status_code=429) assert any("wait" in s.lower() for s in rate_limit.suggestions) # 500 error server_error = ConceptNetAPIError("Server error", status_code=500) assert any("retry" in s.lower() for s in server_error.suggestions) class TestConceptNotFoundError: """Test the ConceptNotFoundError class.""" def test_basic_creation(self): """Test basic concept not found error.""" error = ConceptNotFoundError("test_concept") assert "test_concept" in error.message assert error.concept == "test_concept" assert error.language is None assert error.similar_concepts == [] assert error.error_code == ErrorCode.CONCEPT_NOT_FOUND def test_with_language(self): """Test with language specified.""" error = ConceptNotFoundError("test_concept", language="es") assert "test_concept" in error.message assert "es" in error.message assert error.language == "es" def test_with_similar_concepts(self): """Test with similar concepts.""" similar = ["concept1", "concept2", "concept3"] error = ConceptNotFoundError("test", similar_concepts=similar) assert error.similar_concepts == similar assert any("similar" in s.lower() for s in error.suggestions) def test_with_normalized_term(self): """Test with normalized term.""" error = ConceptNotFoundError("test", normalized_term="test_normalized") assert error.normalized_term == "test_normalized" assert any("normalized" in s.lower() for s in error.suggestions) class TestInvalidConceptURIError: """Test the InvalidConceptURIError class.""" def test_basic_creation(self): """Test basic invalid URI error.""" error = InvalidConceptURIError("invalid_uri") assert "invalid_uri" in error.message assert error.uri == "invalid_uri" assert error.error_code == ErrorCode.CONCEPT_URI_INVALID def test_with_expected_format(self): """Test with expected format.""" error = InvalidConceptURIError("invalid", expected_format="/c/lang/term") assert "/c/lang/term" in error.message assert error.expected_format == "/c/lang/term" def test_with_validation_errors(self): """Test with validation errors.""" validation_errors = ["Error 1", "Error 2"] error = InvalidConceptURIError("invalid", validation_errors=validation_errors) assert error.validation_errors == validation_errors class TestInvalidLanguageError: """Test the InvalidLanguageError class.""" def test_basic_creation(self): """Test basic invalid language error.""" error = InvalidLanguageError("xx") assert "xx" in error.message assert error.language == "xx" assert error.error_code == ErrorCode.CONCEPT_LANGUAGE_INVALID def test_with_supported_languages(self): """Test with supported languages list.""" supported = ["en", "es", "fr"] error = InvalidLanguageError("xx", supported_languages=supported) assert error.supported_languages == supported assert any("supported languages" in s.lower() for s in error.suggestions) def test_with_suggested_languages(self): """Test with suggested languages.""" suggested = ["en", "es"] error = InvalidLanguageError("xx", suggested_languages=suggested) assert error.suggested_languages == suggested assert any("did you mean" in s.lower() for s in error.suggestions) class TestRateLimitExceededError: """Test the RateLimitExceededError class.""" def test_basic_creation(self): """Test basic rate limit error.""" error = RateLimitExceededError() assert "rate limit exceeded" in error.message.lower() assert error.status_code == 429 assert error.error_code == ErrorCode.API_RATE_LIMIT_ERROR def test_with_retry_after(self): """Test with retry after parameter.""" error = RateLimitExceededError(retry_after=60) assert "60 seconds" in error.message assert error.retry_after == 60 assert any("wait at least 60" in s.lower() for s in error.suggestions) def test_with_rate_limit_info(self): """Test with rate limit information.""" error = RateLimitExceededError(limit=100, window="hour", current_usage=150) assert error.limit == 100 assert error.window == "hour" assert error.current_usage == 150 class TestValidationError: """Test the ValidationError class.""" def test_basic_creation(self): """Test basic validation error.""" error = ValidationError("field_name", "invalid_value", "valid format") assert "field_name" in error.message assert "invalid_value" in error.message assert "valid format" in error.message assert error.field == "field_name" assert error.value == "invalid_value" assert error.expected == "valid format" def test_error_code_determination(self): """Test error code determination based on expected text.""" # Required field required_error = ValidationError("field", None, "required field") assert required_error.error_code == ErrorCode.VALIDATION_FIELD_REQUIRED # Type mismatch type_error = ValidationError("field", "text", "integer type") assert type_error.error_code == ErrorCode.VALIDATION_TYPE_MISMATCH # Range error range_error = ValidationError("field", 200, "value between 1 and 100") assert range_error.error_code == ErrorCode.VALIDATION_VALUE_OUT_OF_RANGE # Generic validation error generic_error = ValidationError("field", "bad", "good format") assert generic_error.error_code == ErrorCode.VALIDATION_FIELD_INVALID def test_with_constraints(self): """Test with constraints information.""" constraints = {"min": 1, "max": 100} error = ValidationError("number", 200, "range", constraints=constraints) assert error.constraints == constraints assert any("constraints" in s.lower() for s in error.suggestions) class TestMCPToolError: """Test the MCPToolError class.""" def test_basic_creation(self): """Test basic tool error.""" error = MCPToolError("test_tool", "execution failed") assert "test_tool" in error.message assert "execution failed" in error.message assert error.tool_name == "test_tool" assert error.error_message == "execution failed" assert error.error_code == ErrorCode.TOOL_EXECUTION_ERROR def test_with_execution_stage(self): """Test with execution stage.""" error = MCPToolError("tool", "failed", execution_stage="validation") assert "validation" in error.message assert error.execution_stage == "validation" def test_with_inner_exception(self): """Test with inner exception chaining.""" inner = ValueError("Inner error") error = MCPToolError("tool", "failed", inner_exception=inner) assert error.inner_exception == inner assert error.__cause__ == inner assert error.context["inner_exception_type"] == "ValueError" assert error.context["inner_exception_message"] == "Inner error" class TestPaginationError: """Test the PaginationError class.""" def test_basic_creation(self): """Test basic pagination error.""" error = PaginationError("Pagination failed") assert "Pagination failed" in error.message assert error.partial_results == [] def test_error_code_determination(self): """Test error code based on message content.""" offset_error = PaginationError("Invalid offset value") assert offset_error.error_code == ErrorCode.PAGINATION_INVALID_OFFSET limit_error = PaginationError("Invalid limit value") assert limit_error.error_code == ErrorCode.PAGINATION_INVALID_LIMIT not_found_error = PaginationError("Page not found") assert not_found_error.error_code == ErrorCode.PAGINATION_PAGE_NOT_FOUND merge_error = PaginationError("Failed to merge results") assert merge_error.error_code == ErrorCode.PAGINATION_MERGE_ERROR def test_with_partial_results(self): """Test with partial results.""" partial = [{"id": 1}, {"id": 2}] error = PaginationError("Failed", partial_results=partial) assert error.partial_results == partial assert any("partial results (2 items)" in s for s in error.suggestions) class TestUtilityFunctions: """Test utility functions for exception creation.""" def test_create_concept_not_found_error(self): """Test concept not found error creation utility.""" error = create_concept_not_found_error("test", "en", ["similar1", "similar2"]) assert isinstance(error, ConceptNotFoundError) assert error.concept == "test" assert error.language == "en" assert error.similar_concepts == ["similar1", "similar2"] def test_create_validation_error_with_type(self): """Test validation error creation with type.""" error = create_validation_error("field", "value", str) assert isinstance(error, ValidationError) assert error.field == "field" assert error.value == "value" assert error.expected == "str" def test_create_validation_error_with_string(self): """Test validation error creation with string.""" error = create_validation_error("field", "value", "string format") assert error.expected == "string format" @patch('src.conceptnet_mcp.utils.exceptions.logging') def test_create_api_error_from_response(self, mock_logging): """Test API error creation from response.""" # Mock response object class MockResponse: status_code = 404 headers = {'X-Request-ID': 'req123'} text = "Not found" def json(self): return {"error": "Resource not found"} response = MockResponse() error = create_api_error_from_response(response, "/api/test", "GET") assert isinstance(error, ConceptNetAPIError) assert error.status_code == 404 assert error.endpoint == "/api/test" assert error.method == "GET" assert error.request_id == "req123" assert error.response_data == {"error": "Resource not found"} def test_get_exception_for_error_code(self): """Test getting exception class for error code.""" assert get_exception_for_error_code(ErrorCode.CONCEPT_NOT_FOUND) == ConceptNotFoundError assert get_exception_for_error_code(ErrorCode.API_RATE_LIMIT_ERROR) == RateLimitExceededError assert get_exception_for_error_code(ErrorCode.VALIDATION_FIELD_INVALID) == ValidationError # Unknown error code should return base class assert get_exception_for_error_code(ErrorCode.UNKNOWN_ERROR) == ConceptNetMCPError class TestExceptionRegistry: """Test the exception registry.""" def test_registry_completeness(self): """Test that all error codes are in the registry.""" for error_code in ErrorCode: assert error_code in EXCEPTION_REGISTRY def test_registry_mappings(self): """Test specific registry mappings.""" assert EXCEPTION_REGISTRY[ErrorCode.CONCEPT_NOT_FOUND] == ConceptNotFoundError assert EXCEPTION_REGISTRY[ErrorCode.API_RATE_LIMIT_ERROR] == RateLimitExceededError assert EXCEPTION_REGISTRY[ErrorCode.VALIDATION_FIELD_INVALID] == ValidationError assert EXCEPTION_REGISTRY[ErrorCode.TOOL_EXECUTION_ERROR] == MCPToolError assert EXCEPTION_REGISTRY[ErrorCode.PAGINATION_INVALID_OFFSET] == PaginationError class TestExceptionInheritance: """Test exception inheritance hierarchy.""" def test_inheritance_hierarchy(self): """Test that all exceptions inherit correctly.""" # All custom exceptions should inherit from ConceptNetMCPError assert issubclass(ConceptNetAPIError, ConceptNetMCPError) assert issubclass(ConceptNotFoundError, ConceptNetMCPError) assert issubclass(InvalidConceptURIError, ConceptNetMCPError) assert issubclass(ValidationError, ConceptNetMCPError) assert issubclass(MCPToolError, ConceptNetMCPError) assert issubclass(PaginationError, ConceptNetMCPError) # API-specific exceptions should inherit from ConceptNetAPIError assert issubclass(RateLimitExceededError, ConceptNetAPIError) assert issubclass(NetworkTimeoutError, ConceptNetAPIError) assert issubclass(AuthenticationError, ConceptNetAPIError) # All should ultimately inherit from Exception assert issubclass(ConceptNetMCPError, Exception) def test_exception_catching(self): """Test that exceptions can be caught by their parent classes.""" # Specific exception can be caught by base class try: raise ConceptNotFoundError("test") except ConceptNetMCPError: pass # Should catch it # API exception can be caught by API base class try: raise RateLimitExceededError() except ConceptNetAPIError: pass # Should catch it # All can be caught by Exception try: raise ValidationError("field", "value", "expected") except Exception: pass # Should catch it if __name__ == "__main__": pytest.main([__file__])

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/infinitnet/conceptnet-mcp'

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