- SerieScanner: Remove key file fallback, keep data file fallback - SystemSettings: Add legacy_key_cleanup_completed flag - initialization_service: Add cleanup task to remove key files from folders with DB entries - Tests updated to reflect key file removal from legacy path Key files caused duplicate key errors on folder rename. DB is now sole source of truth.
120 lines
4.8 KiB
Python
120 lines
4.8 KiB
Python
"""Tests for SerieScanner DB lookup functionality."""
|
|
|
|
import logging
|
|
import os
|
|
import tempfile
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.core.entities.series import Serie
|
|
from src.core.SerieScanner import SerieScanner
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_loader():
|
|
"""Create a mock Loader instance."""
|
|
loader = MagicMock()
|
|
loader.get_season_episode_count = MagicMock(return_value={1: 12})
|
|
loader.is_language = MagicMock(return_value=True)
|
|
loader.get_year = MagicMock(return_value=2026)
|
|
return loader
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_directory():
|
|
"""Create a temporary directory with subdirectories for testing."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
anime_folder = os.path.join(tmpdir, "Rooster Fighter (2026)")
|
|
os.makedirs(anime_folder, exist_ok=True)
|
|
mp4_path = os.path.join(anime_folder, "S01E001.mp4")
|
|
with open(mp4_path, "w") as f:
|
|
f.write("dummy mp4")
|
|
yield tmpdir
|
|
|
|
|
|
class TestGetSerieFromFolderDbLookup:
|
|
"""Test __read_data_from_file DB lookup behavior."""
|
|
|
|
def test_db_hit_returns_serie_from_db(self, temp_directory, mock_loader):
|
|
"""DB lookup resolves folder -> Serie returned."""
|
|
from src.server.database.models import AnimeSeries
|
|
from src.server.database import service as anime_series_service
|
|
|
|
mock_session = MagicMock()
|
|
mock_anime_series = MagicMock()
|
|
mock_anime_series.key = "rooster-fighter"
|
|
mock_anime_series.name = "Rooster Fighter"
|
|
mock_anime_series.site = "aniworld.to"
|
|
mock_anime_series.folder = "Rooster Fighter (2026)"
|
|
mock_anime_series.year = 2026
|
|
mock_anime_series.episodes = []
|
|
mock_session.execute.return_value.scalar_one_or_none.return_value = mock_anime_series
|
|
|
|
with patch("src.core.SerieScanner.get_sync_session", return_value=mock_session):
|
|
scanner = SerieScanner(temp_directory, mock_loader)
|
|
result = scanner._SerieScanner__read_data_from_file("Rooster Fighter (2026)")
|
|
|
|
assert result is not None
|
|
assert result.key == "rooster-fighter"
|
|
assert result.name == "Rooster Fighter"
|
|
assert result.year == 2026
|
|
|
|
def test_db_miss_falls_back_to_provider_callback(self, temp_directory, mock_loader):
|
|
"""DB miss -> _db_lookup callback called."""
|
|
lookup = MagicMock(return_value=Serie(
|
|
key="rooster-fighter",
|
|
name="Rooster Fighter",
|
|
site="aniworld.to",
|
|
folder="Rooster Fighter (2026)",
|
|
episodeDict={},
|
|
))
|
|
scanner = SerieScanner(temp_directory, mock_loader, db_lookup=lookup)
|
|
|
|
result = scanner._SerieScanner__read_data_from_file("Rooster Fighter (2026)")
|
|
|
|
assert result is not None
|
|
assert result.key == "rooster-fighter"
|
|
lookup.assert_called_once_with("Rooster Fighter (2026)")
|
|
|
|
def test_no_db_no_callback_generates_key_from_folder_name(self, temp_directory, mock_loader):
|
|
"""No DB entry, no callback -> key generated from folder name."""
|
|
folder = os.path.join(temp_directory, "Legacy Series")
|
|
os.makedirs(folder, exist_ok=True)
|
|
# No key file, no data file - should fall through to Step 4 (key generation)
|
|
|
|
scanner = SerieScanner(temp_directory, mock_loader)
|
|
|
|
result = scanner._SerieScanner__read_data_from_file("Legacy Series")
|
|
|
|
assert result is not None
|
|
assert result.key == "legacy-series"
|
|
assert result.folder == "Legacy Series"
|
|
|
|
def test_db_lookup_exception_caught_and_logged(self, temp_directory, mock_loader):
|
|
"""DB exception -> fallback to provider callback."""
|
|
def bad_lookup(folder):
|
|
raise RuntimeError("DB connection failed")
|
|
|
|
scanner = SerieScanner(temp_directory, mock_loader, db_lookup=bad_lookup)
|
|
|
|
with patch.object(logging.getLogger("src.core.SerieScanner"), "warning") as mock_warning:
|
|
result = scanner._SerieScanner__read_data_from_file("Rooster Fighter (2026)")
|
|
mock_warning.assert_called()
|
|
assert any("DB lookup failed" in str(c) for c in mock_warning.call_args_list)
|
|
|
|
|
|
class TestGetSerieFromFolderEdgeCases:
|
|
"""Edge case tests for __read_data_from_file."""
|
|
|
|
def test_empty_folder_name_returns_none(self, temp_directory, mock_loader):
|
|
"""Empty folder name -> returns None (no DB lookup attempted)."""
|
|
scanner = SerieScanner(temp_directory, mock_loader)
|
|
result = scanner._SerieScanner__read_data_from_file("")
|
|
assert result is None
|
|
|
|
def test_nonexistent_folder_no_exception(self, temp_directory, mock_loader):
|
|
"""Folder doesn't exist -> returns None without raising."""
|
|
scanner = SerieScanner(temp_directory, mock_loader)
|
|
result = scanner._SerieScanner__read_data_from_file("Nonexistent Folder")
|
|
assert result is None |