"""Tests for the Jellyfin client + pool verification. Network is monkeypatched.""" from types import SimpleNamespace import pytest from src.core import jellyfin from src.core.jellyfin import JellyfinError, JellyfinMovie @pytest.fixture(autouse=True) def _configured(monkeypatch): monkeypatch.setenv("JELLYFIN_URL", "http://jf:8096/") monkeypatch.setenv("JELLYFIN_API_KEY", "key") def _resp(status=200, payload=None): return SimpleNamespace(status_code=status, ok=(200 <= status < 300), json=lambda: (payload or {})) def test_base_url_strips_trailing_slash(): assert jellyfin.base_url() == "http://jf:8096" def test_api_available(monkeypatch): assert jellyfin.api_available() monkeypatch.delenv("JELLYFIN_API_KEY", raising=False) assert not jellyfin.api_available() def test_server_version(monkeypatch): monkeypatch.setattr(jellyfin.requests, "request", lambda *a, **k: _resp(200, {"Version": "10.9.11"})) assert jellyfin.server_version() == "10.9.11" def test_scan_all_posts_refresh(monkeypatch): seen = {} def fake(method, url, **k): seen["method"], seen["url"] = method, url return _resp(200, {}) monkeypatch.setattr(jellyfin.requests, "request", fake) jellyfin.scan_all() assert seen["method"] == "POST" and seen["url"].endswith("/Library/Refresh") def test_fetch_movies_maps_tmdb_id(monkeypatch): payload = {"Items": [ {"Id": "a1", "Name": "Matrix", "ProductionYear": 1999, "ProviderIds": {"Tmdb": "603"}, "Path": "/m/Matrix (1999) [tmdbid-603].mkv"}, {"Id": "a2", "Name": "Bez ID", "ProductionYear": 2000, "ProviderIds": {}}, ]} monkeypatch.setattr(jellyfin.requests, "request", lambda *a, **k: _resp(200, payload)) movies = jellyfin.fetch_movies() assert movies[0] == JellyfinMovie("a1", "Matrix", 1999, 603, "/m/Matrix (1999) [tmdbid-603].mkv") assert movies[1].tmdb_id is None def test_missing_config_raises(monkeypatch): monkeypatch.delenv("JELLYFIN_URL", raising=False) with pytest.raises(JellyfinError): jellyfin.server_version() def test_bad_auth_raises(monkeypatch): monkeypatch.setattr(jellyfin.requests, "request", lambda *a, **k: _resp(401)) with pytest.raises(JellyfinError): jellyfin.scan_all() class TestVerifyAgainstJellyfin: @pytest.fixture def file_manager(self, tmp_path, monkeypatch): import src.core.config as config_module monkeypatch.setattr(config_module, "GLOBAL_CONFIG_FILE", tmp_path / ".gtag") from src.core.tag_manager import TagManager from src.core.file_manager import FileManager fm = FileManager(TagManager()) fm.set_pool_dir(tmp_path / "pool") return fm def test_match_by_tmdb_id_and_report_gaps(self, file_manager, tmp_path): (tmp_path / "a.mkv").write_bytes(b"x") (tmp_path / "b.mkv").write_bytes(b"y") file_manager.import_movie(tmp_path / "a.mkv", "Matrix", year=1999, tmdb_id=603) file_manager.import_movie(tmp_path / "b.mkv", "Neznámý", year=2020) # no id jf = [ JellyfinMovie("j1", "Matrix", 1999, 603, None), # matches a by tmdb JellyfinMovie("j2", "Cizí film", 2011, 999, None), # jellyfin-only ] result = file_manager.verify_against_jellyfin(jf) assert result["matched"] == 1 assert result["pool_only"] == ["Neznámý (2020).mkv"] assert result["jellyfin_only"] == ["Cizí film"] assert result["pool_total"] == 2 and result["jellyfin_total"] == 2 def test_match_by_title_year_fallback(self, file_manager, tmp_path): (tmp_path / "a.mkv").write_bytes(b"x") file_manager.import_movie(tmp_path / "a.mkv", "Kolja", year=1996) # no id jf = [JellyfinMovie("j1", "Kolja", 1996, None, None)] result = file_manager.verify_against_jellyfin(jf) assert result["matched"] == 1 and result["pool_only"] == []