""" Tests for alfred.application.filesystem.resolve_destination Uses a real temp filesystem + a real Memory instance (via conftest fixtures). No network calls — TMDB data is passed in directly. """ from pathlib import Path from alfred.application.filesystem.resolve_destination import ( ResolveDestinationUseCase, _find_existing_series_folders, ) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _use_case(): return ResolveDestinationUseCase() # --------------------------------------------------------------------------- # Movies # --------------------------------------------------------------------------- class TestResolveMovie: def test_basic_movie(self, memory_configured): result = _use_case().execute( release_name="Another.Round.2020.1080p.BluRay.x264-YTS", source_file="/downloads/Another.Round.2020.1080p.BluRay.x264-YTS/Another.Round.2020.1080p.BluRay.x264-YTS.mp4", tmdb_title="Another Round", tmdb_year=2020, ) assert result.status == "ok" assert "Another.Round.2020" in result.series_folder_name assert "1080p.BluRay.x264-YTS" in result.series_folder_name assert result.filename.endswith(".mp4") assert result.season_folder is None def test_movie_library_file_path_is_inside_series_folder(self, memory_configured): result = _use_case().execute( release_name="Revolver.2005.1080p.BluRay.x265-RARBG", source_file="/downloads/Revolver.2005.1080p.BluRay.x265-RARBG.mkv", tmdb_title="Revolver", tmdb_year=2005, ) assert result.status == "ok" assert result.library_file.startswith(result.series_folder) def test_movie_library_not_set(self, memory): # memory has no library paths configured result = _use_case().execute( release_name="Revolver.2005.1080p.BluRay.x265-RARBG", source_file="/downloads/Revolver.2005.1080p.BluRay.x265-RARBG.mkv", tmdb_title="Revolver", tmdb_year=2005, ) assert result.status == "error" assert result.error == "library_not_set" def test_movie_folder_marked_new(self, memory_configured): # No existing folder → is_new_series_folder = True result = _use_case().execute( release_name="Godzilla.Minus.One.2023.1080p.BluRay.x265-YTS", source_file="/downloads/Godzilla.Minus.One.2023.1080p.BluRay.x265-YTS.mp4", tmdb_title="Godzilla Minus One", tmdb_year=2023, ) assert result.status == "ok" assert result.is_new_series_folder is True def test_movie_sanitises_forbidden_chars_in_title(self, memory_configured): result = _use_case().execute( release_name="Alien.Earth.2024.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Alien.Earth.2024.1080p.WEBRip.x265-KONTRAST.mkv", tmdb_title="Alien: Earth", tmdb_year=2024, ) assert result.status == "ok" assert ":" not in result.series_folder_name def test_to_dict_ok(self, memory_configured): result = _use_case().execute( release_name="Revolver.2005.1080p.BluRay.x265-RARBG", source_file="/downloads/Revolver.mkv", tmdb_title="Revolver", tmdb_year=2005, ) d = result.to_dict() assert d["status"] == "ok" assert "library_file" in d assert "series_folder_name" in d # --------------------------------------------------------------------------- # TV shows — no existing folder # --------------------------------------------------------------------------- class TestResolveTVShowNewFolder: def test_oz_s01_creates_new_folder(self, memory_configured): result = _use_case().execute( release_name="Oz.S01.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S01.1080p.WEBRip.x265-KONTRAST/Oz.S01E01.1080p.WEBRip.x265-KONTRAST.mp4", tmdb_title="Oz", tmdb_year=1997, ) assert result.status == "ok" assert result.is_new_series_folder is True assert result.series_folder_name == "Oz.1997.1080p.WEBRip.x265-KONTRAST" assert result.season_folder_name == "Oz.S01.1080p.WEBRip.x265-KONTRAST" def test_tv_library_not_set(self, memory): result = _use_case().execute( release_name="Oz.S01.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S01E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) assert result.status == "error" assert result.error == "library_not_set" def test_single_episode_filename(self, memory_configured): result = _use_case().execute( release_name="Fallout.2024.S02E01.1080p.x265-ELiTE", source_file="/downloads/Fallout.2024.S02E01.1080p.x265-ELiTE.mkv", tmdb_title="Fallout", tmdb_year=2024, tmdb_episode_title="The Beginning", ) assert result.status == "ok" assert "S02E01" in result.filename assert "The.Beginning" in result.filename assert result.filename.endswith(".mkv") def test_season_pack_filename_is_folder_name_plus_ext(self, memory_configured): result = _use_case().execute( release_name="Oz.S01.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S01.1080p.WEBRip.x265-KONTRAST/Oz.S01E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) assert result.status == "ok" # Season pack: filename = season_folder_name + ext assert result.filename == result.season_folder_name + ".mp4" def test_library_file_is_inside_season_folder(self, memory_configured): result = _use_case().execute( release_name="Oz.S01.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S01E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) assert result.library_file.startswith(result.season_folder) assert result.season_folder.startswith(result.series_folder) # --------------------------------------------------------------------------- # TV shows — existing folder matching # --------------------------------------------------------------------------- class TestResolveTVShowExistingFolder: def _make_series_folder(self, tv_root, name): """Create a series folder in the tv library.""" path = tv_root / name path.mkdir(parents=True, exist_ok=True) return path def test_uses_existing_single_folder(self, memory_configured, app_temp): """When exactly one folder matches title+year, use it regardless of group.""" from alfred.infrastructure.persistence import get_memory mem = get_memory() tv_root = Path(mem.ltm.library_paths.get("tv_show")) existing = tv_root / "Oz.1997.1080p.WEBRip.x265-RARBG" existing.mkdir(parents=True, exist_ok=True) result = _use_case().execute( release_name="Oz.S02.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S02E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) assert result.status == "ok" assert result.series_folder_name == "Oz.1997.1080p.WEBRip.x265-RARBG" assert result.is_new_series_folder is False def test_needs_clarification_on_multiple_folders(self, memory_configured, app_temp): """When multiple folders match, return needs_clarification with options.""" from alfred.infrastructure.persistence import get_memory mem = get_memory() tv_root = Path(mem.ltm.library_paths.get("tv_show")) (tv_root / "Slow.Horses.2022.1080p.WEBRip.x265-RARBG").mkdir( parents=True, exist_ok=True ) (tv_root / "Slow.Horses.2022.1080p.WEBRip.x265-KONTRAST").mkdir( parents=True, exist_ok=True ) result = _use_case().execute( release_name="Slow.Horses.S05.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Slow.Horses.S05E01.mkv", tmdb_title="Slow Horses", tmdb_year=2022, ) assert result.status == "needs_clarification" assert result.question is not None assert len(result.options) == 2 assert "Slow.Horses.2022.1080p.WEBRip.x265-RARBG" in result.options assert "Slow.Horses.2022.1080p.WEBRip.x265-KONTRAST" in result.options def test_confirmed_folder_bypasses_detection(self, memory_configured, app_temp): """confirmed_folder skips the folder search.""" from alfred.infrastructure.persistence import get_memory mem = get_memory() tv_root = Path(mem.ltm.library_paths.get("tv_show")) chosen = "Slow.Horses.2022.1080p.WEBRip.x265-RARBG" (tv_root / chosen).mkdir(parents=True, exist_ok=True) result = _use_case().execute( release_name="Slow.Horses.S05.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Slow.Horses.S05E01.mkv", tmdb_title="Slow Horses", tmdb_year=2022, confirmed_folder=chosen, ) assert result.status == "ok" assert result.series_folder_name == chosen def test_to_dict_needs_clarification(self, memory_configured, app_temp): from alfred.infrastructure.persistence import get_memory mem = get_memory() tv_root = Path(mem.ltm.library_paths.get("tv_show")) (tv_root / "Oz.1997.1080p.WEBRip.x265-RARBG").mkdir(parents=True, exist_ok=True) (tv_root / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir( parents=True, exist_ok=True ) result = _use_case().execute( release_name="Oz.S03.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S03E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) d = result.to_dict() assert d["status"] == "needs_clarification" assert "question" in d assert isinstance(d["options"], list) def test_to_dict_error(self, memory): result = _use_case().execute( release_name="Oz.S01.1080p.WEBRip.x265-KONTRAST", source_file="/downloads/Oz.S01E01.mp4", tmdb_title="Oz", tmdb_year=1997, ) d = result.to_dict() assert d["status"] == "error" assert "error" in d assert "message" in d # --------------------------------------------------------------------------- # _find_existing_series_folders # --------------------------------------------------------------------------- class TestFindExistingSeriesFolders: def test_empty_library(self, tmp_path): assert _find_existing_series_folders(tmp_path, "Oz", 1997) == [] def test_nonexistent_root(self, tmp_path): assert _find_existing_series_folders(tmp_path / "nope", "Oz", 1997) == [] def test_single_match(self, tmp_path): (tmp_path / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir() result = _find_existing_series_folders(tmp_path, "Oz", 1997) assert result == ["Oz.1997.1080p.WEBRip.x265-KONTRAST"] def test_multiple_matches(self, tmp_path): (tmp_path / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir() (tmp_path / "Oz.1997.1080p.WEBRip.x265-RARBG").mkdir() result = _find_existing_series_folders(tmp_path, "Oz", 1997) assert len(result) == 2 assert sorted(result) == result # sorted def test_no_match_different_year(self, tmp_path): (tmp_path / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir() result = _find_existing_series_folders(tmp_path, "Oz", 2000) assert result == [] def test_no_match_different_title(self, tmp_path): (tmp_path / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir() result = _find_existing_series_folders(tmp_path, "Breaking Bad", 2008) assert result == [] def test_ignores_files_not_dirs(self, tmp_path): (tmp_path / "Oz.1997.1080p.WEBRip.x265-KONTRAST").mkdir() (tmp_path / "Oz.1997.some.file.txt").touch() result = _find_existing_series_folders(tmp_path, "Oz", 1997) assert len(result) == 1 def test_case_insensitive_prefix(self, tmp_path): # Folder stored with mixed case (tmp_path / "OZ.1997.1080p.WEBRip.x265-KONTRAST").mkdir() result = _find_existing_series_folders(tmp_path, "Oz", 1997) assert len(result) == 1 def test_title_with_special_chars_sanitised(self, tmp_path): # "Star Wars: Andor" → sanitised (colon removed) + spaces→dots → "Star.Wars.Andor.2022" (tmp_path / "Star.Wars.Andor.2022.1080p.WEBRip.x265-GROUP").mkdir() result = _find_existing_series_folders(tmp_path, "Star Wars: Andor", 2022) assert len(result) == 1