Add data file to database sync functionality
- Add get_all_series_from_data_files() to SeriesApp - Sync series from data files to DB on startup - Add unit tests for new SeriesApp method - Add integration tests for sync functionality - Update documentation
This commit is contained in:
@@ -559,3 +559,196 @@ class TestSeriesAppAsyncDbInit:
|
||||
assert len(w) == 1
|
||||
assert "without db_session" in str(w[0].message)
|
||||
|
||||
|
||||
class TestSeriesAppGetAllSeriesFromDataFiles:
|
||||
"""Test get_all_series_from_data_files() functionality."""
|
||||
|
||||
@patch('src.core.SeriesApp.Loaders')
|
||||
@patch('src.core.SeriesApp.SerieScanner')
|
||||
@patch('src.core.SeriesApp.SerieList')
|
||||
def test_returns_list_of_series(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that get_all_series_from_data_files returns a list of Serie."""
|
||||
from src.core.entities.series import Serie
|
||||
|
||||
test_dir = "/test/anime"
|
||||
|
||||
# Mock series to return
|
||||
mock_series = [
|
||||
Serie(
|
||||
key="anime1",
|
||||
name="Anime 1",
|
||||
site="https://aniworld.to",
|
||||
folder="Anime 1 (2020)",
|
||||
episodeDict={1: [1, 2, 3]}
|
||||
),
|
||||
Serie(
|
||||
key="anime2",
|
||||
name="Anime 2",
|
||||
site="https://aniworld.to",
|
||||
folder="Anime 2 (2021)",
|
||||
episodeDict={1: [1]}
|
||||
),
|
||||
]
|
||||
|
||||
# Setup mock for the main SerieList instance (constructor call)
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
|
||||
# Setup mock for temporary SerieList in get_all_series_from_data_files
|
||||
mock_temp_list = Mock()
|
||||
mock_temp_list.get_all.return_value = mock_series
|
||||
|
||||
# Return different mocks for the two calls
|
||||
mock_serie_list_class.side_effect = [mock_main_list, mock_temp_list]
|
||||
|
||||
# Create app
|
||||
app = SeriesApp(test_dir)
|
||||
|
||||
# Call the method
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# Verify result is a list of Serie
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
assert all(isinstance(s, Serie) for s in result)
|
||||
assert result[0].key == "anime1"
|
||||
assert result[1].key == "anime2"
|
||||
|
||||
@patch('src.core.SeriesApp.Loaders')
|
||||
@patch('src.core.SeriesApp.SerieScanner')
|
||||
@patch('src.core.SeriesApp.SerieList')
|
||||
def test_returns_empty_list_when_no_data_files(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that empty list is returned when no data files exist."""
|
||||
test_dir = "/test/anime"
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
|
||||
# Setup mock for the temporary SerieList (empty directory)
|
||||
mock_temp_list = Mock()
|
||||
mock_temp_list.get_all.return_value = []
|
||||
|
||||
mock_serie_list_class.side_effect = [mock_main_list, mock_temp_list]
|
||||
|
||||
# Create app
|
||||
app = SeriesApp(test_dir)
|
||||
|
||||
# Call the method
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# Verify empty list is returned
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
@patch('src.core.SeriesApp.Loaders')
|
||||
@patch('src.core.SeriesApp.SerieScanner')
|
||||
@patch('src.core.SeriesApp.SerieList')
|
||||
def test_handles_exception_gracefully(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test exceptions are handled gracefully and empty list returned."""
|
||||
test_dir = "/test/anime"
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
|
||||
# Make the second SerieList constructor raise an exception
|
||||
mock_serie_list_class.side_effect = [
|
||||
mock_main_list,
|
||||
OSError("Directory not found")
|
||||
]
|
||||
|
||||
# Create app
|
||||
app = SeriesApp(test_dir)
|
||||
|
||||
# Call the method - should not raise
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# Verify empty list is returned on error
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
@patch('src.core.SeriesApp.Loaders')
|
||||
@patch('src.core.SeriesApp.SerieScanner')
|
||||
@patch('src.core.SeriesApp.SerieList')
|
||||
def test_uses_file_based_loading(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that method uses file-based loading (no db_session)."""
|
||||
test_dir = "/test/anime"
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
|
||||
# Setup mock for the temporary SerieList
|
||||
mock_temp_list = Mock()
|
||||
mock_temp_list.get_all.return_value = []
|
||||
|
||||
mock_serie_list_class.side_effect = [mock_main_list, mock_temp_list]
|
||||
|
||||
# Create app
|
||||
app = SeriesApp(test_dir)
|
||||
|
||||
# Call the method
|
||||
app.get_all_series_from_data_files()
|
||||
|
||||
# Verify the second SerieList was created with correct params
|
||||
# (file-based loading: db_session=None, skip_load=False)
|
||||
calls = mock_serie_list_class.call_args_list
|
||||
assert len(calls) == 2
|
||||
|
||||
# Check the second call (for get_all_series_from_data_files)
|
||||
second_call = calls[1]
|
||||
assert second_call.kwargs.get('db_session') is None
|
||||
assert second_call.kwargs.get('skip_load') is False
|
||||
|
||||
@patch('src.core.SeriesApp.Loaders')
|
||||
@patch('src.core.SeriesApp.SerieScanner')
|
||||
@patch('src.core.SeriesApp.SerieList')
|
||||
def test_does_not_modify_main_list(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that method does not modify the main SerieList instance."""
|
||||
from src.core.entities.series import Serie
|
||||
|
||||
test_dir = "/test/anime"
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
mock_main_list.get_all.return_value = []
|
||||
|
||||
# Setup mock for the temporary SerieList
|
||||
mock_temp_list = Mock()
|
||||
mock_temp_list.get_all.return_value = [
|
||||
Serie(
|
||||
key="anime1",
|
||||
name="Anime 1",
|
||||
site="https://aniworld.to",
|
||||
folder="Anime 1",
|
||||
episodeDict={}
|
||||
)
|
||||
]
|
||||
|
||||
mock_serie_list_class.side_effect = [mock_main_list, mock_temp_list]
|
||||
|
||||
# Create app
|
||||
app = SeriesApp(test_dir)
|
||||
|
||||
# Store reference to original list
|
||||
original_list = app.list
|
||||
|
||||
# Call the method
|
||||
app.get_all_series_from_data_files()
|
||||
|
||||
# Verify main list is unchanged
|
||||
assert app.list is original_list
|
||||
# Verify the main list's get_all was not called
|
||||
mock_main_list.get_all.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user