"""Unit tests for NfoRepairService — Task 1.""" import shutil from pathlib import Path from unittest.mock import AsyncMock, MagicMock import pytest from src.core.services.nfo_repair_service import ( NfoRepairService, REQUIRED_TAGS, find_missing_tags, nfo_needs_repair, parse_nfo_tags, ) REPO_ROOT = Path(__file__).parents[2] BAD_NFO = REPO_ROOT / "tvshow.nfo.bad" GOOD_NFO = REPO_ROOT / "tvshow.nfo.good" # Tags known to be absent/empty in tvshow.nfo.bad EXPECTED_MISSING_FROM_BAD = { "originaltitle", "year", "plot", "runtime", "premiered", "status", "imdbid", "genre", "studio", "country", "actor/name", "watched", } # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture() def bad_nfo(tmp_path: Path) -> Path: """Copy tvshow.nfo.bad into a temp dir and return path to the copy.""" dest = tmp_path / "tvshow.nfo" shutil.copy(BAD_NFO, dest) return dest @pytest.fixture() def good_nfo(tmp_path: Path) -> Path: """Copy tvshow.nfo.good into a temp dir and return path to the copy.""" dest = tmp_path / "tvshow.nfo" shutil.copy(GOOD_NFO, dest) return dest @pytest.fixture() def mock_nfo_service() -> MagicMock: """Return a MagicMock NFOService with an async update_tvshow_nfo.""" svc = MagicMock() svc.update_tvshow_nfo = AsyncMock(return_value=Path("/fake/tvshow.nfo")) return svc # --------------------------------------------------------------------------- # find_missing_tags # --------------------------------------------------------------------------- def test_find_missing_tags_with_bad_nfo(bad_nfo: Path) -> None: """Bad NFO must report all 12 incomplete/missing tags.""" missing = find_missing_tags(bad_nfo) assert set(missing) == EXPECTED_MISSING_FROM_BAD, ( f"Unexpected missing set: {set(missing)}" ) def test_find_missing_tags_with_good_nfo(good_nfo: Path) -> None: """Good NFO must report no missing tags.""" missing = find_missing_tags(good_nfo) assert missing == [] # --------------------------------------------------------------------------- # nfo_needs_repair # --------------------------------------------------------------------------- def test_nfo_needs_repair_returns_true_for_bad_nfo(bad_nfo: Path) -> None: assert nfo_needs_repair(bad_nfo) is True def test_nfo_needs_repair_returns_false_for_good_nfo(good_nfo: Path) -> None: assert nfo_needs_repair(good_nfo) is False # --------------------------------------------------------------------------- # NfoRepairService.repair_series # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_repair_series_calls_update_when_nfo_needs_repair( tmp_path: Path, mock_nfo_service: MagicMock ) -> None: """repair_series must call update_tvshow_nfo exactly once for a bad NFO.""" shutil.copy(BAD_NFO, tmp_path / "tvshow.nfo") service = NfoRepairService(mock_nfo_service) result = await service.repair_series(tmp_path, "Test Series") assert result is True mock_nfo_service.update_tvshow_nfo.assert_called_once_with( "Test Series", download_media=False ) @pytest.mark.asyncio async def test_repair_series_skips_when_nfo_is_complete( tmp_path: Path, mock_nfo_service: MagicMock ) -> None: """repair_series must NOT call update_tvshow_nfo for a complete NFO.""" shutil.copy(GOOD_NFO, tmp_path / "tvshow.nfo") service = NfoRepairService(mock_nfo_service) result = await service.repair_series(tmp_path, "Test Series") assert result is False mock_nfo_service.update_tvshow_nfo.assert_not_called() # --------------------------------------------------------------------------- # parse_nfo_tags edge cases # --------------------------------------------------------------------------- def test_parse_nfo_tags_handles_missing_file_gracefully() -> None: """parse_nfo_tags must return empty dict for non-existent path.""" result = parse_nfo_tags(Path("/nonexistent/dir/tvshow.nfo")) assert result == {} def test_parse_nfo_tags_handles_malformed_xml_gracefully(tmp_path: Path) -> None: """parse_nfo_tags must return empty dict for malformed XML.""" bad_xml = tmp_path / "tvshow.nfo" bad_xml.write_text("<<< not valid xml >>>", encoding="utf-8") result = parse_nfo_tags(bad_xml) assert result == {}