"""Unit tests for BackgroundLoaderService. Tests task queuing, status tracking, and worker logic in isolation. """ import asyncio from datetime import datetime from unittest.mock import AsyncMock, Mock, patch import pytest from src.server.services.background_loader_service import ( BackgroundLoaderService, LoadingStatus, SeriesLoadingTask, ) @pytest.fixture def mock_websocket_service(): """Mock WebSocket service.""" service = Mock() service.broadcast = AsyncMock() return service @pytest.fixture def mock_anime_service(): """Mock anime service.""" service = Mock() service.rescan = AsyncMock() return service @pytest.fixture def mock_series_app(): """Mock SeriesApp.""" app = Mock() app.directory_to_search = "/test/anime" app.nfo_service = Mock() app.nfo_service.create_tvshow_nfo = AsyncMock() return app @pytest.fixture async def background_loader(mock_websocket_service, mock_anime_service, mock_series_app): """Create BackgroundLoaderService instance.""" service = BackgroundLoaderService( websocket_service=mock_websocket_service, anime_service=mock_anime_service, series_app=mock_series_app ) yield service await service.stop() class TestBackgroundLoaderService: """Test suite for BackgroundLoaderService.""" @pytest.mark.asyncio async def test_service_initialization(self, background_loader): """Test service initializes correctly.""" assert background_loader.task_queue is not None assert isinstance(background_loader.active_tasks, dict) assert len(background_loader.active_tasks) == 0 @pytest.mark.asyncio async def test_start_worker(self, background_loader): """Test worker starts successfully.""" await background_loader.start() assert background_loader.worker_task is not None assert not background_loader.worker_task.done() @pytest.mark.asyncio async def test_stop_worker_gracefully(self, background_loader): """Test worker stops gracefully.""" await background_loader.start() await background_loader.stop() assert background_loader.worker_task.done() @pytest.mark.asyncio async def test_add_series_loading_task(self, background_loader): """Test adding a series to the loading queue.""" await background_loader.add_series_loading_task( key="test-series", folder="Test Series", name="Test Series", year=2024 ) # Verify task in active tasks assert "test-series" in background_loader.active_tasks task = background_loader.active_tasks["test-series"] assert task.key == "test-series" assert task.status == LoadingStatus.PENDING @pytest.mark.asyncio async def test_duplicate_task_handling(self, background_loader): """Test that duplicate tasks for same series are handled correctly.""" key = "test-series" await background_loader.add_series_loading_task( key=key, folder="Test Series", name="Test Series" ) await background_loader.add_series_loading_task( key=key, folder="Test Series", name="Test Series" ) # Verify only one task exists assert len([k for k in background_loader.active_tasks if k == key]) == 1 @pytest.mark.asyncio async def test_check_missing_data_all_missing( self, background_loader, mock_series_app ): """Test checking for missing data when all data is missing.""" with patch('src.server.database.service.AnimeSeriesService.get_by_key') as mock_get: mock_series = Mock() mock_series.episodes_loaded = False mock_series.has_nfo = False mock_series.logo_loaded = False mock_series.images_loaded = False mock_get.return_value = mock_series mock_db = AsyncMock() missing_data = await background_loader.check_missing_data( key="test-series", folder="Test Series", anime_directory="/test/anime", db=mock_db ) assert missing_data["episodes"] is True assert missing_data["nfo"] is True assert missing_data["logo"] is True assert missing_data["images"] is True @pytest.mark.asyncio async def test_broadcast_status(self, background_loader, mock_websocket_service): """Test status broadcasting via WebSocket.""" task = SeriesLoadingTask( key="test-series", folder="Test Series", name="Test Series", status=LoadingStatus.LOADING_EPISODES ) await background_loader._broadcast_status(task) # Verify broadcast was called mock_websocket_service.broadcast.assert_called_once() # Verify message structure call_args = mock_websocket_service.broadcast.call_args[0][0] assert call_args["type"] == "series_loading_update" assert call_args["key"] == "test-series" assert call_args["loading_status"] == "loading_episodes" class TestSeriesLoadingTask: """Test SeriesLoadingTask model.""" def test_task_initialization(self): """Test task initializes with correct defaults.""" task = SeriesLoadingTask( key="test", folder="Test", name="Test" ) assert task.key == "test" assert task.status == LoadingStatus.PENDING assert not any(task.progress.values()) def test_task_progress_tracking(self): """Test progress tracking updates correctly.""" task = SeriesLoadingTask( key="test", folder="Test", name="Test" ) task.progress["episodes"] = True assert task.progress["episodes"] is True assert not task.progress["nfo"] assert not task.progress["logo"] assert not task.progress["images"] def test_loading_status_enum(self): """Test LoadingStatus enum values.""" assert LoadingStatus.PENDING == "pending" assert LoadingStatus.LOADING_EPISODES == "loading_episodes" assert LoadingStatus.LOADING_NFO == "loading_nfo" assert LoadingStatus.COMPLETED == "completed" assert LoadingStatus.FAILED == "failed"