Fix NFO service year extraction from series names
This commit is contained in:
@@ -146,6 +146,70 @@ class TestFSKRatingExtraction:
|
||||
assert fsk is None
|
||||
|
||||
|
||||
class TestYearExtraction:
|
||||
"""Test year extraction from series names."""
|
||||
|
||||
def test_extract_year_with_year(self, nfo_service):
|
||||
"""Test extraction when year is present in format (YYYY)."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Attack on Titan (2013)")
|
||||
assert clean_name == "Attack on Titan"
|
||||
assert year == 2013
|
||||
|
||||
def test_extract_year_without_year(self, nfo_service):
|
||||
"""Test extraction when no year is present."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Attack on Titan")
|
||||
assert clean_name == "Attack on Titan"
|
||||
assert year is None
|
||||
|
||||
def test_extract_year_multiple_parentheses(self, nfo_service):
|
||||
"""Test extraction with multiple parentheses - only last one with year."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Series (Part 1) (2023)")
|
||||
assert clean_name == "Series (Part 1)"
|
||||
assert year == 2023
|
||||
|
||||
def test_extract_year_with_trailing_spaces(self, nfo_service):
|
||||
"""Test extraction with trailing spaces."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Attack on Titan (2013) ")
|
||||
assert clean_name == "Attack on Titan"
|
||||
assert year == 2013
|
||||
|
||||
def test_extract_year_parentheses_not_year(self, nfo_service):
|
||||
"""Test extraction when parentheses don't contain a year."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Series (Special Edition)")
|
||||
assert clean_name == "Series (Special Edition)"
|
||||
assert year is None
|
||||
|
||||
def test_extract_year_invalid_year_format(self, nfo_service):
|
||||
"""Test extraction with invalid year format (not 4 digits)."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Series (23)")
|
||||
assert clean_name == "Series (23)"
|
||||
assert year is None
|
||||
|
||||
def test_extract_year_future_year(self, nfo_service):
|
||||
"""Test extraction with future year."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Future Series (2050)")
|
||||
assert clean_name == "Future Series"
|
||||
assert year == 2050
|
||||
|
||||
def test_extract_year_old_year(self, nfo_service):
|
||||
"""Test extraction with old year."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Classic Series (1990)")
|
||||
assert clean_name == "Classic Series"
|
||||
assert year == 1990
|
||||
|
||||
def test_extract_year_real_world_example(self, nfo_service):
|
||||
"""Test extraction with the real-world example from the bug report."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("The Dreaming Boy is a Realist (2023)")
|
||||
assert clean_name == "The Dreaming Boy is a Realist"
|
||||
assert year == 2023
|
||||
|
||||
def test_extract_year_uebel_blatt(self, nfo_service):
|
||||
"""Test extraction with Übel Blatt example."""
|
||||
clean_name, year = nfo_service._extract_year_from_name("Übel Blatt (2025)")
|
||||
assert clean_name == "Übel Blatt"
|
||||
assert year == 2025
|
||||
|
||||
|
||||
class TestTMDBToNFOModel:
|
||||
"""Test conversion of TMDB data to NFO model."""
|
||||
|
||||
@@ -221,6 +285,110 @@ class TestTMDBToNFOModel:
|
||||
class TestCreateTVShowNFO:
|
||||
"""Test NFO creation workflow."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nfo_with_year_in_name(self, nfo_service, tmp_path, mock_tmdb_data, mock_content_ratings_de):
|
||||
"""Test NFO creation when year is included in series name.
|
||||
|
||||
This test addresses the bug where searching TMDB with year in the name
|
||||
(e.g., "The Dreaming Boy is a Realist (2023)") fails to find results.
|
||||
"""
|
||||
# Setup
|
||||
serie_name = "The Dreaming Boy is a Realist (2023)"
|
||||
serie_folder = "The Dreaming Boy is a Realist (2023)"
|
||||
(tmp_path / serie_folder).mkdir()
|
||||
|
||||
# Mock TMDB responses
|
||||
search_results = {"results": [mock_tmdb_data]}
|
||||
|
||||
with patch.object(nfo_service.tmdb_client, '__aenter__', return_value=nfo_service.tmdb_client):
|
||||
with patch.object(nfo_service.tmdb_client, '__aexit__', return_value=None):
|
||||
with patch.object(nfo_service.tmdb_client, 'search_tv_show', new_callable=AsyncMock) as mock_search:
|
||||
with patch.object(nfo_service.tmdb_client, 'get_tv_show_details', new_callable=AsyncMock) as mock_details:
|
||||
with patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings:
|
||||
with patch.object(nfo_service.image_downloader, 'download_poster', new_callable=AsyncMock):
|
||||
with patch.object(nfo_service.image_downloader, 'download_logo', new_callable=AsyncMock):
|
||||
with patch.object(nfo_service.image_downloader, 'download_fanart', new_callable=AsyncMock):
|
||||
mock_search.return_value = search_results
|
||||
mock_details.return_value = mock_tmdb_data
|
||||
mock_ratings.return_value = mock_content_ratings_de
|
||||
|
||||
# Act
|
||||
nfo_path = await nfo_service.create_tvshow_nfo(
|
||||
serie_name=serie_name,
|
||||
serie_folder=serie_folder,
|
||||
year=None # Year should be auto-extracted
|
||||
)
|
||||
|
||||
# Assert - should search with clean name "The Dreaming Boy is a Realist"
|
||||
mock_search.assert_called_once_with("The Dreaming Boy is a Realist")
|
||||
|
||||
# Verify NFO file was created
|
||||
assert nfo_path.exists()
|
||||
assert nfo_path.name == "tvshow.nfo"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nfo_year_parameter_takes_precedence(self, nfo_service, tmp_path, mock_tmdb_data, mock_content_ratings_de):
|
||||
"""Test that explicit year parameter takes precedence over extracted year."""
|
||||
# Setup
|
||||
serie_name = "Attack on Titan (2013)"
|
||||
serie_folder = "Attack on Titan"
|
||||
explicit_year = 2015 # Different from extracted year
|
||||
(tmp_path / serie_folder).mkdir()
|
||||
|
||||
# Mock TMDB responses
|
||||
search_results = {"results": [mock_tmdb_data]}
|
||||
|
||||
with patch.object(nfo_service.tmdb_client, '__aenter__', return_value=nfo_service.tmdb_client):
|
||||
with patch.object(nfo_service.tmdb_client, '__aexit__', return_value=None):
|
||||
with patch.object(nfo_service.tmdb_client, 'search_tv_show', new_callable=AsyncMock) as mock_search:
|
||||
with patch.object(nfo_service.tmdb_client, 'get_tv_show_details', new_callable=AsyncMock) as mock_details:
|
||||
with patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings:
|
||||
with patch.object(nfo_service.image_downloader, 'download_poster', new_callable=AsyncMock):
|
||||
with patch.object(nfo_service.image_downloader, 'download_logo', new_callable=AsyncMock):
|
||||
with patch.object(nfo_service.image_downloader, 'download_fanart', new_callable=AsyncMock):
|
||||
with patch.object(nfo_service, '_find_best_match') as mock_find_match:
|
||||
mock_search.return_value = search_results
|
||||
mock_details.return_value = mock_tmdb_data
|
||||
mock_ratings.return_value = mock_content_ratings_de
|
||||
mock_find_match.return_value = mock_tmdb_data
|
||||
|
||||
# Act
|
||||
await nfo_service.create_tvshow_nfo(
|
||||
serie_name=serie_name,
|
||||
serie_folder=serie_folder,
|
||||
year=explicit_year # Explicit year provided
|
||||
)
|
||||
|
||||
# Assert - should use explicit year, not extracted year
|
||||
mock_find_match.assert_called_once()
|
||||
call_args = mock_find_match.call_args
|
||||
assert call_args[0][2] == explicit_year # Third argument is year
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nfo_no_results_with_clean_name(self, nfo_service, tmp_path):
|
||||
"""Test error handling when TMDB returns no results even with clean name."""
|
||||
# Setup
|
||||
serie_name = "Nonexistent Series (2023)"
|
||||
serie_folder = "Nonexistent Series (2023)"
|
||||
(tmp_path / serie_folder).mkdir()
|
||||
|
||||
with patch.object(nfo_service.tmdb_client, '__aenter__', return_value=nfo_service.tmdb_client):
|
||||
with patch.object(nfo_service.tmdb_client, '__aexit__', return_value=None):
|
||||
with patch.object(nfo_service.tmdb_client, 'search_tv_show', new_callable=AsyncMock) as mock_search:
|
||||
mock_search.return_value = {"results": []}
|
||||
|
||||
# Act & Assert
|
||||
with pytest.raises(TMDBAPIError) as exc_info:
|
||||
await nfo_service.create_tvshow_nfo(
|
||||
serie_name=serie_name,
|
||||
serie_folder=serie_folder
|
||||
)
|
||||
|
||||
# Should use clean name in error message
|
||||
assert "No results found for: Nonexistent Series" in str(exc_info.value)
|
||||
# Should have searched with clean name
|
||||
mock_search.assert_called_once_with("Nonexistent Series")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nfo_with_fsk(self, nfo_service, tmp_path, mock_tmdb_data, mock_content_ratings_de):
|
||||
"""Test NFO creation includes FSK rating."""
|
||||
|
||||
Reference in New Issue
Block a user