from unittest.mock import MagicMock, patch import pytest from app.services.translate import TranslateService class TestTranslateService: """Test Google Translate service functionality""" @pytest.fixture def translate_service(self): """Create translate service with mocked client""" with patch('app.services.translate.translate.Client') as mock_client: service = TranslateService() service.client = MagicMock() return service @pytest.fixture def sample_vtt(self): """Sample VTT content for testing""" return """WEBVTT 00:00:01.000 --> 00:00:03.000 Hello everyone 00:00:04.000 --> 00:00:06.000 Welcome to our tutorial 00:00:07.000 --> 00:00:09.000 Let's get started """ @pytest.mark.asyncio async def test_translate_vtt_success(self, translate_service, sample_vtt): """Test successful VTT translation""" # Mock translation results translate_service.client.translate.return_value = [ {"translatedText": "Hola a todos"}, {"translatedText": "Bienvenidos a nuestro tutorial"}, {"translatedText": "Empecemos"} ] result = await translate_service.translate_vtt(sample_vtt, "es") # Verify structure is preserved assert "WEBVTT" in result assert "00:00:01.000 --> 00:00:03.000" in result assert "00:00:04.000 --> 00:00:06.000" in result assert "00:00:07.000 --> 00:00:09.000" in result # Verify translation content assert "Hola a todos" in result assert "Bienvenidos a nuestro tutorial" in result assert "Empecemos" in result @pytest.mark.asyncio async def test_translate_vtt_single_result(self, translate_service, sample_vtt): """Test VTT translation with single result format""" # Some Google Translate responses return single dict instead of list translate_service.client.translate.return_value = { "translatedText": "Hola a todos" } # Create VTT with single cue single_cue_vtt = """WEBVTT 00:00:01.000 --> 00:00:03.000 Hello everyone """ result = await translate_service.translate_vtt(single_cue_vtt, "es") assert "Hola a todos" in result assert "00:00:01.000 --> 00:00:03.000" in result @pytest.mark.asyncio async def test_translate_vtt_empty_content(self, translate_service): """Test translation of VTT with no cues""" empty_vtt = """WEBVTT """ result = await translate_service.translate_vtt(empty_vtt, "es") # Should return original content if no cues to translate assert result == empty_vtt @pytest.mark.asyncio async def test_translate_vtt_no_client(self): """Test translation when client is not configured""" service = TranslateService() service.client = None with pytest.raises(ValueError, match="Google Translate not configured"): await service.translate_vtt("WEBVTT\n", "es") @pytest.mark.asyncio async def test_translate_vtt_api_error(self, translate_service, sample_vtt): """Test handling of Google Translate API errors""" translate_service.client.translate.side_effect = Exception("API Error") with pytest.raises(Exception, match="API Error"): await translate_service.translate_vtt(sample_vtt, "es") def test_parse_vtt_cues_simple(self, translate_service): """Test parsing simple VTT cues""" vtt_content = """WEBVTT 00:00:01.000 --> 00:00:03.000 Hello world 00:00:04.000 --> 00:00:06.000 This is a test """ cues = translate_service._parse_vtt_cues(vtt_content) assert len(cues) == 2 assert cues[0]["start"] == "00:00:01.000" assert cues[0]["end"] == "00:00:03.000" assert cues[0]["text"] == "Hello world" assert cues[1]["text"] == "This is a test" def test_parse_vtt_cues_multiline(self, translate_service): """Test parsing VTT cues with multi-line text""" vtt_content = """WEBVTT 00:00:01.000 --> 00:00:03.000 First line Second line 00:00:04.000 --> 00:00:06.000 Another cue """ cues = translate_service._parse_vtt_cues(vtt_content) assert len(cues) == 2 # Note: Current implementation joins lines with space assert "First line Second line" in cues[0]["text"] def test_parse_vtt_cues_with_notes(self, translate_service): """Test parsing VTT with NOTE sections""" vtt_content = """WEBVTT NOTE This is a note section 00:00:01.000 --> 00:00:03.000 Hello world """ cues = translate_service._parse_vtt_cues(vtt_content) assert len(cues) == 1 assert cues[0]["text"] == "Hello world" def test_build_vtt_simple(self, translate_service): """Test building VTT from cues""" cues = [ { "start": "00:00:01.000", "end": "00:00:03.000", "text": "Hola mundo" }, { "start": "00:00:04.000", "end": "00:00:06.000", "text": "Esta es una prueba" } ] result = translate_service._build_vtt(cues) expected = """WEBVTT 00:00:01.000 --> 00:00:03.000 Hola mundo 00:00:04.000 --> 00:00:06.000 Esta es una prueba """ assert result == expected def test_build_vtt_empty(self, translate_service): """Test building VTT from empty cues""" cues = [] result = translate_service._build_vtt(cues) assert result == "WEBVTT\n" @pytest.mark.asyncio async def test_translate_vtt_preserves_timing(self, translate_service): """Test that translation preserves exact timing""" original_vtt = """WEBVTT 00:00:01.234 --> 00:00:03.567 Original text 00:00:05.890 --> 00:00:08.123 Another line """ translate_service.client.translate.return_value = [ {"translatedText": "Texto original"}, {"translatedText": "Otra línea"} ] result = await translate_service.translate_vtt(original_vtt, "es") # Check that exact timestamps are preserved assert "00:00:01.234 --> 00:00:03.567" in result assert "00:00:05.890 --> 00:00:08.123" in result assert "Texto original" in result assert "Otra línea" in result def test_service_initialization_with_api_key(self): """Test service initialization when API key is configured""" with patch('app.services.translate.settings') as mock_settings: mock_settings.translate_api_key = "test_api_key" with patch('app.services.translate.translate.Client') as mock_client: service = TranslateService() mock_client.assert_called_once() assert service.client is not None def test_service_initialization_without_api_key(self): """Test service initialization when API key is not configured""" with patch('app.services.translate.settings') as mock_settings: mock_settings.translate_api_key = "" service = TranslateService() assert service.client is None