238 lines
No EOL
7.3 KiB
Python
238 lines
No EOL
7.3 KiB
Python
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 |