"""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'''
Attack on Titan
Shingeki no Kyojin
2013
Several hundred years ago, humans were nearly exterminated by Titans.
{tmdb_id}
267440
{tmdb_id}
267440
'''
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 = '''
Test Show
12345
'''
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 = '''
Test Show
'''
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 = '''
Unclosed tag
'''
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)