import pytest import json from pathlib import Path from src.core.file import File from src.core.tag import Tag from src.core.tag_manager import TagManager class TestFile: """Testy pro třídu File""" @pytest.fixture def temp_dir(self, tmp_path): """Fixture pro dočasný adresář""" return tmp_path @pytest.fixture def tag_manager(self): """Fixture pro TagManager""" return TagManager() @pytest.fixture def test_file(self, temp_dir): """Fixture pro testovací soubor""" test_file = temp_dir / "test.txt" test_file.write_text("test content") return test_file def test_file_creation(self, test_file, tag_manager): """Test vytvoření File objektu""" file_obj = File(test_file, tag_manager) assert file_obj.file_path == test_file assert file_obj.filename == "test.txt" assert file_obj.new == True def test_file_metadata_filename(self, test_file, tag_manager): """Test názvu metadata souboru""" file_obj = File(test_file, tag_manager) expected = test_file.parent / ".test.txt.!tag" assert file_obj.metadata_filename == expected def test_file_initial_tags(self, test_file, tag_manager): """Test že nový soubor nemá žádné automatické tagy (Stav/Nové odstraněn)""" file_obj = File(test_file, tag_manager) assert file_obj.tags == [] def test_file_metadata_saved(self, test_file, tag_manager): """Test že metadata jsou uložena při vytvoření""" file_obj = File(test_file, tag_manager) assert file_obj.metadata_filename.exists() def test_file_save_metadata(self, test_file, tag_manager): """Test uložení metadat""" file_obj = File(test_file, tag_manager) file_obj.new = False file_obj.ignored = True file_obj.save_metadata() # Načtení a kontrola with open(file_obj.metadata_filename, "r", encoding="utf-8") as f: data = json.load(f) assert data["new"] == False assert data["ignored"] == True def test_file_load_metadata(self, test_file, tag_manager): """Test načtení metadat""" # Vytvoření a uložení metadat file_obj = File(test_file, tag_manager) tag = tag_manager.add_tag("Video", "HD") file_obj.tags.append(tag) file_obj.date = "2025-01-15" file_obj.save_metadata() # Vytvoření nového objektu - měl by načíst metadata file_obj2 = File(test_file, tag_manager) assert len(file_obj2.tags) == 1 # Video/HD assert file_obj2.date == "2025-01-15" # Kontrola že tagy obsahují správné hodnoty tag_paths = {tag.full_path for tag in file_obj2.tags} assert "Video/HD" in tag_paths def test_file_set_date(self, test_file, tag_manager): """Test nastavení data""" file_obj = File(test_file, tag_manager) file_obj.set_date("2025-12-25") assert file_obj.date == "2025-12-25" # Kontrola že bylo uloženo with open(file_obj.metadata_filename, "r", encoding="utf-8") as f: data = json.load(f) assert data["date"] == "2025-12-25" def test_file_set_date_to_none(self, test_file, tag_manager): """Test smazání data""" file_obj = File(test_file, tag_manager) file_obj.set_date("2025-12-25") file_obj.set_date(None) assert file_obj.date is None def test_file_set_date_empty_string(self, test_file, tag_manager): """Test nastavení prázdného řetězce jako datum""" file_obj = File(test_file, tag_manager) file_obj.set_date("2025-12-25") file_obj.set_date("") assert file_obj.date is None def test_file_add_tag_object(self, test_file, tag_manager): """Test přidání Tag objektu""" file_obj = File(test_file, tag_manager) tag = Tag("Video", "4K") file_obj.add_tag(tag) assert tag in file_obj.tags assert len(file_obj.tags) == 1 # Video/4K def test_file_add_tag_string(self, test_file, tag_manager): """Test přidání tagu jako string""" file_obj = File(test_file, tag_manager) file_obj.add_tag("Audio/MP3") tag_paths = {tag.full_path for tag in file_obj.tags} assert "Audio/MP3" in tag_paths def test_file_add_tag_string_without_category(self, test_file, tag_manager): """Test přidání tagu bez kategorie (použije 'default')""" file_obj = File(test_file, tag_manager) file_obj.add_tag("SimpleTag") tag_paths = {tag.full_path for tag in file_obj.tags} assert "default/SimpleTag" in tag_paths def test_file_add_duplicate_tag(self, test_file, tag_manager): """Test že duplicitní tag není přidán""" file_obj = File(test_file, tag_manager) tag = Tag("Video", "HD") file_obj.add_tag(tag) file_obj.add_tag(tag) # Spočítáme kolikrát se tag vyskytuje count = sum(1 for t in file_obj.tags if t == tag) assert count == 1 def test_file_remove_tag_object(self, test_file, tag_manager): """Test odstranění Tag objektu""" file_obj = File(test_file, tag_manager) tag = Tag("Video", "HD") file_obj.add_tag(tag) file_obj.remove_tag(tag) assert tag not in file_obj.tags def test_file_remove_tag_string(self, test_file, tag_manager): """Test odstranění tagu jako string""" file_obj = File(test_file, tag_manager) file_obj.add_tag("Video/HD") file_obj.remove_tag("Video/HD") tag_paths = {tag.full_path for tag in file_obj.tags} assert "Video/HD" not in tag_paths def test_file_remove_tag_string_without_category(self, test_file, tag_manager): """Test odstranění tagu bez kategorie""" file_obj = File(test_file, tag_manager) file_obj.add_tag("SimpleTag") file_obj.remove_tag("SimpleTag") tag_paths = {tag.full_path for tag in file_obj.tags} assert "default/SimpleTag" not in tag_paths def test_file_remove_nonexistent_tag(self, test_file, tag_manager): """Test odstranění neexistujícího tagu (nemělo by vyhodit výjimku)""" file_obj = File(test_file, tag_manager) initial_count = len(file_obj.tags) file_obj.remove_tag("Nonexistent/Tag") assert len(file_obj.tags) == initial_count def test_file_without_tagmanager(self, test_file): """Test File bez TagManager""" file_obj = File(test_file, tagmanager=None) assert file_obj.tagmanager is None assert len(file_obj.tags) == 0 # nový soubor nemá žádné automatické tagy def test_file_metadata_persistence(self, test_file, tag_manager): """Test že metadata přežijí reload""" # Vytvoření a úprava souboru file_obj1 = File(test_file, tag_manager) file_obj1.add_tag("Video/HD") file_obj1.add_tag("Audio/Stereo") file_obj1.set_date("2025-01-01") file_obj1.new = False file_obj1.ignored = True file_obj1.save_metadata() # Načtení nového objektu file_obj2 = File(test_file, tag_manager) # Kontrola assert file_obj2.new == False assert file_obj2.ignored == True assert file_obj2.date == "2025-01-01" tag_paths = {tag.full_path for tag in file_obj2.tags} assert "Video/HD" in tag_paths assert "Audio/Stereo" in tag_paths def test_file_metadata_json_format(self, test_file, tag_manager): """Test formátu JSON metadat""" file_obj = File(test_file, tag_manager) file_obj.add_tag("Test/Tag") file_obj.set_date("2025-06-15") # Kontrola obsahu JSON with open(file_obj.metadata_filename, "r", encoding="utf-8") as f: data = json.load(f) assert "new" in data assert "ignored" in data assert "tags" in data assert "date" in data assert isinstance(data["tags"], list) def test_file_unicode_handling(self, temp_dir, tag_manager): """Test správného zacházení s unicode znaky""" test_file = temp_dir / "český_soubor.txt" test_file.write_text("obsah") file_obj = File(test_file, tag_manager) file_obj.add_tag("Kategorie/Český tag") file_obj.save_metadata() # Reload a kontrola file_obj2 = File(test_file, tag_manager) tag_paths = {tag.full_path for tag in file_obj2.tags} assert "Kategorie/Český tag" in tag_paths def test_file_complex_scenario(self, test_file, tag_manager): """Test komplexního scénáře použití""" file_obj = File(test_file, tag_manager) # Přidání více tagů file_obj.add_tag("Video/HD") file_obj.add_tag("Video/Stereo") file_obj.add_tag("Stav/Zkontrolováno") file_obj.set_date("2025-01-01") # Odstranění tagu file_obj.remove_tag("Stav/Nové") # Kontrola stavu tag_paths = {tag.full_path for tag in file_obj.tags} assert "Video/HD" in tag_paths assert "Video/Stereo" in tag_paths assert "Stav/Zkontrolováno" in tag_paths assert "Stav/Nové" not in tag_paths assert file_obj.date == "2025-01-01" # Reload a kontrola persistence file_obj2 = File(test_file, tag_manager) tag_paths2 = {tag.full_path for tag in file_obj2.tags} assert tag_paths == tag_paths2 assert file_obj2.date == "2025-01-01" class TestApplyCsfdTags: """Tests for File.apply_csfd_tags tag assignment (CSFD fetch is mocked).""" @pytest.fixture def tag_manager(self): return TagManager() @pytest.fixture def movie_file(self, tmp_path, tag_manager): path = tmp_path / "Matrix.mkv" path.write_text("x") f = File(path, tag_manager) f.set_csfd_link("https://www.csfd.cz/film/9499-matrix/") return f def test_apply_csfd_tags_assigns_expected_categories(self, movie_file): from unittest.mock import patch from src.core.csfd import CSFDMovie movie = CSFDMovie( title="Matrix", url="u", year=1999, genres=["Akční", "Sci-Fi"], directors=["Lana Wachowski", "Lilly Wachowski"], actors=["Keanu Reeves", "Laurence Fishburne"], rating=90, countries=["USA"], ) with patch("src.core.csfd.fetch_movie", return_value=movie): result = movie_file.apply_csfd_tags() assert result["success"] paths = {t.full_path for t in movie_file.tags} assert "Žánr/Akční" in paths assert "Žánr/Sci-Fi" in paths assert "Rok/1999" in paths assert "Země původu/USA" in paths assert "Hodnocení/90" in paths # exact value; folder grouping happens later def test_apply_csfd_tags_does_not_tag_directors_or_actors(self, movie_file): """Režie/herci se jen cachují, netvoří se z nich tagy (bylo by jich moc).""" from unittest.mock import patch from src.core.csfd import CSFDMovie movie = CSFDMovie( title="Matrix", url="u", directors=["Lana Wachowski"], actors=["Keanu Reeves", "Laurence Fishburne"], genres=["Drama"], ) with patch("src.core.csfd.fetch_movie", return_value=movie): movie_file.apply_csfd_tags() paths = {t.full_path for t in movie_file.tags} assert not any(p.startswith("Režie/") for p in paths) assert not any(p.startswith("Herec/") for p in paths) # …but the data is kept in the cache cached = movie_file.get_cached_movie() assert cached.directors == ["Lana Wachowski"] assert cached.actors == ["Keanu Reeves", "Laurence Fishburne"] def test_name_context_fields(self, movie_file): movie_file.title = "Dr. No" movie_file.csfd_cache = {"year": 1962, "rating": 75} ctx = movie_file.name_context() assert ctx["title"] == "Dr. No" assert ctx["year"] == 1962 assert ctx["rating"] == 75 assert ctx["ext"] == movie_file.file_path.suffix assert ctx["filename"] == movie_file.filename assert "{year} - {title}{ext}".format(**ctx) == f"1962 - Dr. No{ctx['ext']}" def test_name_context_year_from_tag_when_no_cache(self, movie_file): movie_file.csfd_cache = None movie_file.add_tag("Rok/1999") assert movie_file.name_context()["year"] == "1999" def test_set_attribute_persists_and_in_context(self, movie_file): movie_file.set_attribute("collection_sort", "03") assert movie_file.attributes["collection_sort"] == "03" assert movie_file.name_context()["collection_sort"] == "03" # reload (from index) keeps it reloaded = File(movie_file.file_path, movie_file.tagmanager, index=movie_file.index) assert reloaded.attributes["collection_sort"] == "03" def test_set_attribute_empty_removes_it(self, movie_file): movie_file.set_attribute("collection_sort", "03") movie_file.set_attribute("collection_sort", None) assert "collection_sort" not in movie_file.attributes def test_attribute_usable_in_filename_template(self, movie_file): movie_file.title = "Dr. No" movie_file.set_attribute("collection_sort", "01") ctx = movie_file.name_context() assert "{collection_sort} - {title}{ext}".format(**ctx) == \ f"01 - Dr. No{ctx['ext']}" def test_apply_csfd_tags_honors_custom_schema(self, movie_file): """A schema without the rating entry produces no Hodnocení tags.""" from unittest.mock import patch from src.core.csfd import CSFDMovie schema = [{"category": "Žánr", "csfd_field": "genres", "transform": None, "filmoteka_root": ""}] movie = CSFDMovie(title="Matrix", url="u", rating=90, genres=["Drama"]) with patch("src.core.csfd.fetch_movie", return_value=movie): movie_file.apply_csfd_tags(schema) paths = {t.full_path for t in movie_file.tags} assert "Žánr/Drama" in paths assert not any(p.startswith("Hodnocení/") for p in paths) def test_apply_csfd_tags_preserves_user_tags_on_refetch(self, movie_file): """Re-fetching regenerates only ČSFD tags; user-added tags survive.""" from unittest.mock import patch from src.core.csfd import CSFDMovie movie_file.add_tag("Sbírka/Oblíbené") # user tag first = CSFDMovie(title="A", url="u", year=1999, genres=["Akční"]) with patch("src.core.csfd.fetch_movie", return_value=first): movie_file.apply_csfd_tags() # different movie on re-fetch second = CSFDMovie(title="B", url="u", year=2009, genres=["Drama"]) with patch("src.core.csfd.fetch_movie", return_value=second): movie_file.apply_csfd_tags() paths = {t.full_path for t in movie_file.tags} assert "Sbírka/Oblíbené" in paths # user tag kept assert "Žánr/Drama" in paths # new ČSFD tag assert "Rok/2009" in paths assert "Žánr/Akční" not in paths # stale ČSFD tag dropped assert "Rok/1999" not in paths