"""Tests for UnresolvedFolderService and UnresolvedFolder model.""" from datetime import datetime, timezone from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.server.database.models import UnresolvedFolder from src.server.database.service import UnresolvedFolderService class TestUnresolvedFolderModel: """Test UnresolvedFolder model.""" def test_is_resolved_false_when_no_key(self): """provider_key is None → is_resolved is False.""" folder = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, provider_key=None, resolved_at=None, ) assert folder.is_resolved is False def test_is_resolved_false_when_key_but_no_timestamp(self): """provider_key set but resolved_at is None → is_resolved is False.""" folder = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, provider_key="test-key", resolved_at=None, ) assert folder.is_resolved is False def test_is_resolved_true_when_both_set(self): """Both provider_key and resolved_at set → is_resolved is True.""" folder = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, provider_key="test-key", resolved_at=datetime.now(timezone.utc), ) assert folder.is_resolved is True def test_validate_folder_name_empty_raises(self): """Empty folder_name → raises ValueError during construction.""" with pytest.raises(ValueError, match="Folder name cannot be empty"): UnresolvedFolder( folder_name="", title="Test", ) def test_validate_folder_name_too_long_raises(self): """Folder name > 1000 chars → raises ValueError during construction.""" long_name = "x" * 1001 with pytest.raises(ValueError, match="Folder name must be 1000 characters"): UnresolvedFolder( folder_name=long_name, title="Test", ) def test_validate_title_empty_raises(self): """Empty title → raises ValueError during construction.""" with pytest.raises(ValueError, match="Title cannot be empty"): UnresolvedFolder( folder_name="Test (2020)", title="", ) class TestUnresolvedFolderService: """Test UnresolvedFolderService methods.""" @pytest.mark.asyncio async def test_create(self): """Creates a new unresolved folder record.""" mock_db = AsyncMock() mock_db.flush = AsyncMock() mock_db.refresh = AsyncMock() folder = await UnresolvedFolderService.create( db=mock_db, folder_name="Test (2020)", title="Test", year=2020, search_attempts=1, ) assert folder.folder_name == "Test (2020)" assert folder.title == "Test" assert folder.year == 2020 assert folder.search_attempts == 1 mock_db.add.assert_called_once() mock_db.flush.assert_called_once() @pytest.mark.asyncio async def test_get_by_folder_name_found(self): """Found → returns UnresolvedFolder.""" mock_db = AsyncMock() mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, ) mock_db.execute.return_value = mock_result folder = await UnresolvedFolderService.get_by_folder_name(mock_db, "Test (2020)") assert folder is not None assert folder.folder_name == "Test (2020)" @pytest.mark.asyncio async def test_get_by_folder_name_not_found(self): """Not found → returns None.""" mock_db = AsyncMock() mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_db.execute.return_value = mock_result folder = await UnresolvedFolderService.get_by_folder_name(mock_db, "Unknown") assert folder is None @pytest.mark.asyncio async def test_get_all_unresolved(self): """Returns only unresolved folders (no provider_key).""" mock_db = AsyncMock() mock_result = MagicMock() mock_result.scalars.return_value.all.return_value = [ UnresolvedFolder(folder_name="Folder1", title="Title1", year=2020), UnresolvedFolder(folder_name="Folder2", title="Title2", year=2021), ] mock_db.execute.return_value = mock_result folders = await UnresolvedFolderService.get_all_unresolved(mock_db) assert len(folders) == 2 mock_db.execute.assert_called_once() @pytest.mark.asyncio async def test_resolve(self): """Marks folder as resolved with provider_key.""" mock_db = AsyncMock() mock_db.flush = AsyncMock() mock_db.refresh = AsyncMock() existing = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, provider_key=None, resolved_at=None, ) with patch.object( UnresolvedFolderService, 'get_by_folder_name', new_callable=AsyncMock, return_value=existing ): result = await UnresolvedFolderService.resolve( mock_db, "Test (2020)", "test-key" ) assert result.provider_key == "test-key" assert result.resolved_at is not None mock_db.flush.assert_called_once() @pytest.mark.asyncio async def test_resolve_not_found(self): """Folder not found → returns None.""" mock_db = AsyncMock() with patch.object( UnresolvedFolderService, 'get_by_folder_name', new_callable=AsyncMock, return_value=None ): result = await UnresolvedFolderService.resolve( mock_db, "Unknown", "test-key" ) assert result is None @pytest.mark.asyncio async def test_delete(self): """Deletes unresolved folder record.""" mock_db = AsyncMock() mock_db.flush = AsyncMock() existing = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, ) with patch.object( UnresolvedFolderService, 'get_by_folder_name', new_callable=AsyncMock, return_value=existing ): result = await UnresolvedFolderService.delete(mock_db, "Test (2020)") assert result is True mock_db.delete.assert_called_once_with(existing) mock_db.flush.assert_called_once() @pytest.mark.asyncio async def test_delete_not_found(self): """Folder not found → returns False.""" mock_db = AsyncMock() with patch.object( UnresolvedFolderService, 'get_by_folder_name', new_callable=AsyncMock, return_value=None ): result = await UnresolvedFolderService.delete(mock_db, "Unknown") assert result is False @pytest.mark.asyncio async def test_update_search_result(self): """Increments search_attempts and updates last_search_result.""" mock_db = AsyncMock() mock_db.flush = AsyncMock() mock_db.refresh = AsyncMock() existing = UnresolvedFolder( folder_name="Test (2020)", title="Test", year=2020, search_attempts=1, last_search_result=None, ) with patch.object( UnresolvedFolderService, 'get_by_folder_name', new_callable=AsyncMock, return_value=existing ): result = await UnresolvedFolderService.update_search_result( mock_db, "Test (2020)", '[{"title": "Test"}]' ) assert result.search_attempts == 2 assert result.last_search_result == '[{"title": "Test"}]'