test: fix NFO workflow and background loader tests
- Add missing TMDB async mock methods (_ensure_session, close) to all TMDB mocks in test_nfo_workflow.py - Refactor test_anime_add_nfo_isolation.py to mock get_nfo_factory() instead of asserting on series_app.nfo_service directly - Patch get_nfo_factory in test_background_loader_service.py to align with factory-based NFOService creation Fixes test failures caused by NFOService refactoring that introduced explicit TMDB session lifecycle and NFO factory pattern.
This commit is contained in:
@@ -91,6 +91,13 @@ def _setup_loader_mocks(loader_service):
|
|||||||
loader_service._broadcast_status = AsyncMock()
|
loader_service._broadcast_status = AsyncMock()
|
||||||
|
|
||||||
|
|
||||||
|
def _mock_nfo_factory(mock_nfo_service):
|
||||||
|
"""Create a mock NFO factory that returns the given mock service."""
|
||||||
|
mock_factory = MagicMock()
|
||||||
|
mock_factory.create = MagicMock(return_value=mock_nfo_service)
|
||||||
|
return mock_factory
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_add_anime_loads_nfo_only_for_new_anime(
|
async def test_add_anime_loads_nfo_only_for_new_anime(
|
||||||
temp_anime_dir,
|
temp_anime_dir,
|
||||||
@@ -112,49 +119,55 @@ async def test_add_anime_loads_nfo_only_for_new_anime(
|
|||||||
)
|
)
|
||||||
_setup_loader_mocks(loader_service)
|
_setup_loader_mocks(loader_service)
|
||||||
|
|
||||||
await loader_service.start()
|
# Set up mock NFO service via factory
|
||||||
|
mock_nfo_service = AsyncMock()
|
||||||
|
mock_nfo_service.create_tvshow_nfo = AsyncMock(return_value="/anime/New Anime (2024)/tvshow.nfo")
|
||||||
|
mock_factory = _mock_nfo_factory(mock_nfo_service)
|
||||||
|
|
||||||
try:
|
with patch("src.server.services.background_loader_service.get_nfo_factory", return_value=mock_factory):
|
||||||
new_anime_key = "new-anime"
|
await loader_service.start()
|
||||||
new_anime_folder = "New Anime (2024)"
|
|
||||||
new_anime_name = "New Anime"
|
|
||||||
new_anime_year = 2024
|
|
||||||
|
|
||||||
new_anime_dir = Path(temp_anime_dir) / new_anime_folder
|
try:
|
||||||
new_anime_dir.mkdir()
|
new_anime_key = "new-anime"
|
||||||
|
new_anime_folder = "New Anime (2024)"
|
||||||
|
new_anime_name = "New Anime"
|
||||||
|
new_anime_year = 2024
|
||||||
|
|
||||||
await loader_service.add_series_loading_task(
|
new_anime_dir = Path(temp_anime_dir) / new_anime_folder
|
||||||
key=new_anime_key,
|
new_anime_dir.mkdir()
|
||||||
folder=new_anime_folder,
|
|
||||||
name=new_anime_name,
|
|
||||||
year=new_anime_year,
|
|
||||||
)
|
|
||||||
|
|
||||||
await asyncio.sleep(1.0)
|
await loader_service.add_series_loading_task(
|
||||||
|
key=new_anime_key,
|
||||||
|
folder=new_anime_folder,
|
||||||
|
name=new_anime_name,
|
||||||
|
year=new_anime_year,
|
||||||
|
)
|
||||||
|
|
||||||
assert mock_series_app.nfo_service.create_tvshow_nfo.call_count == 1
|
await asyncio.sleep(1.0)
|
||||||
|
|
||||||
call_args = mock_series_app.nfo_service.create_tvshow_nfo.call_args
|
assert mock_nfo_service.create_tvshow_nfo.call_count == 1
|
||||||
assert call_args is not None
|
|
||||||
|
|
||||||
kwargs = call_args.kwargs
|
call_args = mock_nfo_service.create_tvshow_nfo.call_args
|
||||||
assert kwargs["serie_name"] == new_anime_name
|
assert call_args is not None
|
||||||
assert kwargs["serie_folder"] == new_anime_folder
|
|
||||||
assert kwargs["year"] == new_anime_year
|
|
||||||
assert kwargs["download_poster"] is True
|
|
||||||
assert kwargs["download_logo"] is True
|
|
||||||
assert kwargs["download_fanart"] is True
|
|
||||||
|
|
||||||
all_calls = mock_series_app.nfo_service.create_tvshow_nfo.call_args_list
|
kwargs = call_args.kwargs
|
||||||
for call_obj in all_calls:
|
assert kwargs["serie_name"] == new_anime_name
|
||||||
call_kwargs = call_obj.kwargs
|
assert kwargs["serie_folder"] == new_anime_folder
|
||||||
assert call_kwargs["serie_name"] != "Existing Anime 1"
|
assert kwargs["year"] == new_anime_year
|
||||||
assert call_kwargs["serie_name"] != "Existing Anime 2"
|
assert kwargs["download_poster"] is True
|
||||||
assert call_kwargs["serie_folder"] != "Existing Anime 1"
|
assert kwargs["download_logo"] is True
|
||||||
assert call_kwargs["serie_folder"] != "Existing Anime 2"
|
assert kwargs["download_fanart"] is True
|
||||||
|
|
||||||
finally:
|
all_calls = mock_nfo_service.create_tvshow_nfo.call_args_list
|
||||||
await loader_service.stop()
|
for call_obj in all_calls:
|
||||||
|
call_kwargs = call_obj.kwargs
|
||||||
|
assert call_kwargs["serie_name"] != "Existing Anime 1"
|
||||||
|
assert call_kwargs["serie_name"] != "Existing Anime 2"
|
||||||
|
assert call_kwargs["serie_folder"] != "Existing Anime 1"
|
||||||
|
assert call_kwargs["serie_folder"] != "Existing Anime 2"
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await loader_service.stop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -216,48 +229,54 @@ async def test_multiple_anime_added_each_loads_independently(
|
|||||||
)
|
)
|
||||||
_setup_loader_mocks(loader_service)
|
_setup_loader_mocks(loader_service)
|
||||||
|
|
||||||
await loader_service.start()
|
# Set up mock NFO service via factory
|
||||||
|
mock_nfo_service = AsyncMock()
|
||||||
|
mock_nfo_service.create_tvshow_nfo = AsyncMock(return_value="/anime/tvshow.nfo")
|
||||||
|
mock_factory = _mock_nfo_factory(mock_nfo_service)
|
||||||
|
|
||||||
try:
|
with patch("src.server.services.background_loader_service.get_nfo_factory", return_value=mock_factory):
|
||||||
anime_to_add = [
|
await loader_service.start()
|
||||||
("anime-a", "Anime A (2024)", "Anime A", 2024),
|
|
||||||
("anime-b", "Anime B (2023)", "Anime B", 2023),
|
|
||||||
("anime-c", "Anime C (2025)", "Anime C", 2025),
|
|
||||||
]
|
|
||||||
|
|
||||||
for key, folder, name, year in anime_to_add:
|
try:
|
||||||
anime_dir = Path(temp_anime_dir) / folder
|
anime_to_add = [
|
||||||
anime_dir.mkdir()
|
("anime-a", "Anime A (2024)", "Anime A", 2024),
|
||||||
|
("anime-b", "Anime B (2023)", "Anime B", 2023),
|
||||||
|
("anime-c", "Anime C (2025)", "Anime C", 2025),
|
||||||
|
]
|
||||||
|
|
||||||
await loader_service.add_series_loading_task(
|
for key, folder, name, year in anime_to_add:
|
||||||
key=key,
|
anime_dir = Path(temp_anime_dir) / folder
|
||||||
folder=folder,
|
anime_dir.mkdir()
|
||||||
name=name,
|
|
||||||
year=year,
|
|
||||||
)
|
|
||||||
|
|
||||||
await asyncio.sleep(2.0)
|
await loader_service.add_series_loading_task(
|
||||||
|
key=key,
|
||||||
|
folder=folder,
|
||||||
|
name=name,
|
||||||
|
year=year,
|
||||||
|
)
|
||||||
|
|
||||||
assert mock_series_app.nfo_service.create_tvshow_nfo.call_count == 3
|
await asyncio.sleep(2.0)
|
||||||
|
|
||||||
all_calls = mock_series_app.nfo_service.create_tvshow_nfo.call_args_list
|
assert mock_nfo_service.create_tvshow_nfo.call_count == 3
|
||||||
|
|
||||||
called_names = [call_obj.kwargs["serie_name"] for call_obj in all_calls]
|
all_calls = mock_nfo_service.create_tvshow_nfo.call_args_list
|
||||||
called_folders = [call_obj.kwargs["serie_folder"] for call_obj in all_calls]
|
|
||||||
|
|
||||||
assert "Anime A" in called_names
|
called_names = [call_obj.kwargs["serie_name"] for call_obj in all_calls]
|
||||||
assert "Anime B" in called_names
|
called_folders = [call_obj.kwargs["serie_folder"] for call_obj in all_calls]
|
||||||
assert "Anime C" in called_names
|
|
||||||
|
|
||||||
assert "Anime A (2024)" in called_folders
|
assert "Anime A" in called_names
|
||||||
assert "Anime B (2023)" in called_folders
|
assert "Anime B" in called_names
|
||||||
assert "Anime C (2025)" in called_folders
|
assert "Anime C" in called_names
|
||||||
|
|
||||||
assert "Existing Anime 1" not in called_names
|
assert "Anime A (2024)" in called_folders
|
||||||
assert "Existing Anime 2" not in called_names
|
assert "Anime B (2023)" in called_folders
|
||||||
|
assert "Anime C (2025)" in called_folders
|
||||||
|
|
||||||
finally:
|
assert "Existing Anime 1" not in called_names
|
||||||
await loader_service.stop()
|
assert "Existing Anime 2" not in called_names
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await loader_service.stop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -275,38 +294,44 @@ async def test_nfo_service_receives_correct_parameters(
|
|||||||
)
|
)
|
||||||
_setup_loader_mocks(loader_service)
|
_setup_loader_mocks(loader_service)
|
||||||
|
|
||||||
await loader_service.start()
|
# Set up mock NFO service via factory
|
||||||
|
mock_nfo_service = AsyncMock()
|
||||||
|
mock_nfo_service.create_tvshow_nfo = AsyncMock(return_value="/anime/Test Anime Series (2024)/tvshow.nfo")
|
||||||
|
mock_factory = _mock_nfo_factory(mock_nfo_service)
|
||||||
|
|
||||||
try:
|
with patch("src.server.services.background_loader_service.get_nfo_factory", return_value=mock_factory):
|
||||||
test_key = "test-anime-key"
|
await loader_service.start()
|
||||||
test_folder = "Test Anime Series (2024)"
|
|
||||||
test_name = "Test Anime Series"
|
|
||||||
test_year = 2024
|
|
||||||
|
|
||||||
anime_dir = Path(temp_anime_dir) / test_folder
|
try:
|
||||||
anime_dir.mkdir()
|
test_key = "test-anime-key"
|
||||||
|
test_folder = "Test Anime Series (2024)"
|
||||||
|
test_name = "Test Anime Series"
|
||||||
|
test_year = 2024
|
||||||
|
|
||||||
await loader_service.add_series_loading_task(
|
anime_dir = Path(temp_anime_dir) / test_folder
|
||||||
key=test_key,
|
anime_dir.mkdir()
|
||||||
folder=test_folder,
|
|
||||||
name=test_name,
|
|
||||||
year=test_year,
|
|
||||||
)
|
|
||||||
|
|
||||||
await asyncio.sleep(1.0)
|
await loader_service.add_series_loading_task(
|
||||||
|
key=test_key,
|
||||||
|
folder=test_folder,
|
||||||
|
name=test_name,
|
||||||
|
year=test_year,
|
||||||
|
)
|
||||||
|
|
||||||
assert mock_series_app.nfo_service.create_tvshow_nfo.call_count == 1
|
await asyncio.sleep(1.0)
|
||||||
|
|
||||||
call_kwargs = mock_series_app.nfo_service.create_tvshow_nfo.call_args.kwargs
|
assert mock_nfo_service.create_tvshow_nfo.call_count == 1
|
||||||
|
|
||||||
assert call_kwargs["serie_name"] == test_name
|
call_kwargs = mock_nfo_service.create_tvshow_nfo.call_args.kwargs
|
||||||
assert call_kwargs["serie_folder"] == test_folder
|
|
||||||
assert call_kwargs["year"] == test_year
|
|
||||||
assert call_kwargs["download_poster"] is True
|
|
||||||
assert call_kwargs["download_logo"] is True
|
|
||||||
assert call_kwargs["download_fanart"] is True
|
|
||||||
|
|
||||||
assert "Existing Anime" not in str(call_kwargs)
|
assert call_kwargs["serie_name"] == test_name
|
||||||
|
assert call_kwargs["serie_folder"] == test_folder
|
||||||
|
assert call_kwargs["year"] == test_year
|
||||||
|
assert call_kwargs["download_poster"] is True
|
||||||
|
assert call_kwargs["download_logo"] is True
|
||||||
|
assert call_kwargs["download_fanart"] is True
|
||||||
|
|
||||||
finally:
|
assert "Existing Anime" not in str(call_kwargs)
|
||||||
await loader_service.stop()
|
|
||||||
|
finally:
|
||||||
|
await loader_service.stop()
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ class TestCompleteNFOWorkflow:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(return_value={"results": [mock_tmdb_show]})
|
mock_tmdb.search_tv_show = AsyncMock(return_value={"results": [mock_tmdb_show]})
|
||||||
mock_tmdb.get_tv_show = AsyncMock(return_value=mock_tmdb_show)
|
mock_tmdb.get_tv_show = AsyncMock(return_value=mock_tmdb_show)
|
||||||
mock_tmdb.get_tv_show_details = AsyncMock(return_value=mock_tmdb_show)
|
mock_tmdb.get_tv_show_details = AsyncMock(return_value=mock_tmdb_show)
|
||||||
@@ -158,6 +160,8 @@ class TestCompleteNFOWorkflow:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(
|
mock_tmdb.search_tv_show = AsyncMock(
|
||||||
return_value={"results": [{
|
return_value={"results": [{
|
||||||
"id": 999,
|
"id": 999,
|
||||||
@@ -208,6 +212,8 @@ class TestCompleteNFOWorkflow:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(
|
mock_tmdb.search_tv_show = AsyncMock(
|
||||||
side_effect=TMDBAPIError("API error")
|
side_effect=TMDBAPIError("API error")
|
||||||
)
|
)
|
||||||
@@ -253,6 +259,8 @@ class TestCompleteNFOWorkflow:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(
|
mock_tmdb.search_tv_show = AsyncMock(
|
||||||
return_value={"results": [{
|
return_value={"results": [{
|
||||||
"id": 999,
|
"id": 999,
|
||||||
@@ -307,16 +315,22 @@ class TestCompleteNFOWorkflow:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(
|
mock_tmdb.search_tv_show = AsyncMock(
|
||||||
side_effect=[
|
side_effect=[
|
||||||
{"results": [{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"}]},
|
{"results": [{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"}]},
|
||||||
{"results": [{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"}]},
|
{"results": [{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"}]},
|
||||||
|
{"results": [{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"}]},
|
||||||
|
{"results": [{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"}]},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
mock_tmdb.get_tv_show_details = AsyncMock(
|
mock_tmdb.get_tv_show_details = AsyncMock(
|
||||||
side_effect=[
|
side_effect=[
|
||||||
{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"},
|
{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"},
|
||||||
{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"},
|
{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"},
|
||||||
|
{"id": 1, "name": "Anime 1", "first_air_date": "2020-01-01"},
|
||||||
|
{"id": 2, "name": "Anime 2", "first_air_date": "2021-01-01"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
mock_tmdb.get_tv_show_content_ratings = AsyncMock(return_value={"results": []})
|
mock_tmdb.get_tv_show_content_ratings = AsyncMock(return_value={"results": []})
|
||||||
@@ -366,6 +380,8 @@ class TestNFOWorkflowWithDownloads:
|
|||||||
mock_tmdb = Mock()
|
mock_tmdb = Mock()
|
||||||
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
mock_tmdb.__aenter__ = AsyncMock(return_value=mock_tmdb)
|
||||||
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
mock_tmdb.__aexit__ = AsyncMock(return_value=None)
|
||||||
|
mock_tmdb._ensure_session = AsyncMock()
|
||||||
|
mock_tmdb.close = AsyncMock()
|
||||||
mock_tmdb.search_tv_show = AsyncMock(
|
mock_tmdb.search_tv_show = AsyncMock(
|
||||||
return_value={"results": [{
|
return_value={"results": [{
|
||||||
"id": 999,
|
"id": 999,
|
||||||
|
|||||||
@@ -528,7 +528,13 @@ class TestLoadNfoAndImages:
|
|||||||
year=2020
|
year=2020
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("src.server.database.service.AnimeSeriesService") as mock_service_class:
|
mock_nfo_service = AsyncMock()
|
||||||
|
mock_nfo_service.create_tvshow_nfo = AsyncMock(return_value="/anime/test_folder/tvshow.nfo")
|
||||||
|
mock_factory = MagicMock()
|
||||||
|
mock_factory.create = MagicMock(return_value=mock_nfo_service)
|
||||||
|
|
||||||
|
with patch("src.server.database.service.AnimeSeriesService") as mock_service_class, \
|
||||||
|
patch("src.server.services.background_loader_service.get_nfo_factory", return_value=mock_factory):
|
||||||
mock_service_class.get_by_key = AsyncMock(return_value=mock_series)
|
mock_service_class.get_by_key = AsyncMock(return_value=mock_series)
|
||||||
|
|
||||||
result = await background_loader_service._load_nfo_and_images(task, mock_db)
|
result = await background_loader_service._load_nfo_and_images(task, mock_db)
|
||||||
|
|||||||
Reference in New Issue
Block a user