feat: add perform_nfo_repair_scan startup hook

This commit is contained in:
2026-02-22 11:16:25 +01:00
parent 3e5ad8a4a6
commit d71feb64dd
3 changed files with 190 additions and 1 deletions

View File

@@ -28,6 +28,7 @@ from src.server.services.initialization_service import (
perform_initial_setup,
perform_media_scan_if_needed,
perform_nfo_scan_if_needed,
perform_nfo_repair_scan,
)
@@ -757,3 +758,123 @@ class TestInitializationIntegration:
result2 = await perform_initial_setup()
assert result2 is False
class TestPerformNfoRepairScan:
"""Tests for the perform_nfo_repair_scan startup hook."""
@pytest.mark.asyncio
async def test_skips_without_tmdb_api_key(self, tmp_path):
"""Should return immediately when no TMDB API key is configured."""
mock_settings = MagicMock()
mock_settings.tmdb_api_key = ""
mock_settings.anime_directory = str(tmp_path)
with patch(
"src.server.services.initialization_service.settings", mock_settings
):
await perform_nfo_repair_scan()
# No exception means guard worked — nothing was iterated
@pytest.mark.asyncio
async def test_skips_without_anime_directory(self, tmp_path):
"""Should return immediately when no anime directory is configured."""
mock_settings = MagicMock()
mock_settings.tmdb_api_key = "some-key"
mock_settings.anime_directory = ""
with patch(
"src.server.services.initialization_service.settings", mock_settings
):
await perform_nfo_repair_scan()
@pytest.mark.asyncio
async def test_queues_deficient_series_via_background_loader(self, tmp_path):
"""Series with incomplete NFO should be queued via background_loader."""
# Create a fake series directory with a tvshow.nfo file
series_dir = tmp_path / "MyAnime"
series_dir.mkdir()
nfo_file = series_dir / "tvshow.nfo"
nfo_file.write_text("<tvshow><title>MyAnime</title></tvshow>")
mock_settings = MagicMock()
mock_settings.tmdb_api_key = "test-key"
mock_settings.anime_directory = str(tmp_path)
mock_background_loader = AsyncMock()
mock_background_loader.add_series_loading_task = AsyncMock()
with patch(
"src.server.services.initialization_service.settings", mock_settings
), patch(
"src.core.services.nfo_repair_service.nfo_needs_repair",
return_value=True,
), patch(
"src.core.services.nfo_factory.NFOServiceFactory"
) as mock_factory_cls:
mock_factory_cls.return_value.create.return_value = MagicMock()
await perform_nfo_repair_scan(background_loader=mock_background_loader)
mock_background_loader.add_series_loading_task.assert_called_once_with(
key="MyAnime", folder="MyAnime", name="MyAnime"
)
@pytest.mark.asyncio
async def test_skips_complete_series(self, tmp_path):
"""Series with complete NFO should not be queued or repaired."""
series_dir = tmp_path / "CompleteAnime"
series_dir.mkdir()
nfo_file = series_dir / "tvshow.nfo"
nfo_file.write_text("<tvshow><title>CompleteAnime</title></tvshow>")
mock_settings = MagicMock()
mock_settings.tmdb_api_key = "test-key"
mock_settings.anime_directory = str(tmp_path)
mock_background_loader = AsyncMock()
mock_background_loader.add_series_loading_task = AsyncMock()
with patch(
"src.server.services.initialization_service.settings", mock_settings
), patch(
"src.core.services.nfo_repair_service.nfo_needs_repair",
return_value=False,
), patch(
"src.core.services.nfo_factory.NFOServiceFactory"
) as mock_factory_cls:
mock_factory_cls.return_value.create.return_value = MagicMock()
await perform_nfo_repair_scan(background_loader=mock_background_loader)
mock_background_loader.add_series_loading_task.assert_not_called()
@pytest.mark.asyncio
async def test_repairs_directly_without_background_loader(self, tmp_path):
"""When no background_loader provided, repair_series is called directly."""
series_dir = tmp_path / "NeedsRepair"
series_dir.mkdir()
nfo_file = series_dir / "tvshow.nfo"
nfo_file.write_text("<tvshow><title>NeedsRepair</title></tvshow>")
mock_settings = MagicMock()
mock_settings.tmdb_api_key = "test-key"
mock_settings.anime_directory = str(tmp_path)
mock_repair_service = AsyncMock()
mock_repair_service.repair_series = AsyncMock(return_value=True)
with patch(
"src.server.services.initialization_service.settings", mock_settings
), patch(
"src.core.services.nfo_repair_service.nfo_needs_repair",
return_value=True,
), patch(
"src.core.services.nfo_factory.NFOServiceFactory"
) as mock_factory_cls, patch(
"src.core.services.nfo_repair_service.NfoRepairService",
return_value=mock_repair_service,
):
mock_factory_cls.return_value.create.return_value = MagicMock()
await perform_nfo_repair_scan(background_loader=None)
mock_repair_service.repair_series.assert_called_once()