"""Limbă & voce în voice mode pe voci pocket-tts (English-only). Regula (preferința lui Marius, 2026-07-11): vocea configurată NU se schimbă niciodată din cauza limbii. Diacriticele RO scăpate într-un răspuns (nume proprii ca „Constanța", cuvinte românești) se transliterează la ASCII ca pocket-tts să le accepte — fără fallback pe Supertonic, fără clauze mute. """ import queue from unittest.mock import patch from src.voice.tts_stream import TTSQueue from tools.tts import _RO_DIACRITICS, fold_ro_diacritics def _drain_items(q: queue.Queue) -> list: items = [] while True: try: items.append(q.get_nowait()) except queue.Empty: return items class TestFoldRoDiacritics: def test_folds_all_romanian_diacritics(self): assert fold_ro_diacritics("ăâîșț ĂÂÎȘȚ şţ ŞŢ") == "aaist AAIST st ST" def test_proper_noun(self): assert fold_ro_diacritics("Constanța") == "Constanta" def test_ascii_untouched(self): text = "Tomorrow in Constanta: sunny, 28 degrees." assert fold_ro_diacritics(text) == text class TestPushTextLangRouting: @patch("src.voice.tts_stream.engine_for_voice", return_value="pockettts") def test_diacritics_folded_voice_kept_on_pockettts(self, _eng): ttsq = TTSQueue(voice_id="Marius 4") ttsq.push_text("Tomorrow in Constanța it will be sunny, around 28 degrees.") items = _drain_items(ttsq._text_queue) assert items, "clauzele trebuie să ajungă în coadă" for clause, voice, lang in items: assert voice == "Marius 4" assert lang == "en" assert not any(ch in _RO_DIACRITICS for ch in clause) @patch("src.voice.tts_stream.engine_for_voice", return_value="pockettts") def test_full_romanian_text_still_keeps_configured_voice(self, _eng): ttsq = TTSQueue(voice_id="Marius 4") ttsq.push_text("Ora acum e nouă și cinci minute dimineața.") items = _drain_items(ttsq._text_queue) assert items for clause, voice, lang in items: assert voice == "Marius 4" assert lang == "en" assert not any(ch in _RO_DIACRITICS for ch in clause) @patch("src.voice.tts_stream.engine_for_voice", return_value="supertonic") def test_supertonic_voice_keeps_romanian(self, _eng): ttsq = TTSQueue(voice_id="M2") ttsq.push_text("Ora acum e nouă și cinci minute dimineața.") items = _drain_items(ttsq._text_queue) assert items joined = " ".join(clause for clause, _, _ in items) assert any(ch in _RO_DIACRITICS for ch in joined), "diacriticele rămân pe Supertonic" for clause, voice, lang in items: assert voice == "M2" assert lang == "ro" class TestWorkerConsumesTuples: @patch("src.voice.tts_stream.synthesize", return_value={"ok": False, "error": "x"}) def test_worker_unpacks_clause_voice_lang(self, mock_synth): ttsq = TTSQueue(voice_id="Marius 4") ttsq._text_queue.put(("Hello there.", "Marius 4", "en")) ttsq.start() try: import time deadline = time.monotonic() + 2.0 while not mock_synth.called and time.monotonic() < deadline: time.sleep(0.02) finally: ttsq.stop() mock_synth.assert_called_once_with("Hello there.", voice="Marius 4", lang="en")