# #region Test.LanguageDetectService [C:2] [TYPE Module] [SEMANTICS test,language,detection] # @BRIEF Tests for _lang_detect.py — language detection. # @RELATION BINDS_TO -> [LanguageDetectService] import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src")) import pytest from unittest.mock import MagicMock, patch from lingua import LanguageDetectorBuilder from src.plugins.translate._lang_detect import ( _detector_cache_key, build_detector, get_detector, detect_language, _character_block_fallback, batch_detect, ) class TestDetectorCacheKey: """_detector_cache_key.""" def test_none(self): """None returns '__default'.""" assert _detector_cache_key(None) == "__default" def test_empty_list(self): """Empty list returns '__default'.""" assert _detector_cache_key([]) == "__default" def test_sorted_key(self): """Languages are sorted for deterministic key.""" key = _detector_cache_key(["fr", "en", "de"]) assert key == "de,en,fr" def test_duplicates(self): """Duplicates are removed.""" key = _detector_cache_key(["en", "en", "fr"]) assert key == "en,fr" class TestBuildDetector: """build_detector.""" def test_with_target_languages(self): """Builds detector with specific languages.""" detector = build_detector(["ru", "de"]) assert detector is not None def test_no_target_languages(self): """No target languages builds with common source codes.""" detector = build_detector(None) assert detector is not None def test_empty_target_languages(self): """Empty list builds with common source codes.""" detector = build_detector([]) assert detector is not None def test_unknown_language_codes(self): """Unknown codes are ignored.""" detector = build_detector(["xx", "yy"]) # Should try to build; if no lingua_langs found, falls back to all spoken assert detector is not None def test_fallback_to_all_spoken(self): """When _CODE_TO_LANG has no matches, falls back to all spoken languages (line 93).""" with patch("src.plugins.translate._lang_detect._CODE_TO_LANG", {}): detector = build_detector(["en"]) assert detector is not None class TestGetDetector: """get_detector — cached detector.""" def test_cached(self): """Same key returns same detector.""" with patch("src.plugins.translate._lang_detect._detector_cache", {}): d1 = get_detector(["en", "fr"]) d2 = get_detector(["en", "fr"]) assert d1 is d2 def test_different_keys(self): """Different keys return different detectors.""" with patch("src.plugins.translate._lang_detect._detector_cache", {}): d1 = get_detector(["en"]) d2 = get_detector(["fr"]) assert d1 is not d2 def test_default_key(self): """None returns a detector.""" with patch("src.plugins.translate._lang_detect._detector_cache", {}): d = get_detector(None) assert d is not None class TestDetectLanguage: """detect_language.""" def test_empty_text(self): """Empty text returns 'und'.""" detector = build_detector(["en"]) assert detect_language("", detector) == "und" def test_whitespace_only(self): """Whitespace-only returns 'und'.""" detector = build_detector(["en"]) assert detect_language(" ", detector) == "und" def test_english_text(self): """English text detected.""" detector = build_detector(["en", "ru"]) result = detect_language("Hello, how are you today?", detector) assert result == "en" def test_russian_text(self): """Russian text detected.""" detector = build_detector(["ru", "en"]) result = detect_language("Привет, как дела?", detector) assert result == "ru" def test_exception_returns_und(self): """Exception in detection returns 'und'.""" detector = MagicMock() detector.detect_language_of.side_effect = Exception("error") result = detect_language("hello", detector) assert result == "und" def test_none_result(self): """None result from detector returns 'und'.""" detector = MagicMock() detector.detect_language_of.return_value = None result = detect_language("hello", detector) assert result == "und" class TestCharacterBlockFallback: """_character_block_fallback.""" def test_no_target_languages(self): """No target languages returns results unchanged.""" results = _character_block_fallback(["text"], ["und"], None) assert results == ["und"] def test_non_cyrillic_targets(self): """No Cyrillic targets returns results unchanged.""" results = _character_block_fallback(["text"], ["und"], ["en", "fr"]) assert results == ["und"] def test_no_und_results(self): """No 'und' results returns unchanged.""" results = _character_block_fallback(["text"], ["en"], ["ru"]) assert results == ["en"] def test_cyrillic_text_detected(self): """Cyrillic-dominant text gets assigned ru.""" results = _character_block_fallback( ["привет мир текст"], ["und"], ["ru", "en"], ) assert results[0] == "ru" def test_non_cyrillic_text_stays_und(self): """Non-Cyrillic text stays 'und'.""" results = _character_block_fallback( ["hello world 123"], ["und"], ["ru", "en"], ) assert results[0] == "und" def test_empty_text_stays_und(self): """Empty text stays 'und'.""" results = _character_block_fallback( [""], ["und"], ["ru"], ) assert results[0] == "und" class TestBatchDetect: """batch_detect.""" def test_basic_batch(self): """Detects languages for multiple texts.""" detector = build_detector(["en", "ru", "fr"]) results = batch_detect(["Hello", "Привет", "Bonjour"], detector=detector) assert len(results) == 3 def test_builds_detector_when_none(self): """Builds detector when not provided.""" results = batch_detect(["Hello"], target_languages=["en"]) assert len(results) == 1 def test_empty_texts(self): """Empty texts list returns empty list.""" results = batch_detect([]) assert results == [] def test_applies_fallback(self): """Cyrillic fallback is applied.""" results = batch_detect( ["привет мир"], target_languages=["ru"], ) assert len(results) == 1 # #endregion Test.LanguageDetectService