"""Unit tests for ITranslationService interface contract.""" import pytest from abc import ABC from unittest.mock import Mock from src.domain.interfaces.translation import ITranslationService from src.domain.models.translation_request import TranslationRequest from src.domain.models.text_content import TextContent class TestITranslationService: """Test cases for ITranslationService interface contract.""" def test_interface_is_abstract(self): """Test that ITranslationService is an abstract base class.""" assert issubclass(ITranslationService, ABC) # Should not be able to instantiate directly with pytest.raises(TypeError): ITranslationService() # type: ignore def test_interface_has_required_method(self): """Test that interface defines the required abstract method.""" # Check that the method exists and is abstract assert hasattr(ITranslationService, 'translate') assert getattr(ITranslationService.translate, '__isabstractmethod__', False) def test_method_signature(self): """Test that the method has the correct signature.""" import inspect method = ITranslationService.translate signature = inspect.signature(method) # Check parameter names params = list(signature.parameters.keys()) expected_params = ['self', 'request'] assert params == expected_params # Check return annotation assert signature.return_annotation == "'TextContent'" def test_concrete_implementation_must_implement_method(self): """Test that concrete implementations must implement the abstract method.""" class IncompleteImplementation(ITranslationService): pass # Should not be able to instantiate without implementing abstract method with pytest.raises(TypeError, match="Can't instantiate abstract class"): IncompleteImplementation() # type: ignore def test_concrete_implementation_with_method(self): """Test that concrete implementation with method can be instantiated.""" class ConcreteImplementation(ITranslationService): def translate(self, request): return TextContent(text="translated text", language=request.target_language) # Should be able to instantiate implementation = ConcreteImplementation() assert isinstance(implementation, ITranslationService) def test_method_contract_with_mock(self): """Test the method contract using a mock implementation.""" class MockImplementation(ITranslationService): def __init__(self): self.mock_method = Mock() def translate(self, request): return self.mock_method(request) # Create test data source_text = TextContent(text="Hello world", language="en") request = TranslationRequest( source_text=source_text, target_language="es" ) expected_result = TextContent(text="Hola mundo", language="es") # Setup mock implementation = MockImplementation() implementation.mock_method.return_value = expected_result # Call method result = implementation.translate(request) # Verify call and result implementation.mock_method.assert_called_once_with(request) assert result == expected_result def test_interface_docstring_requirements(self): """Test that the interface method has proper documentation.""" method = ITranslationService.translate assert method.__doc__ is not None docstring = method.__doc__ # Check that docstring contains key information assert "Translate text from source language to target language" in docstring assert "Args:" in docstring assert "Returns:" in docstring assert "Raises:" in docstring assert "TranslationFailedException" in docstring def test_interface_type_hints(self): """Test that the interface uses proper type hints.""" method = ITranslationService.translate annotations = getattr(method, '__annotations__', {}) assert 'request' in annotations assert 'return' in annotations # Check that type annotations are correct assert annotations['request'] == "'TranslationRequest'" assert annotations['return'] == "'TextContent'" def test_multiple_implementations_possible(self): """Test that multiple implementations of the interface are possible.""" class NLLBImplementation(ITranslationService): def translate(self, request): return TextContent(text="NLLB translation", language=request.target_language) class GoogleImplementation(ITranslationService): def translate(self, request): return TextContent(text="Google translation", language=request.target_language) nllb = NLLBImplementation() google = GoogleImplementation() assert isinstance(nllb, ITranslationService) assert isinstance(google, ITranslationService) assert type(nllb) != type(google) def test_interface_method_can_be_called_polymorphically(self): """Test that interface methods can be called polymorphically.""" class TestImplementation(ITranslationService): def __init__(self, translation_prefix): self.translation_prefix = translation_prefix def translate(self, request): translated_text = f"{self.translation_prefix}: {request.source_text.text}" return TextContent(text=translated_text, language=request.target_language) # Create different implementations implementations = [ TestImplementation("Provider1"), TestImplementation("Provider2") ] # Test polymorphic usage source_text = TextContent(text="Hello", language="en") request = TranslationRequest(source_text=source_text, target_language="es") results = [] for impl in implementations: # Can call the same method on different implementations result = impl.translate(request) results.append(result.text) assert results == ["Provider1: Hello", "Provider2: Hello"] def test_interface_inheritance_chain(self): """Test the inheritance chain of the interface.""" # Check that it inherits from ABC assert ABC in ITranslationService.__mro__ # Check that it's at the right position in MRO mro = ITranslationService.__mro__ assert mro[0] == ITranslationService assert ABC in mro def test_method_parameter_validation_in_implementation(self): """Test that implementations can validate parameters.""" class ValidatingImplementation(ITranslationService): def translate(self, request): if not isinstance(request, TranslationRequest): raise TypeError("request must be TranslationRequest") # Validate that source and target languages are different if request.effective_source_language == request.target_language: raise ValueError("Source and target languages cannot be the same") return TextContent( text=f"Translated: {request.source_text.text}", language=request.target_language ) impl = ValidatingImplementation() # Valid call should work source_text = TextContent(text="Hello", language="en") request = TranslationRequest(source_text=source_text, target_language="es") result = impl.translate(request) assert result.text == "Translated: Hello" assert result.language == "es" # Invalid parameter type should raise error with pytest.raises(TypeError, match="request must be TranslationRequest"): impl.translate("not a request") # type: ignore # Same language should raise error same_lang_text = TextContent(text="Hello", language="en") same_lang_request = TranslationRequest(source_text=same_lang_text, target_language="en") with pytest.raises(ValueError, match="Source and target languages cannot be the same"): impl.translate(same_lang_request) def test_implementation_can_handle_different_language_pairs(self): """Test that implementations can handle different language pairs.""" class MultiLanguageImplementation(ITranslationService): def __init__(self): self.translations = { ("en", "es"): {"Hello": "Hola", "world": "mundo"}, ("en", "fr"): {"Hello": "Bonjour", "world": "monde"}, ("es", "en"): {"Hola": "Hello", "mundo": "world"}, ("fr", "en"): {"Bonjour": "Hello", "monde": "world"} } def translate(self, request): source_lang = request.effective_source_language target_lang = request.target_language translation_dict = self.translations.get((source_lang, target_lang), {}) # Simple word-by-word translation for testing words = request.source_text.text.split() translated_words = [translation_dict.get(word, word) for word in words] translated_text = " ".join(translated_words) return TextContent(text=translated_text, language=target_lang) impl = MultiLanguageImplementation() # Test different language pairs test_cases = [ ("Hello world", "en", "es", "Hola mundo"), ("Hello world", "en", "fr", "Bonjour monde"), ("Hola mundo", "es", "en", "Hello world"), ("Bonjour monde", "fr", "en", "Hello world") ] for text, source_lang, target_lang, expected in test_cases: source_text = TextContent(text=text, language=source_lang) request = TranslationRequest( source_text=source_text, target_language=target_lang, source_language=source_lang ) result = impl.translate(request) assert result.text == expected assert result.language == target_lang def test_implementation_can_handle_auto_detect_source_language(self): """Test that implementations can handle auto-detection of source language.""" class AutoDetectImplementation(ITranslationService): def translate(self, request): # Use the effective source language (from TextContent if not explicitly set) source_lang = request.effective_source_language target_lang = request.target_language # Simple mock translation based on detected language if source_lang == "en" and target_lang == "es": translated_text = f"ES: {request.source_text.text}" elif source_lang == "es" and target_lang == "en": translated_text = f"EN: {request.source_text.text}" else: translated_text = f"UNKNOWN: {request.source_text.text}" return TextContent(text=translated_text, language=target_lang) impl = AutoDetectImplementation() # Test with explicit source language source_text = TextContent(text="Hello", language="en") explicit_request = TranslationRequest( source_text=source_text, target_language="es", source_language="en" ) result = impl.translate(explicit_request) assert result.text == "ES: Hello" assert result.language == "es" # Test with auto-detected source language (None) auto_request = TranslationRequest( source_text=source_text, # language="en" in TextContent target_language="es" # source_language=None (default) ) result = impl.translate(auto_request) assert result.text == "ES: Hello" # Should use language from TextContent assert result.language == "es" assert auto_request.is_auto_detect_source is True