feat: Implement NFOService.update_tvshow_nfo()
- Parse existing NFO to extract TMDB ID from uniqueid or tmdbid element - Fetch fresh metadata from TMDB API - Regenerate NFO with updated data - Optionally re-download media files - Add comprehensive error handling (missing NFO, no TMDB ID, invalid XML) - Add unit tests for XML parsing logic (4 tests, all passing) - Add integration test script (requires TMDB API key)
This commit is contained in:
178
tests/unit/test_nfo_update_parsing.py
Normal file
178
tests/unit/test_nfo_update_parsing.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""Unit test for NFOService.update_tvshow_nfo() - tests XML parsing logic."""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
from lxml import etree
|
||||
import pytest
|
||||
|
||||
from src.core.services.nfo_service import NFOService
|
||||
from src.core.services.tmdb_client import TMDBAPIError
|
||||
|
||||
|
||||
def create_sample_nfo(tmdb_id: int = 1429) -> str:
|
||||
"""Create a sample NFO XML with TMDB ID."""
|
||||
return f'''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tvshow>
|
||||
<title>Attack on Titan</title>
|
||||
<originaltitle>Shingeki no Kyojin</originaltitle>
|
||||
<year>2013</year>
|
||||
<plot>Several hundred years ago, humans were nearly exterminated by Titans.</plot>
|
||||
<uniqueid type="tmdb" default="false">{tmdb_id}</uniqueid>
|
||||
<uniqueid type="tvdb" default="true">267440</uniqueid>
|
||||
<tmdbid>{tmdb_id}</tmdbid>
|
||||
<tvdbid>267440</tvdbid>
|
||||
</tvshow>'''
|
||||
|
||||
|
||||
def test_parse_nfo_with_uniqueid():
|
||||
"""Test parsing NFO with uniqueid elements."""
|
||||
# Create temporary directory structure
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
serie_folder = temp_dir / "test_series"
|
||||
serie_folder.mkdir()
|
||||
nfo_path = serie_folder / "tvshow.nfo"
|
||||
|
||||
try:
|
||||
# Write sample NFO
|
||||
nfo_path.write_text(create_sample_nfo(1429), encoding="utf-8")
|
||||
|
||||
# Parse it (same logic as in update_tvshow_nfo)
|
||||
tree = etree.parse(str(nfo_path))
|
||||
root = tree.getroot()
|
||||
|
||||
# Extract TMDB ID
|
||||
tmdb_id = None
|
||||
for uniqueid in root.findall(".//uniqueid"):
|
||||
if uniqueid.get("type") == "tmdb":
|
||||
tmdb_id = int(uniqueid.text)
|
||||
break
|
||||
|
||||
assert tmdb_id == 1429, f"Expected TMDB ID 1429, got {tmdb_id}"
|
||||
print(f"✓ Successfully parsed TMDB ID from uniqueid: {tmdb_id}")
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
def test_parse_nfo_with_tmdbid_element():
|
||||
"""Test parsing NFO with tmdbid element (fallback)."""
|
||||
# Create NFO without uniqueid but with tmdbid element
|
||||
nfo_content = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tvshow>
|
||||
<title>Test Show</title>
|
||||
<tmdbid>12345</tmdbid>
|
||||
</tvshow>'''
|
||||
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
serie_folder = temp_dir / "test_series"
|
||||
serie_folder.mkdir()
|
||||
nfo_path = serie_folder / "tvshow.nfo"
|
||||
|
||||
try:
|
||||
nfo_path.write_text(nfo_content, encoding="utf-8")
|
||||
|
||||
# Parse it
|
||||
tree = etree.parse(str(nfo_path))
|
||||
root = tree.getroot()
|
||||
|
||||
# Try uniqueid first (should fail)
|
||||
tmdb_id = None
|
||||
for uniqueid in root.findall(".//uniqueid"):
|
||||
if uniqueid.get("type") == "tmdb":
|
||||
tmdb_id = int(uniqueid.text)
|
||||
break
|
||||
|
||||
# Fallback to tmdbid element
|
||||
if tmdb_id is None:
|
||||
tmdbid_elem = root.find(".//tmdbid")
|
||||
if tmdbid_elem is not None and tmdbid_elem.text:
|
||||
tmdb_id = int(tmdbid_elem.text)
|
||||
|
||||
assert tmdb_id == 12345, f"Expected TMDB ID 12345, got {tmdb_id}"
|
||||
print(f"✓ Successfully parsed TMDB ID from tmdbid element: {tmdb_id}")
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
def test_parse_nfo_without_tmdb_id():
|
||||
"""Test parsing NFO without TMDB ID raises appropriate error."""
|
||||
# Create NFO without any TMDB ID
|
||||
nfo_content = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tvshow>
|
||||
<title>Test Show</title>
|
||||
</tvshow>'''
|
||||
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
serie_folder = temp_dir / "test_series"
|
||||
serie_folder.mkdir()
|
||||
nfo_path = serie_folder / "tvshow.nfo"
|
||||
|
||||
try:
|
||||
nfo_path.write_text(nfo_content, encoding="utf-8")
|
||||
|
||||
# Parse it
|
||||
tree = etree.parse(str(nfo_path))
|
||||
root = tree.getroot()
|
||||
|
||||
# Try to extract TMDB ID
|
||||
tmdb_id = None
|
||||
for uniqueid in root.findall(".//uniqueid"):
|
||||
if uniqueid.get("type") == "tmdb":
|
||||
tmdb_id = int(uniqueid.text)
|
||||
break
|
||||
|
||||
if tmdb_id is None:
|
||||
tmdbid_elem = root.find(".//tmdbid")
|
||||
if tmdbid_elem is not None and tmdbid_elem.text:
|
||||
tmdb_id = int(tmdbid_elem.text)
|
||||
|
||||
assert tmdb_id is None, "Should not have found TMDB ID"
|
||||
print("✓ Correctly identified NFO without TMDB ID")
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
def test_parse_invalid_xml():
|
||||
"""Test parsing invalid XML raises appropriate error."""
|
||||
nfo_content = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tvshow>
|
||||
<title>Unclosed tag
|
||||
</tvshow>'''
|
||||
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
serie_folder = temp_dir / "test_series"
|
||||
serie_folder.mkdir()
|
||||
nfo_path = serie_folder / "tvshow.nfo"
|
||||
|
||||
try:
|
||||
nfo_path.write_text(nfo_content, encoding="utf-8")
|
||||
|
||||
# Try to parse - should raise XMLSyntaxError
|
||||
try:
|
||||
tree = etree.parse(str(nfo_path))
|
||||
assert False, "Should have raised XMLSyntaxError"
|
||||
except etree.XMLSyntaxError:
|
||||
print("✓ Correctly raised XMLSyntaxError for invalid XML")
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing NFO XML parsing logic...")
|
||||
print()
|
||||
|
||||
test_parse_nfo_with_uniqueid()
|
||||
test_parse_nfo_with_tmdbid_element()
|
||||
test_parse_nfo_without_tmdb_id()
|
||||
test_parse_invalid_xml()
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("✓ ALL TESTS PASSED")
|
||||
print("=" * 60)
|
||||
Reference in New Issue
Block a user