video-accessibility/backend/tests/unit/test_translate.py
2025-08-24 16:28:33 -05:00

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