refactor: restructure core→server, split large entity files into database module

- Move src/core/ → src/server/
- Split SerieList.py (531 lines) and series.py (414 lines) into src/server/database/
- Add database/models.py for SQLAlchemy models
- Update all test imports to reflect new structure
- Remove deprecated test files (test_serie_class.py, test_serie_folder_with_year.py)
This commit is contained in:
2026-06-04 21:11:53 +02:00
parent 09d454d4c0
commit 5526ab884a
76 changed files with 1186 additions and 3574 deletions

View File

@@ -699,61 +699,58 @@ class TestErrorHandling:
class TestRemoveEpisodeFromMissingList:
"""Test that completed downloads remove episodes from missing list."""
@staticmethod
def make_anime(key, name, folder, episode_dict):
"""Create mock AnimeSeries for testing."""
anime = MagicMock()
anime.key = key
anime.name = name
anime.site = "https://example.com"
anime.folder = folder
anime.episodeDict = episode_dict
return anime
@pytest.mark.asyncio
async def test_remove_episode_from_memory(self, download_service):
"""Test _remove_episode_from_memory updates in-memory state."""
from src.core.entities.series import Serie
# Set up in-memory series with missing episodes
serie = Serie(
key="test-series",
name="Test Series",
site="https://example.com",
folder="Test Series (2024)",
episodeDict={1: [1, 2, 3], 2: [1, 2]},
)
anime = self.make_anime("test-series", "Test Series", "Test Series (2024)", {1: [1, 2, 3], 2: [1, 2]})
mock_app = MagicMock()
mock_app.list.keyDict = {"test-series": serie}
mock_app.list.GetMissingEpisode.return_value = [serie]
mock_app.series_list = [serie]
mock_app.list.keyDict = {"test-series": anime}
mock_app.list.GetMissingEpisode.return_value = [anime]
mock_app.series_list = [anime]
download_service._anime_service._app = mock_app
# Remove episode S01E02
download_service._remove_episode_from_memory("test-series", 1, 2)
# Episode should be removed from episodeDict
assert 2 not in serie.episodeDict[1]
assert serie.episodeDict[1] == [1, 3]
assert 2 not in anime.episodeDict[1]
assert anime.episodeDict[1] == [1, 3]
# Season 2 should be untouched
assert serie.episodeDict[2] == [1, 2]
assert anime.episodeDict[2] == [1, 2]
@pytest.mark.asyncio
async def test_remove_last_episode_in_season_removes_season(
self, download_service
):
"""Test removing the last episode in a season removes the season key."""
from src.core.entities.series import Serie
serie = Serie(
key="test-series",
name="Test Series",
site="https://example.com",
folder="Test Series (2024)",
episodeDict={1: [5], 2: [1, 2]},
)
anime = self.make_anime("test-series", "Test Series", "Test Series (2024)", {1: [5], 2: [1, 2]})
mock_app = MagicMock()
mock_app.list.keyDict = {"test-series": serie}
mock_app.list.GetMissingEpisode.return_value = [serie]
mock_app.series_list = [serie]
mock_app.list.keyDict = {"test-series": anime}
mock_app.list.GetMissingEpisode.return_value = [anime]
mock_app.series_list = [anime]
download_service._anime_service._app = mock_app
# Remove the only episode in season 1
download_service._remove_episode_from_memory("test-series", 1, 5)
# Season 1 should be completely removed
assert 1 not in serie.episodeDict
assert 1 not in anime.episodeDict
# Season 2 untouched
assert serie.episodeDict[2] == [1, 2]
assert anime.episodeDict[2] == [1, 2]
# GetMissingEpisode should have been called to refresh
mock_app.list.GetMissingEpisode.assert_called()
@@ -778,20 +775,12 @@ class TestRemoveEpisodeFromMissingList:
"""Test _remove_episode_from_missing_list updates both DB and memory."""
from unittest.mock import patch
from src.core.entities.series import Serie
# Set up in-memory state
serie = Serie(
key="test-series",
name="Test Series",
site="https://example.com",
folder="Test Series (2024)",
episodeDict={1: [1, 2, 3]},
)
anime = self.make_anime("test-series", "Test Series", "Test Series (2024)", {1: [1, 2, 3]})
mock_app = MagicMock()
mock_app.list.keyDict = {"test-series": serie}
mock_app.list.GetMissingEpisode.return_value = [serie]
mock_app.series_list = [serie]
mock_app.list.keyDict = {"test-series": anime}
mock_app.list.GetMissingEpisode.return_value = [anime]
mock_app.series_list = [anime]
download_service._anime_service._app = mock_app
download_service._anime_service._cached_list_missing = MagicMock()
@@ -845,8 +834,8 @@ class TestRemoveEpisodeFromMissingList:
),
)
# In-memory update happened
assert 2 not in serie.episodeDict[1]
assert serie.episodeDict[1] == [1, 3]
assert 2 not in anime.episodeDict[1]
assert anime.episodeDict[1] == [1, 3]
# Cache was cleared
download_service._anime_service._cached_list_missing.cache_clear.assert_called()
# Broadcast was sent so frontend gets real-time update
@@ -862,25 +851,17 @@ class TestRemoveEpisodeFromMissingList:
"""Test full flow: download success removes episode from missing list."""
from unittest.mock import patch
from src.core.entities.series import Serie
# Setup mock anime service to return success
download_service._anime_service.download = AsyncMock(
return_value=True
)
# Set up in-memory series state
serie = Serie(
key="series-1",
name="Test Series",
site="https://example.com",
folder="series",
episodeDict={1: [1, 2, 3]},
)
anime = self.make_anime("series-1", "Test Series", "series", {1: [1, 2, 3]})
mock_app = MagicMock()
mock_app.list.keyDict = {"series-1": serie}
mock_app.list.GetMissingEpisode.return_value = [serie]
mock_app.series_list = [serie]
mock_app.list.keyDict = {"series-1": anime}
mock_app.list.GetMissingEpisode.return_value = [anime]
mock_app.series_list = [anime]
download_service._anime_service._app = mock_app
download_service._anime_service._cached_list_missing = MagicMock()
@@ -936,8 +917,8 @@ class TestRemoveEpisodeFromMissingList:
assert download_service._completed_items[0].status == DownloadStatus.COMPLETED
# Episode 2 should be removed from in-memory missing list
assert 2 not in serie.episodeDict[1]
assert serie.episodeDict[1] == [1, 3]
assert 2 not in anime.episodeDict[1]
assert anime.episodeDict[1] == [1, 3]
class TestQueueDeduplication: