Fix get_all_series_from_data_files to read data files directly
Previously, the method created a SerieList instance which only loads from database, not from data files. Now reads data files directly and parses JSON to create AnimeSeries objects. Also added _load_data_file helper function and fixed logger.warning calls to use proper format strings instead of keyword arguments. Updated unit tests to use real temp directories instead of mocks.
This commit is contained in:
@@ -10,6 +10,9 @@ Tests the functionality of SeriesApp including:
|
||||
- Error handling
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
@@ -489,53 +492,45 @@ class TestSeriesAppGetAllSeriesFromDataFiles:
|
||||
|
||||
@patch('src.server.SeriesApp.Loaders')
|
||||
@patch('src.server.SeriesApp.SerieScanner')
|
||||
@patch('src.server.SeriesApp.SerieList')
|
||||
def test_returns_list_of_series(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
self, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that get_all_series_from_data_files returns a list of AnimeSeries."""
|
||||
from src.server.database.models import AnimeSeries
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Create test data files
|
||||
anime_dir1 = os.path.join(tmp_dir, "Anime 1")
|
||||
os.makedirs(anime_dir1)
|
||||
data1 = {
|
||||
"key": "anime1",
|
||||
"name": "Anime 1",
|
||||
"site": "https://aniworld.to",
|
||||
"folder": "Anime 1 (2020)",
|
||||
"episodeDict": {"1": [1, 2, 3]}
|
||||
}
|
||||
with open(os.path.join(anime_dir1, "data"), "w") as f:
|
||||
json.dump(data1, f)
|
||||
|
||||
test_dir = "/test/anime"
|
||||
anime_dir2 = os.path.join(tmp_dir, "Anime 2")
|
||||
os.makedirs(anime_dir2)
|
||||
data2 = {
|
||||
"key": "anime2",
|
||||
"name": "Anime 2",
|
||||
"site": "https://aniworld.to",
|
||||
"folder": "Anime 2 (2021)",
|
||||
"episodeDict": {"1": [1, 2]}
|
||||
}
|
||||
with open(os.path.join(anime_dir2, "data"), "w") as f:
|
||||
json.dump(data2, f)
|
||||
|
||||
def make_anime(key, name, folder):
|
||||
anime = MagicMock(spec=AnimeSeries)
|
||||
anime.key = key
|
||||
anime.name = name
|
||||
anime.site = "https://aniworld.to"
|
||||
anime.folder = folder
|
||||
anime.episodeDict = {1: [1, 2, 3]} if key == "anime1" else {1: [1, 2]}
|
||||
return anime
|
||||
with patch('src.server.SeriesApp.SerieList'):
|
||||
app = SeriesApp(tmp_dir)
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# Mock series to return
|
||||
mock_series = [
|
||||
make_anime("anime1", "Anime 1", "Anime 1 (2020)"),
|
||||
make_anime("anime2", "Anime 2", "Anime 2 (2021)"),
|
||||
]
|
||||
|
||||
# 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 AnimeSeries
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
assert all(isinstance(s, MagicMock) for s in result)
|
||||
assert result[0].key == "anime1"
|
||||
assert result[1].key == "anime2"
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
keys = {s.key for s in result}
|
||||
assert "anime1" in keys
|
||||
assert "anime2" in keys
|
||||
|
||||
@patch('src.server.SeriesApp.Loaders')
|
||||
@patch('src.server.SeriesApp.SerieScanner')
|
||||
@@ -544,27 +539,14 @@ class TestSeriesAppGetAllSeriesFromDataFiles:
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that empty list is returned when no data files exist."""
|
||||
test_dir = "/test/anime"
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# No data files created - directory is empty
|
||||
with patch('src.server.SeriesApp.SerieList'):
|
||||
app = SeriesApp(tmp_dir)
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# 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
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
@patch('src.server.SeriesApp.Loaders')
|
||||
@patch('src.server.SeriesApp.SerieScanner')
|
||||
@@ -573,61 +555,53 @@ class TestSeriesAppGetAllSeriesFromDataFiles:
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test exceptions are handled gracefully and empty list returned."""
|
||||
test_dir = "/test/anime"
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Create a data file that will cause an error
|
||||
anime_dir = os.path.join(tmp_dir, "Anime")
|
||||
os.makedirs(anime_dir)
|
||||
with open(os.path.join(anime_dir, "data"), "w") as f:
|
||||
f.write("invalid json {{{")
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
with patch('src.server.SeriesApp.SerieList'):
|
||||
app = SeriesApp(tmp_dir)
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
# 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
|
||||
# Should return empty list due to corrupt file
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
@patch('src.server.SeriesApp.Loaders')
|
||||
@patch('src.server.SeriesApp.SerieScanner')
|
||||
@patch('src.server.SeriesApp.SerieList')
|
||||
def test_uses_file_based_loading(
|
||||
self, mock_serie_list_class, mock_scanner, mock_loaders
|
||||
self, mock_scanner, mock_loaders
|
||||
):
|
||||
"""Test that method uses SerieList for file-based loading."""
|
||||
test_dir = "/test/anime"
|
||||
"""Test that method reads directly from data files, not SerieList."""
|
||||
import tempfile
|
||||
import os
|
||||
import json
|
||||
|
||||
# Setup mock for the main SerieList instance
|
||||
mock_main_list = Mock()
|
||||
mock_main_list.GetMissingEpisode.return_value = []
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Create test data file
|
||||
anime_dir = os.path.join(tmp_dir, "Anime")
|
||||
os.makedirs(anime_dir)
|
||||
data = {
|
||||
"key": "anime1",
|
||||
"name": "Anime 1",
|
||||
"site": "https://aniworld.to",
|
||||
"folder": "Anime",
|
||||
"episodeDict": {"1": [1]}
|
||||
}
|
||||
with open(os.path.join(anime_dir, "data"), "w") as f:
|
||||
json.dump(data, f)
|
||||
|
||||
# Setup mock for the temporary SerieList
|
||||
mock_temp_list = Mock()
|
||||
mock_temp_list.get_all.return_value = []
|
||||
with patch('src.server.SeriesApp.SerieList') as mock_serie_list_class:
|
||||
app = SeriesApp(tmp_dir)
|
||||
result = app.get_all_series_from_data_files()
|
||||
|
||||
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 SerieList was called twice (main + temp)
|
||||
calls = mock_serie_list_class.call_args_list
|
||||
assert len(calls) == 2
|
||||
|
||||
# Check the second call is for temp SerieList with directory
|
||||
second_call = calls[1]
|
||||
# base_path is passed as positional argument
|
||||
assert second_call.args[0] == test_dir
|
||||
# SerieList should NOT be instantiated by this method
|
||||
# The new implementation uses direct file reading
|
||||
assert len(result) == 1
|
||||
assert result[0].key == "anime1"
|
||||
|
||||
@patch('src.server.SeriesApp.Loaders')
|
||||
@patch('src.server.SeriesApp.SerieScanner')
|
||||
|
||||
Reference in New Issue
Block a user