Store library settings in the pool index and drop legacy sidecars
This commit is contained in:
+97
-151
@@ -1,191 +1,154 @@
|
||||
import pytest
|
||||
import json
|
||||
from pathlib import Path
|
||||
from src.core.file import File
|
||||
from src.core.pool_index import PoolIndex
|
||||
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
|
||||
"""Testy pro třídu File (metadata žijí v pool indexu)"""
|
||||
|
||||
@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 index(self, tmp_path):
|
||||
"""Pool index that backs the File's metadata."""
|
||||
return PoolIndex(tmp_path)
|
||||
|
||||
def test_file_creation(self, test_file, tag_manager):
|
||||
"""Test vytvoření File objektu"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
@pytest.fixture
|
||||
def test_file(self, tmp_path):
|
||||
f = tmp_path / "test.txt"
|
||||
f.write_text("test content")
|
||||
return f
|
||||
|
||||
def test_file_creation(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
assert file_obj.file_path == test_file
|
||||
assert file_obj.filename == "test.txt"
|
||||
assert file_obj.new == True
|
||||
assert file_obj.new is 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)
|
||||
def test_file_initial_tags(self, test_file, tag_manager, index):
|
||||
"""Nový soubor nemá žádné automatické tagy (Stav/Nové odstraněn)."""
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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_metadata_saved_to_index(self, test_file, tag_manager, index):
|
||||
"""Metadata jsou zapsána do indexu už při vytvoření."""
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
assert index.get(file_obj.file_path) is not None
|
||||
|
||||
def test_file_save_metadata(self, test_file, tag_manager):
|
||||
"""Test uložení metadat"""
|
||||
def test_file_no_sidecar_written(self, test_file, tag_manager, index):
|
||||
"""Žádný .!tag sidecar už nevzniká."""
|
||||
File(test_file, tag_manager, index=index)
|
||||
assert not (test_file.parent / ".test.txt.!tag").exists()
|
||||
|
||||
def test_index_less_file_is_in_memory_only(self, test_file, tag_manager):
|
||||
"""Bez indexu je File jen v paměti — nic se nezapíše na disk."""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.add_tag("Video/HD")
|
||||
file_obj.save_metadata() # no-op
|
||||
assert not (test_file.parent / ".test.txt.!tag").exists()
|
||||
|
||||
def test_file_save_metadata(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
data = index.get(file_obj.file_path)
|
||||
assert data["new"] is False
|
||||
assert data["ignored"] is True
|
||||
|
||||
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)
|
||||
def test_file_load_metadata(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
# A fresh File over the same index reloads the metadata
|
||||
file_obj2 = File(test_file, tag_manager, index=index)
|
||||
assert len(file_obj2.tags) == 1 # Video/HD
|
||||
assert file_obj2.date == "2025-01-15"
|
||||
assert "Video/HD" in {t.full_path for t in file_obj2.tags}
|
||||
|
||||
# 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)
|
||||
def test_file_set_date(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
file_obj.set_date("2025-12-25")
|
||||
assert file_obj.date == "2025-12-25"
|
||||
assert index.get(file_obj.file_path)["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)
|
||||
def test_file_set_date_to_none(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
def test_file_set_date_empty_string(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
def test_file_add_tag_object(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
tag = Tag("Video", "4K")
|
||||
file_obj.add_tag(tag)
|
||||
|
||||
assert tag in file_obj.tags
|
||||
assert len(file_obj.tags) == 1 # Video/4K
|
||||
assert len(file_obj.tags) == 1
|
||||
|
||||
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)
|
||||
def test_file_add_tag_string(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
file_obj.add_tag("Audio/MP3")
|
||||
assert "Audio/MP3" in {t.full_path for t in file_obj.tags}
|
||||
|
||||
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)
|
||||
def test_file_add_tag_string_without_category(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
file_obj.add_tag("SimpleTag")
|
||||
assert "default/SimpleTag" in {t.full_path for t in file_obj.tags}
|
||||
|
||||
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)
|
||||
def test_file_add_duplicate_tag(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
tag = Tag("Video", "HD")
|
||||
file_obj.add_tag(tag)
|
||||
file_obj.add_tag(tag)
|
||||
assert sum(1 for t in file_obj.tags if t == tag) == 1
|
||||
|
||||
# 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)
|
||||
def test_file_remove_tag_object(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
def test_file_remove_tag_string(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
file_obj.add_tag("Video/HD")
|
||||
file_obj.remove_tag("Video/HD")
|
||||
assert "Video/HD" not in {t.full_path for t in file_obj.tags}
|
||||
|
||||
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)
|
||||
def test_file_remove_tag_string_without_category(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
file_obj.add_tag("SimpleTag")
|
||||
file_obj.remove_tag("SimpleTag")
|
||||
assert "default/SimpleTag" not in {t.full_path for t in file_obj.tags}
|
||||
|
||||
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)
|
||||
def test_file_remove_nonexistent_tag(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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
|
||||
assert len(file_obj.tags) == 0
|
||||
|
||||
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)
|
||||
def test_file_metadata_persistence(self, test_file, tag_manager, index):
|
||||
file_obj1 = File(test_file, tag_manager, index=index)
|
||||
file_obj1.add_tag("Video/HD")
|
||||
file_obj1.add_tag("Audio/Stereo")
|
||||
file_obj1.set_date("2025-01-01")
|
||||
@@ -193,73 +156,54 @@ class TestFile:
|
||||
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
|
||||
file_obj2 = File(test_file, tag_manager, index=index)
|
||||
assert file_obj2.new is False
|
||||
assert file_obj2.ignored is True
|
||||
assert file_obj2.date == "2025-01-01"
|
||||
|
||||
tag_paths = {tag.full_path for tag in file_obj2.tags}
|
||||
tag_paths = {t.full_path for t 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)
|
||||
def test_file_metadata_record_shape(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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)
|
||||
|
||||
data = index.get(file_obj.file_path)
|
||||
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"
|
||||
def test_file_unicode_handling(self, tmp_path, tag_manager, index):
|
||||
test_file = tmp_path / "český_soubor.txt"
|
||||
test_file.write_text("obsah")
|
||||
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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
|
||||
file_obj2 = File(test_file, tag_manager, index=index)
|
||||
assert "Kategorie/Český tag" in {t.full_path for t in file_obj2.tags}
|
||||
|
||||
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ů
|
||||
def test_file_complex_scenario(self, test_file, tag_manager, index):
|
||||
file_obj = File(test_file, tag_manager, index=index)
|
||||
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}
|
||||
tag_paths = {t.full_path for t 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
|
||||
file_obj2 = File(test_file, tag_manager, index=index)
|
||||
assert {t.full_path for t in file_obj2.tags} == tag_paths
|
||||
assert file_obj2.date == "2025-01-01"
|
||||
|
||||
|
||||
@@ -271,10 +215,14 @@ class TestApplyCsfdTags:
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def movie_file(self, tmp_path, tag_manager):
|
||||
def index(self, tmp_path):
|
||||
return PoolIndex(tmp_path)
|
||||
|
||||
@pytest.fixture
|
||||
def movie_file(self, tmp_path, tag_manager, index):
|
||||
path = tmp_path / "Matrix.mkv"
|
||||
path.write_text("x")
|
||||
f = File(path, tag_manager)
|
||||
f = File(path, tag_manager, index=index)
|
||||
f.set_csfd_link("https://www.csfd.cz/film/9499-matrix/")
|
||||
return f
|
||||
|
||||
@@ -314,7 +262,6 @@ class TestApplyCsfdTags:
|
||||
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"]
|
||||
@@ -392,7 +339,6 @@ class TestApplyCsfdTags:
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user