Spaces:
Build error
Build error
"""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 |