trax/tests/test_concrete_services.py

346 lines
14 KiB
Python

"""Unit tests for concrete service implementations."""
import pytest
from pathlib import Path
from typing import Any, Dict, List
from unittest.mock import AsyncMock, MagicMock, patch
from src.services.protocols import (
YouTubeServiceProtocol,
MediaServiceProtocol,
TranscriptionServiceProtocol,
EnhancementServiceProtocol,
ExportServiceProtocol,
BatchProcessorProtocol,
validate_protocol_implementation,
get_missing_methods,
)
class TestYouTubeServiceImplementation:
"""Test YouTube service protocol implementation."""
@pytest.fixture
def mock_youtube_service(self):
"""Create a mock YouTube service that implements the protocol."""
service = MagicMock()
# Mock the required methods
service.extract_metadata = AsyncMock(return_value={
"title": "Test Video",
"duration": 120,
"channel": "Test Channel"
})
service.batch_extract = AsyncMock(return_value=[
{"success": True, "data": {"title": "Video 1"}, "url": "http://example.com/1"},
{"success": True, "data": {"title": "Video 2"}, "url": "http://example.com/2"}
])
return service
def test_youtube_service_protocol_compliance(self, mock_youtube_service):
"""Test that YouTube service implements the protocol correctly."""
from src.services.protocols import YouTubeServiceProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_youtube_service, YouTubeServiceProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_youtube_service, YouTubeServiceProtocol)
assert len(missing_methods) == 0
@pytest.mark.asyncio
async def test_youtube_service_methods(self, mock_youtube_service):
"""Test YouTube service method calls."""
# Test extract_metadata
result = await mock_youtube_service.extract_metadata("http://example.com/video")
assert isinstance(result, dict)
assert "title" in result
# Test batch_extract
results = await mock_youtube_service.batch_extract(["http://example.com/1", "http://example.com/2"])
assert isinstance(results, list)
assert len(results) == 2
assert all("success" in result for result in results)
class TestMediaServiceImplementation:
"""Test Media service protocol implementation."""
@pytest.fixture
def mock_media_service(self):
"""Create a mock Media service that implements the protocol."""
service = MagicMock()
# Mock all required methods
service.download_media = AsyncMock()
service.preprocess_audio = AsyncMock(return_value=True)
service.validate_file_size = AsyncMock(return_value=True)
service.check_audio_quality = AsyncMock(return_value=True)
service.get_media_info = AsyncMock(return_value={"duration": 120, "format": "mp4"})
service.create_media_file_record = AsyncMock()
service.update_media_file_status = AsyncMock()
service.get_media_file_by_id = AsyncMock()
service.get_pending_media_files = AsyncMock(return_value=[])
service.get_ready_media_files = AsyncMock(return_value=[])
service.process_media_pipeline = AsyncMock()
service.get_telemetry_data = MagicMock(return_value=[])
service.clear_telemetry_data = MagicMock()
return service
def test_media_service_protocol_compliance(self, mock_media_service):
"""Test that Media service implements the protocol correctly."""
from src.services.protocols import MediaServiceProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_media_service, MediaServiceProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_media_service, MediaServiceProtocol)
assert len(missing_methods) == 0
@pytest.mark.asyncio
async def test_media_service_methods(self, mock_media_service):
"""Test Media service method calls."""
# Test download_media
await mock_media_service.download_media("http://example.com/video", Path("/tmp"))
mock_media_service.download_media.assert_called_once()
# Test preprocess_audio
result = await mock_media_service.preprocess_audio(Path("/input.wav"), Path("/output.wav"))
assert result is True
# Test validate_file_size
result = await mock_media_service.validate_file_size(Path("/test.mp4"))
assert result is True
# Test get_media_info
info = await mock_media_service.get_media_info(Path("/test.mp4"))
assert isinstance(info, dict)
assert "duration" in info
class TestTranscriptionServiceImplementation:
"""Test Transcription service protocol implementation."""
@pytest.fixture
def mock_transcription_service(self):
"""Create a mock Transcription service that implements the protocol."""
service = MagicMock()
# Mock all required methods
service.transcribe_file = AsyncMock()
service.transcribe_audio = AsyncMock()
service.create_transcription_job = AsyncMock()
service.get_job_status = AsyncMock()
service.cancel_job = AsyncMock(return_value=True)
return service
def test_transcription_service_protocol_compliance(self, mock_transcription_service):
"""Test that Transcription service implements the protocol correctly."""
from src.services.protocols import TranscriptionServiceProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_transcription_service, TranscriptionServiceProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_transcription_service, TranscriptionServiceProtocol)
assert len(missing_methods) == 0
@pytest.mark.asyncio
async def test_transcription_service_methods(self, mock_transcription_service):
"""Test Transcription service method calls."""
# Test transcribe_file
await mock_transcription_service.transcribe_file(MagicMock())
mock_transcription_service.transcribe_file.assert_called_once()
# Test transcribe_audio
await mock_transcription_service.transcribe_audio(Path("/test.wav"))
mock_transcription_service.transcribe_audio.assert_called_once()
# Test cancel_job
result = await mock_transcription_service.cancel_job("job-id")
assert result is True
class TestEnhancementServiceImplementation:
"""Test Enhancement service protocol implementation."""
@pytest.fixture
def mock_enhancement_service(self):
"""Create a mock Enhancement service that implements the protocol."""
service = MagicMock()
# Mock all required methods
service.initialize = AsyncMock()
service.enhance_transcript = AsyncMock()
service.enhance_transcript_batch = AsyncMock()
service.enhance_transcription_result = AsyncMock()
return service
def test_enhancement_service_protocol_compliance(self, mock_enhancement_service):
"""Test that Enhancement service implements the protocol correctly."""
from src.services.protocols import EnhancementServiceProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_enhancement_service, EnhancementServiceProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_enhancement_service, EnhancementServiceProtocol)
assert len(missing_methods) == 0
@pytest.mark.asyncio
async def test_enhancement_service_methods(self, mock_enhancement_service):
"""Test Enhancement service method calls."""
# Test initialize
await mock_enhancement_service.initialize()
mock_enhancement_service.initialize.assert_called_once()
# Test enhance_transcript
await mock_enhancement_service.enhance_transcript("test transcript")
mock_enhancement_service.enhance_transcript.assert_called_once()
# Test enhance_transcript_batch
await mock_enhancement_service.enhance_transcript_batch(["transcript1", "transcript2"])
mock_enhancement_service.enhance_transcript_batch.assert_called_once()
class TestExportServiceImplementation:
"""Test Export service protocol implementation."""
@pytest.fixture
def mock_export_service(self):
"""Create a mock Export service that implements the protocol."""
service = MagicMock()
# Mock all required methods
service.export_transcript = AsyncMock()
service.export_batch = AsyncMock()
service.get_supported_formats = MagicMock(return_value=["json", "txt", "srt"])
return service
def test_export_service_protocol_compliance(self, mock_export_service):
"""Test that Export service implements the protocol correctly."""
from src.services.protocols import ExportServiceProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_export_service, ExportServiceProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_export_service, ExportServiceProtocol)
assert len(missing_methods) == 0
def test_export_service_methods(self, mock_export_service):
"""Test Export service method calls."""
# Test get_supported_formats
formats = mock_export_service.get_supported_formats()
assert isinstance(formats, list)
assert "json" in formats
# Test export_transcript
mock_export_service.export_transcript(MagicMock(), Path("/output"), "json")
mock_export_service.export_transcript.assert_called_once()
class TestBatchProcessorImplementation:
"""Test Batch processor protocol implementation."""
@pytest.fixture
def mock_batch_processor(self):
"""Create a mock Batch processor that implements the protocol."""
processor = MagicMock()
# Mock all required methods
processor.add_task = AsyncMock(return_value="task-id")
processor.process_tasks = AsyncMock()
processor.get_progress = AsyncMock()
processor.cancel_task = AsyncMock(return_value=True)
processor.get_task_status = AsyncMock()
processor.get_completed_tasks = AsyncMock(return_value=[])
return processor
def test_batch_processor_protocol_compliance(self, mock_batch_processor):
"""Test that Batch processor implements the protocol correctly."""
from src.services.protocols import BatchProcessorProtocol
# Test protocol validation
assert validate_protocol_implementation(mock_batch_processor, BatchProcessorProtocol)
# Test that no methods are missing
missing_methods = get_missing_methods(mock_batch_processor, BatchProcessorProtocol)
assert len(missing_methods) == 0
@pytest.mark.asyncio
async def test_batch_processor_methods(self, mock_batch_processor):
"""Test Batch processor method calls."""
# Test add_task
task_id = await mock_batch_processor.add_task("transcription", {"url": "test"})
assert task_id == "task-id"
# Test process_tasks
await mock_batch_processor.process_tasks(max_workers=4)
mock_batch_processor.process_tasks.assert_called_once_with(max_workers=4)
# Test cancel_task
result = await mock_batch_processor.cancel_task("task-id")
assert result is True
class TestProtocolValidationUtilities:
"""Test protocol validation utility functions."""
def test_validate_protocol_implementation_with_valid_instance(self):
"""Test protocol validation with a valid instance."""
class ValidService:
def extract_metadata(self, url: str) -> Dict[str, Any]:
return {"title": "Test"}
def batch_extract(self, urls: List[str]) -> List[Dict[str, Any]]:
return [{"title": "Test"}]
service = ValidService()
from src.services.protocols import YouTubeServiceProtocol
# This should work at runtime since we have the required methods
# Note: This is a runtime check, not a static type check
assert hasattr(service, 'extract_metadata')
assert hasattr(service, 'batch_extract')
def test_get_missing_methods_with_incomplete_implementation(self):
"""Test getting missing methods from incomplete implementation."""
class IncompleteService:
def extract_metadata(self, url: str) -> Dict[str, Any]:
return {"title": "Test"}
# Missing batch_extract method
service = IncompleteService()
from src.services.protocols import YouTubeServiceProtocol
missing_methods = get_missing_methods(service, YouTubeServiceProtocol)
assert "batch_extract" in missing_methods
def test_get_missing_methods_with_complete_implementation(self):
"""Test getting missing methods from complete implementation."""
class CompleteService:
def extract_metadata(self, url: str) -> Dict[str, Any]:
return {"title": "Test"}
def batch_extract(self, urls: List[str]) -> List[Dict[str, Any]]:
return [{"title": "Test"}]
service = CompleteService()
from src.services.protocols import YouTubeServiceProtocol
missing_methods = get_missing_methods(service, YouTubeServiceProtocol)
assert len(missing_methods) == 0
if __name__ == "__main__":
pytest.main([__file__])