CSFD integration
This commit is contained in:
@@ -4,8 +4,7 @@ from pathlib import Path
|
||||
from src.core.config import (
|
||||
load_global_config, save_global_config, DEFAULT_GLOBAL_CONFIG,
|
||||
load_folder_config, save_folder_config, DEFAULT_FOLDER_CONFIG,
|
||||
get_folder_config_path, folder_has_config, FOLDER_CONFIG_NAME,
|
||||
load_config, save_config # Legacy functions
|
||||
get_folder_config_path, folder_has_config, FOLDER_CONFIG_NAME
|
||||
)
|
||||
|
||||
|
||||
@@ -266,36 +265,6 @@ class TestFolderConfig:
|
||||
assert loaded2["ignore_patterns"] == ["*.jpg"]
|
||||
|
||||
|
||||
class TestLegacyFunctions:
|
||||
"""Testy pro zpětnou kompatibilitu"""
|
||||
|
||||
@pytest.fixture
|
||||
def temp_global_config(self, tmp_path, monkeypatch):
|
||||
"""Fixture pro dočasný globální config soubor"""
|
||||
config_path = tmp_path / "config.json"
|
||||
import src.core.config as config_module
|
||||
monkeypatch.setattr(config_module, 'GLOBAL_CONFIG_FILE', config_path)
|
||||
return config_path
|
||||
|
||||
def test_load_config_legacy(self, temp_global_config):
|
||||
"""Test že load_config funguje jako alias pro load_global_config"""
|
||||
test_config = {**DEFAULT_GLOBAL_CONFIG, "last_folder": "/test"}
|
||||
|
||||
save_global_config(test_config)
|
||||
loaded = load_config()
|
||||
|
||||
assert loaded["last_folder"] == "/test"
|
||||
|
||||
def test_save_config_legacy(self, temp_global_config):
|
||||
"""Test že save_config funguje jako alias pro save_global_config"""
|
||||
test_config = {**DEFAULT_GLOBAL_CONFIG, "last_folder": "/legacy"}
|
||||
|
||||
save_config(test_config)
|
||||
loaded = load_global_config()
|
||||
|
||||
assert loaded["last_folder"] == "/legacy"
|
||||
|
||||
|
||||
class TestConfigEdgeCases:
|
||||
"""Testy pro edge cases"""
|
||||
|
||||
|
||||
262
tests/test_csfd.py
Normal file
262
tests/test_csfd.py
Normal file
@@ -0,0 +1,262 @@
|
||||
"""Tests for CSFD.cz scraper module."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from src.core.csfd import (
|
||||
CSFDMovie,
|
||||
fetch_movie,
|
||||
search_movies,
|
||||
fetch_movie_by_id,
|
||||
_extract_csfd_id,
|
||||
_parse_duration,
|
||||
_extract_json_ld,
|
||||
_extract_rating,
|
||||
_extract_poster,
|
||||
_extract_plot,
|
||||
_extract_genres,
|
||||
_extract_origin_info,
|
||||
_check_dependencies,
|
||||
)
|
||||
|
||||
|
||||
# Sample HTML for testing
|
||||
SAMPLE_JSON_LD = """
|
||||
{
|
||||
"@type": "Movie",
|
||||
"name": "Test Movie",
|
||||
"director": [{"@type": "Person", "name": "Test Director"}],
|
||||
"actor": [{"@type": "Person", "name": "Actor 1"}, {"@type": "Person", "name": "Actor 2"}],
|
||||
"aggregateRating": {"ratingValue": 85.5, "ratingCount": 1000},
|
||||
"duration": "PT120M",
|
||||
"description": "A test movie description."
|
||||
}
|
||||
"""
|
||||
|
||||
SAMPLE_HTML = """
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/ld+json">%s</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="film-rating-average">85%%</div>
|
||||
<div class="genres">
|
||||
<a href="/zanry/1/">Drama</a> /
|
||||
<a href="/zanry/2/">Thriller</a>
|
||||
</div>
|
||||
<div class="origin">Česko, 2020, 120 min</div>
|
||||
<div class="film-poster">
|
||||
<img src="//image.example.com/poster.jpg">
|
||||
</div>
|
||||
<div class="plot-full"><p>Full plot description.</p></div>
|
||||
</body>
|
||||
</html>
|
||||
""" % SAMPLE_JSON_LD
|
||||
|
||||
|
||||
class TestCSFDMovie:
|
||||
"""Tests for CSFDMovie dataclass."""
|
||||
|
||||
def test_csfd_movie_basic(self):
|
||||
"""Test basic CSFDMovie creation."""
|
||||
movie = CSFDMovie(title="Test", url="https://csfd.cz/film/123/")
|
||||
assert movie.title == "Test"
|
||||
assert movie.url == "https://csfd.cz/film/123/"
|
||||
assert movie.year is None
|
||||
assert movie.genres == []
|
||||
assert movie.rating is None
|
||||
|
||||
def test_csfd_movie_full(self):
|
||||
"""Test CSFDMovie with all fields."""
|
||||
movie = CSFDMovie(
|
||||
title="Test Movie",
|
||||
url="https://csfd.cz/film/123/",
|
||||
year=2020,
|
||||
genres=["Drama", "Thriller"],
|
||||
directors=["Director 1"],
|
||||
actors=["Actor 1", "Actor 2"],
|
||||
rating=85,
|
||||
rating_count=1000,
|
||||
duration=120,
|
||||
country="Česko",
|
||||
poster_url="https://image.example.com/poster.jpg",
|
||||
plot="A test movie.",
|
||||
csfd_id=123
|
||||
)
|
||||
assert movie.year == 2020
|
||||
assert movie.genres == ["Drama", "Thriller"]
|
||||
assert movie.rating == 85
|
||||
assert movie.duration == 120
|
||||
assert movie.csfd_id == 123
|
||||
|
||||
def test_csfd_movie_str(self):
|
||||
"""Test CSFDMovie string representation."""
|
||||
movie = CSFDMovie(
|
||||
title="Test Movie",
|
||||
url="https://csfd.cz/film/123/",
|
||||
year=2020,
|
||||
genres=["Drama"],
|
||||
directors=["Director 1"],
|
||||
rating=85
|
||||
)
|
||||
s = str(movie)
|
||||
assert "Test Movie (2020)" in s
|
||||
assert "85%" in s
|
||||
assert "Drama" in s
|
||||
assert "Director 1" in s
|
||||
|
||||
def test_csfd_movie_str_minimal(self):
|
||||
"""Test CSFDMovie string with minimal data."""
|
||||
movie = CSFDMovie(title="Test", url="https://csfd.cz/film/123/")
|
||||
s = str(movie)
|
||||
assert "Test" in s
|
||||
|
||||
|
||||
class TestHelperFunctions:
|
||||
"""Tests for helper functions."""
|
||||
|
||||
def test_extract_csfd_id_valid(self):
|
||||
"""Test extracting CSFD ID from valid URL."""
|
||||
assert _extract_csfd_id("https://www.csfd.cz/film/9423-pane-vy-jste-vdova/") == 9423
|
||||
assert _extract_csfd_id("https://www.csfd.cz/film/123456/") == 123456
|
||||
assert _extract_csfd_id("/film/999/prehled/") == 999
|
||||
|
||||
def test_extract_csfd_id_invalid(self):
|
||||
"""Test extracting CSFD ID from invalid URL."""
|
||||
assert _extract_csfd_id("https://www.csfd.cz/") is None
|
||||
assert _extract_csfd_id("not-a-url") is None
|
||||
|
||||
def test_parse_duration_valid(self):
|
||||
"""Test parsing ISO 8601 duration."""
|
||||
assert _parse_duration("PT97M") == 97
|
||||
assert _parse_duration("PT120M") == 120
|
||||
assert _parse_duration("PT60M") == 60
|
||||
|
||||
def test_parse_duration_invalid(self):
|
||||
"""Test parsing invalid duration."""
|
||||
assert _parse_duration("") is None
|
||||
assert _parse_duration("invalid") is None
|
||||
assert _parse_duration("PT") is None
|
||||
|
||||
|
||||
class TestHTMLExtraction:
|
||||
"""Tests for HTML extraction functions."""
|
||||
|
||||
@pytest.fixture
|
||||
def soup(self):
|
||||
"""Create BeautifulSoup object from sample HTML."""
|
||||
from bs4 import BeautifulSoup
|
||||
return BeautifulSoup(SAMPLE_HTML, "html.parser")
|
||||
|
||||
def test_extract_json_ld(self, soup):
|
||||
"""Test extracting data from JSON-LD."""
|
||||
data = _extract_json_ld(soup)
|
||||
assert data["title"] == "Test Movie"
|
||||
assert data["directors"] == ["Test Director"]
|
||||
assert data["actors"] == ["Actor 1", "Actor 2"]
|
||||
assert data["rating"] == 86 # Rounded from 85.5
|
||||
assert data["rating_count"] == 1000
|
||||
assert data["duration"] == 120
|
||||
|
||||
def test_extract_rating(self, soup):
|
||||
"""Test extracting rating from HTML."""
|
||||
rating = _extract_rating(soup)
|
||||
assert rating == 85
|
||||
|
||||
def test_extract_genres(self, soup):
|
||||
"""Test extracting genres from HTML."""
|
||||
genres = _extract_genres(soup)
|
||||
assert "Drama" in genres
|
||||
assert "Thriller" in genres
|
||||
|
||||
def test_extract_poster(self, soup):
|
||||
"""Test extracting poster URL."""
|
||||
poster = _extract_poster(soup)
|
||||
assert poster == "https://image.example.com/poster.jpg"
|
||||
|
||||
def test_extract_plot(self, soup):
|
||||
"""Test extracting plot."""
|
||||
plot = _extract_plot(soup)
|
||||
assert plot == "Full plot description."
|
||||
|
||||
def test_extract_origin_info(self, soup):
|
||||
"""Test extracting origin info."""
|
||||
info = _extract_origin_info(soup)
|
||||
assert info["country"] == "Česko"
|
||||
assert info["year"] == 2020
|
||||
assert info["duration"] == 120
|
||||
|
||||
|
||||
class TestFetchMovie:
|
||||
"""Tests for fetch_movie function."""
|
||||
|
||||
@patch("src.core.csfd.requests")
|
||||
def test_fetch_movie_success(self, mock_requests):
|
||||
"""Test successful movie fetch."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.text = SAMPLE_HTML
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_requests.get.return_value = mock_response
|
||||
|
||||
movie = fetch_movie("https://www.csfd.cz/film/123-test/")
|
||||
|
||||
assert movie.title == "Test Movie"
|
||||
assert movie.csfd_id == 123
|
||||
assert movie.rating == 86
|
||||
assert "Drama" in movie.genres
|
||||
mock_requests.get.assert_called_once()
|
||||
|
||||
@patch("src.core.csfd.requests")
|
||||
def test_fetch_movie_network_error(self, mock_requests):
|
||||
"""Test network error handling."""
|
||||
import requests as real_requests
|
||||
mock_requests.get.side_effect = real_requests.RequestException("Network error")
|
||||
|
||||
with pytest.raises(real_requests.RequestException):
|
||||
fetch_movie("https://www.csfd.cz/film/123/")
|
||||
|
||||
|
||||
class TestSearchMovies:
|
||||
"""Tests for search_movies function."""
|
||||
|
||||
@patch("src.core.csfd.requests")
|
||||
def test_search_movies(self, mock_requests):
|
||||
"""Test movie search."""
|
||||
search_html = """
|
||||
<html><body>
|
||||
<a href="/film/123-test/" class="film-title-name">Test Movie</a>
|
||||
<a href="/film/456-another/" class="film-title-name">Another Movie</a>
|
||||
</body></html>
|
||||
"""
|
||||
mock_response = MagicMock()
|
||||
mock_response.text = search_html
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_requests.get.return_value = mock_response
|
||||
mock_requests.utils.quote = lambda x: x
|
||||
|
||||
results = search_movies("test", limit=10)
|
||||
|
||||
assert len(results) >= 1
|
||||
assert any(m.csfd_id == 123 for m in results)
|
||||
|
||||
|
||||
class TestFetchMovieById:
|
||||
"""Tests for fetch_movie_by_id function."""
|
||||
|
||||
@patch("src.core.csfd.fetch_movie")
|
||||
def test_fetch_by_id(self, mock_fetch):
|
||||
"""Test fetching movie by ID."""
|
||||
mock_fetch.return_value = CSFDMovie(title="Test", url="https://csfd.cz/film/9423/")
|
||||
|
||||
movie = fetch_movie_by_id(9423)
|
||||
|
||||
mock_fetch.assert_called_once_with("https://www.csfd.cz/film/9423/")
|
||||
assert movie.title == "Test"
|
||||
|
||||
|
||||
class TestDependencyCheck:
|
||||
"""Tests for dependency checking."""
|
||||
|
||||
def test_dependencies_available(self):
|
||||
"""Test that dependencies are available (they should be in test env)."""
|
||||
# Should not raise
|
||||
_check_dependencies()
|
||||
@@ -263,3 +263,155 @@ class TestFile:
|
||||
tag_paths2 = {tag.full_path for tag in file_obj2.tags}
|
||||
assert tag_paths == tag_paths2
|
||||
assert file_obj2.date == "2025-01-01"
|
||||
|
||||
|
||||
class TestFileCSFDIntegration:
|
||||
"""Testy pro CSFD integraci v File"""
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir(self, tmp_path):
|
||||
return tmp_path
|
||||
|
||||
@pytest.fixture
|
||||
def tag_manager(self):
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def test_file(self, temp_dir):
|
||||
test_file = temp_dir / "film.mkv"
|
||||
test_file.write_text("video content")
|
||||
return test_file
|
||||
|
||||
def test_file_csfd_url_initial(self, test_file, tag_manager):
|
||||
"""Test že csfd_url je None při vytvoření"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
assert file_obj.csfd_url is None
|
||||
|
||||
def test_file_set_csfd_url(self, test_file, tag_manager):
|
||||
"""Test nastavení CSFD URL"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/9423-pane-vy-jste-vdova/")
|
||||
assert file_obj.csfd_url == "https://www.csfd.cz/film/9423-pane-vy-jste-vdova/"
|
||||
|
||||
def test_file_set_csfd_url_persistence(self, test_file, tag_manager):
|
||||
"""Test že CSFD URL přežije reload"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
|
||||
file_obj2 = File(test_file, tag_manager)
|
||||
assert file_obj2.csfd_url == "https://www.csfd.cz/film/123/"
|
||||
|
||||
def test_file_set_csfd_url_none(self, test_file, tag_manager):
|
||||
"""Test smazání CSFD URL"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
file_obj.set_csfd_url(None)
|
||||
assert file_obj.csfd_url is None
|
||||
|
||||
def test_file_set_csfd_url_empty(self, test_file, tag_manager):
|
||||
"""Test nastavení prázdného řetězce jako CSFD URL"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
file_obj.set_csfd_url("")
|
||||
assert file_obj.csfd_url is None
|
||||
|
||||
def test_file_csfd_url_in_metadata(self, test_file, tag_manager):
|
||||
"""Test že CSFD URL je uložena v metadatech"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/999/")
|
||||
|
||||
import json
|
||||
with open(file_obj.metadata_filename, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
assert data["csfd_url"] == "https://www.csfd.cz/film/999/"
|
||||
|
||||
def test_apply_csfd_tags_no_url(self, test_file, tag_manager):
|
||||
"""Test apply_csfd_tags bez nastaveného URL"""
|
||||
file_obj = File(test_file, tag_manager)
|
||||
result = file_obj.apply_csfd_tags()
|
||||
|
||||
assert result["success"] is False
|
||||
assert "URL není nastavena" in result["error"]
|
||||
assert result["tags_added"] == []
|
||||
|
||||
@pytest.fixture
|
||||
def mock_csfd_movie(self):
|
||||
"""Mock CSFDMovie pro testování"""
|
||||
from unittest.mock import MagicMock
|
||||
movie = MagicMock()
|
||||
movie.title = "Test Film"
|
||||
movie.year = 2020
|
||||
movie.genres = ["Komedie", "Drama"]
|
||||
movie.country = "Česko"
|
||||
movie.rating = 85
|
||||
return movie
|
||||
|
||||
def test_apply_csfd_tags_success(self, test_file, tag_manager, mock_csfd_movie):
|
||||
"""Test úspěšného načtení tagů z CSFD"""
|
||||
from unittest.mock import patch
|
||||
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
|
||||
with patch("src.core.csfd.fetch_movie", return_value=mock_csfd_movie):
|
||||
result = file_obj.apply_csfd_tags()
|
||||
|
||||
assert result["success"] is True
|
||||
assert "Žánr/Komedie" in result["tags_added"]
|
||||
assert "Žánr/Drama" in result["tags_added"]
|
||||
assert "Rok/2020" in result["tags_added"]
|
||||
assert "Země/Česko" in result["tags_added"]
|
||||
|
||||
# Kontrola že tagy jsou opravdu přidány
|
||||
tag_paths = {tag.full_path for tag in file_obj.tags}
|
||||
assert "Žánr/Komedie" in tag_paths
|
||||
assert "Žánr/Drama" in tag_paths
|
||||
assert "Rok/2020" in tag_paths
|
||||
assert "Země/Česko" in tag_paths
|
||||
|
||||
def test_apply_csfd_tags_genres_only(self, test_file, tag_manager, mock_csfd_movie):
|
||||
"""Test načtení pouze žánrů"""
|
||||
from unittest.mock import patch
|
||||
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
|
||||
with patch("src.core.csfd.fetch_movie", return_value=mock_csfd_movie):
|
||||
result = file_obj.apply_csfd_tags(add_genres=True, add_year=False, add_country=False)
|
||||
|
||||
assert result["success"] is True
|
||||
assert "Žánr/Komedie" in result["tags_added"]
|
||||
assert "Rok/2020" not in result["tags_added"]
|
||||
assert "Země/Česko" not in result["tags_added"]
|
||||
|
||||
def test_apply_csfd_tags_no_duplicate(self, test_file, tag_manager, mock_csfd_movie):
|
||||
"""Test že duplicitní tagy nejsou přidány"""
|
||||
from unittest.mock import patch
|
||||
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
|
||||
# Přidáme tag ručně
|
||||
file_obj.add_tag("Žánr/Komedie")
|
||||
|
||||
with patch("src.core.csfd.fetch_movie", return_value=mock_csfd_movie):
|
||||
result = file_obj.apply_csfd_tags()
|
||||
|
||||
# Komedie by neměla být v tags_added, protože už existuje
|
||||
assert "Žánr/Komedie" not in result["tags_added"]
|
||||
assert "Žánr/Drama" in result["tags_added"]
|
||||
|
||||
def test_apply_csfd_tags_network_error(self, test_file, tag_manager):
|
||||
"""Test chyby při načítání z CSFD"""
|
||||
from unittest.mock import patch
|
||||
|
||||
file_obj = File(test_file, tag_manager)
|
||||
file_obj.set_csfd_url("https://www.csfd.cz/film/123/")
|
||||
|
||||
with patch("src.core.csfd.fetch_movie", side_effect=Exception("Network error")):
|
||||
result = file_obj.apply_csfd_tags()
|
||||
|
||||
assert result["success"] is False
|
||||
assert "error" in result
|
||||
assert result["tags_added"] == []
|
||||
|
||||
@@ -556,3 +556,433 @@ class TestFileManagerEdgeCases:
|
||||
filenames = {f.filename for f in file_manager.filelist}
|
||||
assert "soubor s mezerami.txt" in filenames
|
||||
assert "čeština.txt" in filenames
|
||||
|
||||
|
||||
class TestFileManagerCloseFolder:
|
||||
"""Testy pro close_folder metodu"""
|
||||
|
||||
@pytest.fixture
|
||||
def tag_manager(self):
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def temp_global_config(self, tmp_path, monkeypatch):
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
config_path = config_dir / "test_config.json"
|
||||
import src.core.config as config_module
|
||||
monkeypatch.setattr(config_module, 'GLOBAL_CONFIG_FILE', config_path)
|
||||
return config_path
|
||||
|
||||
@pytest.fixture
|
||||
def file_manager(self, tag_manager, temp_global_config):
|
||||
return FileManager(tag_manager)
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir(self, tmp_path):
|
||||
data_dir = tmp_path / "data"
|
||||
data_dir.mkdir()
|
||||
(data_dir / "file1.txt").write_text("content1")
|
||||
(data_dir / "file2.txt").write_text("content2")
|
||||
return data_dir
|
||||
|
||||
def test_close_folder_clears_state(self, file_manager, temp_dir):
|
||||
"""Test že close_folder vymaže stav"""
|
||||
file_manager.append(temp_dir)
|
||||
assert len(file_manager.filelist) == 2
|
||||
assert file_manager.current_folder == temp_dir
|
||||
|
||||
file_manager.close_folder()
|
||||
|
||||
assert len(file_manager.filelist) == 0
|
||||
assert len(file_manager.folders) == 0
|
||||
assert file_manager.current_folder is None
|
||||
assert len(file_manager.folder_configs) == 0
|
||||
|
||||
def test_close_folder_saves_metadata(self, file_manager, temp_dir):
|
||||
"""Test že close_folder uloží metadata"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Find file1.txt specifically
|
||||
file = next(f for f in file_manager.filelist if f.filename == "file1.txt")
|
||||
file.add_tag("Test/CloseTag")
|
||||
|
||||
file_manager.close_folder()
|
||||
|
||||
# Reload file and check tag persists
|
||||
from src.core.file import File
|
||||
reloaded = File(temp_dir / "file1.txt", file_manager.tagmanager)
|
||||
tag_paths = {t.full_path for t in reloaded.tags}
|
||||
assert "Test/CloseTag" in tag_paths
|
||||
|
||||
def test_close_folder_callback(self, file_manager, temp_dir):
|
||||
"""Test že close_folder volá callback"""
|
||||
file_manager.append(temp_dir)
|
||||
callback_calls = []
|
||||
|
||||
def callback(filelist):
|
||||
callback_calls.append(len(filelist))
|
||||
|
||||
file_manager.on_files_changed = callback
|
||||
file_manager.close_folder()
|
||||
|
||||
assert len(callback_calls) == 1
|
||||
assert callback_calls[0] == 0 # Empty list after close
|
||||
|
||||
def test_close_folder_no_folder_open(self, file_manager):
|
||||
"""Test close_folder bez otevřené složky"""
|
||||
# Should not raise
|
||||
file_manager.close_folder()
|
||||
assert file_manager.current_folder is None
|
||||
|
||||
def test_close_folder_preserves_global_config(self, file_manager, temp_dir):
|
||||
"""Test že close_folder zachová global config"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.global_config["test_key"] = "test_value"
|
||||
|
||||
file_manager.close_folder()
|
||||
|
||||
assert file_manager.global_config.get("test_key") == "test_value"
|
||||
|
||||
|
||||
class TestFileManagerRenameTag:
|
||||
"""Testy pro přejmenování tagů v souborech"""
|
||||
|
||||
@pytest.fixture
|
||||
def tag_manager(self):
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def temp_global_config(self, tmp_path, monkeypatch):
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
config_path = config_dir / "test_config.json"
|
||||
import src.core.config as config_module
|
||||
monkeypatch.setattr(config_module, 'GLOBAL_CONFIG_FILE', config_path)
|
||||
return config_path
|
||||
|
||||
@pytest.fixture
|
||||
def file_manager(self, tag_manager, temp_global_config):
|
||||
return FileManager(tag_manager)
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir(self, tmp_path):
|
||||
data_dir = tmp_path / "data"
|
||||
data_dir.mkdir()
|
||||
(data_dir / "file1.txt").write_text("content1")
|
||||
(data_dir / "file2.txt").write_text("content2")
|
||||
(data_dir / "file3.txt").write_text("content3")
|
||||
return data_dir
|
||||
|
||||
def test_rename_tag_in_files_success(self, file_manager, temp_dir):
|
||||
"""Test úspěšného přejmenování tagu v souborech"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Přidat tag dvěma souborům
|
||||
files_to_tag = file_manager.filelist[:2]
|
||||
file_manager.assign_tag_to_file_objects(files_to_tag, "Video/HD")
|
||||
|
||||
# Přejmenovat tag
|
||||
updated_count = file_manager.rename_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
assert updated_count == 2
|
||||
|
||||
# Zkontrolovat že tagy jsou přejmenovány
|
||||
for f in files_to_tag:
|
||||
tag_paths = {t.full_path for t in f.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_rename_tag_in_files_persistence(self, file_manager, temp_dir):
|
||||
"""Test že přejmenovaný tag přežije reload"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
|
||||
file_manager.rename_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
# Reload soubor
|
||||
from src.core.file import File
|
||||
reloaded = File(file.file_path, file_manager.tagmanager)
|
||||
tag_paths = {t.full_path for t in reloaded.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_rename_tag_in_files_no_match(self, file_manager, temp_dir):
|
||||
"""Test přejmenování tagu který žádný soubor nemá"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.assign_tag_to_file_objects(file_manager.filelist[:1], "Video/HD")
|
||||
|
||||
updated_count = file_manager.rename_tag_in_files("Video", "4K", "UHD")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_rename_tag_in_files_nonexistent_category(self, file_manager, temp_dir):
|
||||
"""Test přejmenování tagu v neexistující kategorii"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
updated_count = file_manager.rename_tag_in_files("NonExistent", "Tag", "NewTag")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_rename_tag_in_files_callback(self, file_manager, temp_dir):
|
||||
"""Test že přejmenování tagu volá callback"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.assign_tag_to_file_objects(file_manager.filelist[:1], "Video/HD")
|
||||
|
||||
callback_calls = []
|
||||
def callback(filelist):
|
||||
callback_calls.append(len(filelist))
|
||||
|
||||
file_manager.on_files_changed = callback
|
||||
file_manager.rename_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
assert len(callback_calls) == 1
|
||||
|
||||
def test_rename_tag_preserves_other_tags(self, file_manager, temp_dir):
|
||||
"""Test že přejmenování jednoho tagu neovlivní ostatní tagy souboru"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Audio/Stereo")
|
||||
file_manager.assign_tag_to_file_objects([file], "Quality/High")
|
||||
|
||||
file_manager.rename_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
tag_paths = {t.full_path for t in file.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Audio/Stereo" in tag_paths
|
||||
assert "Quality/High" in tag_paths
|
||||
|
||||
def test_rename_category_in_files_success(self, file_manager, temp_dir):
|
||||
"""Test úspěšného přejmenování kategorie v souborech"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Přidat tagy ze stejné kategorie
|
||||
file_manager.assign_tag_to_file_objects(file_manager.filelist[:2], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[0]], "Video/4K")
|
||||
|
||||
# Přejmenovat kategorii
|
||||
updated_count = file_manager.rename_category_in_files("Video", "Rozlišení")
|
||||
|
||||
assert updated_count == 2
|
||||
|
||||
# Zkontrolovat že tagy mají novou kategorii
|
||||
file1 = file_manager.filelist[0]
|
||||
tag_paths = {t.full_path for t in file1.tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Rozlišení/4K" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
assert "Video/4K" not in tag_paths
|
||||
|
||||
def test_rename_category_in_files_persistence(self, file_manager, temp_dir):
|
||||
"""Test že přejmenovaná kategorie přežije reload"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
|
||||
file_manager.rename_category_in_files("Video", "Rozlišení")
|
||||
|
||||
# Reload soubor
|
||||
from src.core.file import File
|
||||
reloaded = File(file.file_path, file_manager.tagmanager)
|
||||
tag_paths = {t.full_path for t in reloaded.tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_rename_category_in_files_no_match(self, file_manager, temp_dir):
|
||||
"""Test přejmenování kategorie kterou žádný soubor nemá"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.assign_tag_to_file_objects(file_manager.filelist[:1], "Video/HD")
|
||||
|
||||
updated_count = file_manager.rename_category_in_files("Audio", "Sound")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_rename_category_in_files_nonexistent(self, file_manager, temp_dir):
|
||||
"""Test přejmenování neexistující kategorie"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
updated_count = file_manager.rename_category_in_files("NonExistent", "NewName")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_rename_category_preserves_other_categories(self, file_manager, temp_dir):
|
||||
"""Test že přejmenování kategorie neovlivní jiné kategorie"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Audio/Stereo")
|
||||
|
||||
file_manager.rename_category_in_files("Video", "Rozlišení")
|
||||
|
||||
tag_paths = {t.full_path for t in file.tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Audio/Stereo" in tag_paths
|
||||
|
||||
|
||||
class TestFileManagerMergeTag:
|
||||
"""Testy pro slučování tagů v souborech"""
|
||||
|
||||
@pytest.fixture
|
||||
def tag_manager(self):
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def temp_global_config(self, tmp_path, monkeypatch):
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
config_path = config_dir / "test_config.json"
|
||||
import src.core.config as config_module
|
||||
monkeypatch.setattr(config_module, 'GLOBAL_CONFIG_FILE', config_path)
|
||||
return config_path
|
||||
|
||||
@pytest.fixture
|
||||
def file_manager(self, tag_manager, temp_global_config):
|
||||
return FileManager(tag_manager)
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir(self, tmp_path):
|
||||
data_dir = tmp_path / "data"
|
||||
data_dir.mkdir()
|
||||
(data_dir / "file1.txt").write_text("content1")
|
||||
(data_dir / "file2.txt").write_text("content2")
|
||||
(data_dir / "file3.txt").write_text("content3")
|
||||
return data_dir
|
||||
|
||||
def test_merge_tag_in_files_success(self, file_manager, temp_dir):
|
||||
"""Test úspěšného sloučení tagů v souborech"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Přidat oba tagy - jeden soubor má HD, druhý má FullHD
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[0]], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[1]], "Video/FullHD")
|
||||
|
||||
# Sloučit HD do FullHD
|
||||
updated_count = file_manager.merge_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
assert updated_count == 1
|
||||
|
||||
# Soubor 0 by měl mít FullHD místo HD
|
||||
tag_paths = {t.full_path for t in file_manager.filelist[0].tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_merge_tag_in_files_file_has_both(self, file_manager, temp_dir):
|
||||
"""Test sloučení když soubor má oba tagy"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Soubor má oba tagy
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/FullHD")
|
||||
|
||||
# Sloučit HD do FullHD - HD by měl být odstraněn
|
||||
updated_count = file_manager.merge_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
assert updated_count == 1
|
||||
|
||||
tag_paths = {t.full_path for t in file.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
# FullHD by měl být jen jednou
|
||||
fullhd_count = sum(1 for t in file.tags if t.full_path == "Video/FullHD")
|
||||
assert fullhd_count == 1
|
||||
|
||||
def test_merge_tag_in_files_persistence(self, file_manager, temp_dir):
|
||||
"""Test že sloučený tag přežije reload"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[1]], "Video/FullHD")
|
||||
|
||||
file_manager.merge_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
# Reload soubor
|
||||
from src.core.file import File
|
||||
reloaded = File(file.file_path, file_manager.tagmanager)
|
||||
tag_paths = {t.full_path for t in reloaded.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_merge_tag_in_files_no_source(self, file_manager, temp_dir):
|
||||
"""Test sloučení když žádný soubor nemá zdrojový tag"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[0]], "Video/FullHD")
|
||||
|
||||
updated_count = file_manager.merge_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_merge_tag_preserves_other_tags(self, file_manager, temp_dir):
|
||||
"""Test že sloučení neovlivní ostatní tagy"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/FullHD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Audio/Stereo")
|
||||
|
||||
file_manager.merge_tag_in_files("Video", "HD", "FullHD")
|
||||
|
||||
tag_paths = {t.full_path for t in file.tags}
|
||||
assert "Video/FullHD" in tag_paths
|
||||
assert "Audio/Stereo" in tag_paths
|
||||
|
||||
def test_merge_category_in_files_success(self, file_manager, temp_dir):
|
||||
"""Test úspěšného sloučení kategorií v souborech"""
|
||||
file_manager.append(temp_dir)
|
||||
|
||||
# Přidat tagy z různých kategorií
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[0]], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[1]], "Rozlišení/4K")
|
||||
|
||||
# Sloučit Video do Rozlišení
|
||||
updated_count = file_manager.merge_category_in_files("Video", "Rozlišení")
|
||||
|
||||
assert updated_count == 1
|
||||
|
||||
# Soubor 0 by měl mít Rozlišení/HD místo Video/HD
|
||||
tag_paths = {t.full_path for t in file_manager.filelist[0].tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_merge_category_in_files_persistence(self, file_manager, temp_dir):
|
||||
"""Test že sloučená kategorie přežije reload"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[1]], "Rozlišení/4K")
|
||||
|
||||
file_manager.merge_category_in_files("Video", "Rozlišení")
|
||||
|
||||
# Reload soubor
|
||||
from src.core.file import File
|
||||
reloaded = File(file.file_path, file_manager.tagmanager)
|
||||
tag_paths = {t.full_path for t in reloaded.tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
def test_merge_category_no_source_files(self, file_manager, temp_dir):
|
||||
"""Test sloučení když žádný soubor nemá zdrojovou kategorii"""
|
||||
file_manager.append(temp_dir)
|
||||
file_manager.assign_tag_to_file_objects([file_manager.filelist[0]], "Rozlišení/4K")
|
||||
|
||||
updated_count = file_manager.merge_category_in_files("Video", "Rozlišení")
|
||||
|
||||
assert updated_count == 0
|
||||
|
||||
def test_merge_category_preserves_other_categories(self, file_manager, temp_dir):
|
||||
"""Test že sloučení kategorie neovlivní jiné kategorie"""
|
||||
file_manager.append(temp_dir)
|
||||
file = file_manager.filelist[0]
|
||||
file_manager.assign_tag_to_file_objects([file], "Video/HD")
|
||||
file_manager.assign_tag_to_file_objects([file], "Rozlišení/4K")
|
||||
file_manager.assign_tag_to_file_objects([file], "Audio/Stereo")
|
||||
|
||||
file_manager.merge_category_in_files("Video", "Rozlišení")
|
||||
|
||||
tag_paths = {t.full_path for t in file.tags}
|
||||
assert "Rozlišení/HD" in tag_paths
|
||||
assert "Rozlišení/4K" in tag_paths
|
||||
assert "Audio/Stereo" in tag_paths
|
||||
assert "Video/HD" not in tag_paths
|
||||
|
||||
@@ -2,7 +2,7 @@ import tempfile
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from src.core.media_utils import load_icon
|
||||
from src.ui.utils import load_icon
|
||||
from PIL import Image, ImageTk
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
@@ -104,3 +104,44 @@ class TestTag:
|
||||
assert tag.category == "Kategorie"
|
||||
assert tag.name == "Čeština"
|
||||
assert tag.full_path == "Kategorie/Čeština"
|
||||
|
||||
|
||||
class TestTagFromString:
|
||||
"""Testy pro Tag.from_string() class method"""
|
||||
|
||||
def test_from_string_with_category(self):
|
||||
"""Test parsování stringu s kategorií"""
|
||||
tag = Tag.from_string("Stav/Nové")
|
||||
assert tag.category == "Stav"
|
||||
assert tag.name == "Nové"
|
||||
|
||||
def test_from_string_without_category(self):
|
||||
"""Test parsování stringu bez kategorie - použije default"""
|
||||
tag = Tag.from_string("simple")
|
||||
assert tag.category == "default"
|
||||
assert tag.name == "simple"
|
||||
|
||||
def test_from_string_custom_default_category(self):
|
||||
"""Test parsování s vlastní default kategorií"""
|
||||
tag = Tag.from_string("simple", default_category="Custom")
|
||||
assert tag.category == "Custom"
|
||||
assert tag.name == "simple"
|
||||
|
||||
def test_from_string_multiple_slashes(self):
|
||||
"""Test parsování stringu s více lomítky"""
|
||||
tag = Tag.from_string("Kategorie/Název/s/lomítky")
|
||||
assert tag.category == "Kategorie"
|
||||
assert tag.name == "Název/s/lomítky"
|
||||
|
||||
def test_from_string_unicode(self):
|
||||
"""Test parsování unicode stringu"""
|
||||
tag = Tag.from_string("Žánr/Komedie")
|
||||
assert tag.category == "Žánr"
|
||||
assert tag.name == "Komedie"
|
||||
|
||||
def test_from_string_equality(self):
|
||||
"""Test že from_string vytváří ekvivalentní tag"""
|
||||
tag1 = Tag("Stav", "Nové")
|
||||
tag2 = Tag.from_string("Stav/Nové")
|
||||
assert tag1 == tag2
|
||||
assert hash(tag1) == hash(tag2)
|
||||
|
||||
@@ -325,3 +325,287 @@ class TestDefaultTags:
|
||||
tm.add_tag("Hodnocení", "Custom Rating")
|
||||
|
||||
assert len(tm.get_tags_in_category("Hodnocení")) == initial_count + 1
|
||||
|
||||
|
||||
class TestRenameTag:
|
||||
"""Testy pro přejmenování tagů a kategorií"""
|
||||
|
||||
@pytest.fixture
|
||||
def tag_manager(self):
|
||||
return TagManager()
|
||||
|
||||
@pytest.fixture
|
||||
def empty_tag_manager(self):
|
||||
tm = TagManager()
|
||||
for category in list(tm.tags_by_category.keys()):
|
||||
tm.remove_category(category)
|
||||
return tm
|
||||
|
||||
def test_rename_tag_success(self, empty_tag_manager):
|
||||
"""Test úspěšného přejmenování tagu"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
new_tag = tm.rename_tag("Video", "HD", "FullHD")
|
||||
|
||||
assert new_tag is not None
|
||||
assert new_tag.name == "FullHD"
|
||||
assert new_tag.category == "Video"
|
||||
# Old tag should not exist
|
||||
tags = tm.get_tags_in_category("Video")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "HD" not in tag_names
|
||||
assert "FullHD" in tag_names
|
||||
|
||||
def test_rename_tag_nonexistent_category(self, empty_tag_manager):
|
||||
"""Test přejmenování tagu v neexistující kategorii"""
|
||||
result = empty_tag_manager.rename_tag("Nonexistent", "Tag", "NewTag")
|
||||
assert result is None
|
||||
|
||||
def test_rename_tag_nonexistent_tag(self, empty_tag_manager):
|
||||
"""Test přejmenování neexistujícího tagu"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
result = tm.rename_tag("Video", "Nonexistent", "NewTag")
|
||||
assert result is None
|
||||
|
||||
def test_rename_tag_to_existing_name(self, empty_tag_manager):
|
||||
"""Test přejmenování tagu na existující název"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
|
||||
result = tm.rename_tag("Video", "HD", "4K")
|
||||
assert result is None
|
||||
# Original tags should still exist
|
||||
tags = tm.get_tags_in_category("Video")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "HD" in tag_names
|
||||
assert "4K" in tag_names
|
||||
|
||||
def test_rename_tag_same_name(self, empty_tag_manager):
|
||||
"""Test přejmenování tagu na stejný název (no-op)"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
new_tag = tm.rename_tag("Video", "HD", "HD")
|
||||
# Should succeed but effectively be a no-op
|
||||
assert new_tag is not None
|
||||
assert new_tag.name == "HD"
|
||||
|
||||
def test_rename_category_success(self, empty_tag_manager):
|
||||
"""Test úspěšného přejmenování kategorie"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
|
||||
result = tm.rename_category("Video", "Rozlišení")
|
||||
|
||||
assert result is True
|
||||
assert "Video" not in tm.get_categories()
|
||||
assert "Rozlišení" in tm.get_categories()
|
||||
# Tags should be moved to new category
|
||||
tags = tm.get_tags_in_category("Rozlišení")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "HD" in tag_names
|
||||
assert "4K" in tag_names
|
||||
|
||||
def test_rename_category_nonexistent(self, empty_tag_manager):
|
||||
"""Test přejmenování neexistující kategorie"""
|
||||
result = empty_tag_manager.rename_category("Nonexistent", "NewName")
|
||||
assert result is False
|
||||
|
||||
def test_rename_category_to_existing_name(self, empty_tag_manager):
|
||||
"""Test přejmenování kategorie na existující název"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Audio", "MP3")
|
||||
|
||||
result = tm.rename_category("Video", "Audio")
|
||||
assert result is False
|
||||
# Original categories should still exist
|
||||
assert "Video" in tm.get_categories()
|
||||
assert "Audio" in tm.get_categories()
|
||||
|
||||
def test_rename_category_same_name(self, empty_tag_manager):
|
||||
"""Test přejmenování kategorie na stejný název"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
result = tm.rename_category("Video", "Video")
|
||||
# Should succeed but effectively be a no-op
|
||||
assert result is True
|
||||
assert "Video" in tm.get_categories()
|
||||
|
||||
def test_rename_tag_preserves_other_tags(self, empty_tag_manager):
|
||||
"""Test že přejmenování jednoho tagu neovlivní ostatní"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
tm.add_tag("Video", "SD")
|
||||
|
||||
tm.rename_tag("Video", "HD", "FullHD")
|
||||
|
||||
tags = tm.get_tags_in_category("Video")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert len(tag_names) == 3
|
||||
assert "FullHD" in tag_names
|
||||
assert "4K" in tag_names
|
||||
assert "SD" in tag_names
|
||||
assert "HD" not in tag_names
|
||||
|
||||
def test_rename_category_preserves_tags(self, empty_tag_manager):
|
||||
"""Test že přejmenování kategorie zachová všechny tagy"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
tm.add_tag("Video", "SD")
|
||||
|
||||
tm.rename_category("Video", "Rozlišení")
|
||||
|
||||
tags = tm.get_tags_in_category("Rozlišení")
|
||||
assert len(tags) == 3
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "HD" in tag_names
|
||||
assert "4K" in tag_names
|
||||
assert "SD" in tag_names
|
||||
|
||||
|
||||
class TestMergeTag:
|
||||
"""Testy pro slučování tagů a kategorií"""
|
||||
|
||||
@pytest.fixture
|
||||
def empty_tag_manager(self):
|
||||
tm = TagManager()
|
||||
for category in list(tm.tags_by_category.keys()):
|
||||
tm.remove_category(category)
|
||||
return tm
|
||||
|
||||
def test_merge_tag_success(self, empty_tag_manager):
|
||||
"""Test úspěšného sloučení tagů"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "FullHD")
|
||||
|
||||
result = tm.merge_tag("Video", "HD", "FullHD")
|
||||
|
||||
assert result is not None
|
||||
assert result.name == "FullHD"
|
||||
tags = tm.get_tags_in_category("Video")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "FullHD" in tag_names
|
||||
assert "HD" not in tag_names
|
||||
assert len(tag_names) == 1
|
||||
|
||||
def test_merge_tag_nonexistent_source(self, empty_tag_manager):
|
||||
"""Test sloučení neexistujícího zdrojového tagu"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "FullHD")
|
||||
|
||||
result = tm.merge_tag("Video", "HD", "FullHD")
|
||||
assert result is None
|
||||
|
||||
def test_merge_tag_nonexistent_target(self, empty_tag_manager):
|
||||
"""Test sloučení do neexistujícího cílového tagu"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
result = tm.merge_tag("Video", "HD", "FullHD")
|
||||
assert result is None
|
||||
|
||||
def test_merge_tag_nonexistent_category(self, empty_tag_manager):
|
||||
"""Test sloučení v neexistující kategorii"""
|
||||
result = empty_tag_manager.merge_tag("Nonexistent", "HD", "FullHD")
|
||||
assert result is None
|
||||
|
||||
def test_merge_tag_preserves_other_tags(self, empty_tag_manager):
|
||||
"""Test že sloučení jednoho tagu neovlivní ostatní"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
tm.add_tag("Video", "SD")
|
||||
|
||||
tm.merge_tag("Video", "HD", "4K")
|
||||
|
||||
tags = tm.get_tags_in_category("Video")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert len(tag_names) == 2
|
||||
assert "4K" in tag_names
|
||||
assert "SD" in tag_names
|
||||
assert "HD" not in tag_names
|
||||
|
||||
def test_merge_category_success(self, empty_tag_manager):
|
||||
"""Test úspěšného sloučení kategorií"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
tm.add_tag("Rozlišení", "SD")
|
||||
|
||||
result = tm.merge_category("Video", "Rozlišení")
|
||||
|
||||
assert result is True
|
||||
assert "Video" not in tm.get_categories()
|
||||
assert "Rozlišení" in tm.get_categories()
|
||||
tags = tm.get_tags_in_category("Rozlišení")
|
||||
tag_names = [t.name for t in tags]
|
||||
assert "HD" in tag_names
|
||||
assert "4K" in tag_names
|
||||
assert "SD" in tag_names
|
||||
|
||||
def test_merge_category_nonexistent_source(self, empty_tag_manager):
|
||||
"""Test sloučení neexistující zdrojové kategorie"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Rozlišení", "HD")
|
||||
|
||||
result = tm.merge_category("Video", "Rozlišení")
|
||||
assert result is False
|
||||
|
||||
def test_merge_category_nonexistent_target(self, empty_tag_manager):
|
||||
"""Test sloučení do neexistující cílové kategorie"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
result = tm.merge_category("Video", "Rozlišení")
|
||||
assert result is False
|
||||
|
||||
def test_merge_category_same_category(self, empty_tag_manager):
|
||||
"""Test sloučení kategorie se sebou samou"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
result = tm.merge_category("Video", "Video")
|
||||
assert result is True # No-op, should succeed
|
||||
|
||||
def test_merge_category_duplicate_tags(self, empty_tag_manager):
|
||||
"""Test sloučení kategorií s duplicitními tagy"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
tm.add_tag("Video", "4K")
|
||||
tm.add_tag("Rozlišení", "HD") # Same tag name in target
|
||||
|
||||
result = tm.merge_category("Video", "Rozlišení")
|
||||
|
||||
assert result is True
|
||||
tags = tm.get_tags_in_category("Rozlišení")
|
||||
tag_names = [t.name for t in tags]
|
||||
# HD should appear only once (set deduplication)
|
||||
assert tag_names.count("HD") == 1
|
||||
assert "4K" in tag_names
|
||||
|
||||
def test_tag_exists(self, empty_tag_manager):
|
||||
"""Test kontroly existence tagu"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
assert tm.tag_exists("Video", "HD") is True
|
||||
assert tm.tag_exists("Video", "4K") is False
|
||||
assert tm.tag_exists("Nonexistent", "HD") is False
|
||||
|
||||
def test_category_exists(self, empty_tag_manager):
|
||||
"""Test kontroly existence kategorie"""
|
||||
tm = empty_tag_manager
|
||||
tm.add_tag("Video", "HD")
|
||||
|
||||
assert tm.category_exists("Video") is True
|
||||
assert tm.category_exists("Nonexistent") is False
|
||||
|
||||
Reference in New Issue
Block a user