EpisodeMatcher/tests/test_tvdb_client.py
Jarian f1aa2c4cda test: add test suite with 90% coverage
- 176 unit tests covering ConfigManager, FileClassifier, EpisodeRenamer,
  TVDBClient, MockTVDBClient, TVDBCache, and episode_matcher CLI
- Tests organized in tests/ with conftest.py shared fixtures
- pytest-cov configured with 90% coverage threshold in pyproject.toml
- CI workflow updated with COVERAGE_CORE=sysmon for Python 3.13 compat
- .gitignore updated for coverage artifacts and venv
2026-07-06 14:31:22 +00:00

225 lines
7.9 KiB
Python

"""Tests for src/TVDBProvider/tvdb_client.py."""
import sys
from pathlib import Path
from unittest.mock import patch, MagicMock, PropertyMock
sys.path.insert(0, str(Path(__file__).parent.parent))
import pytest
from src.TVDBProvider.tvdb_client import TVDBClient, MockTVDBClient
class TestTVDBClientInit:
def test_init(self):
client = TVDBClient()
assert client.base_url == "https://api4.thetvdb.com/v4"
assert client.token is None
assert "Content-Type" in client.headers
def test_init_has_cache(self):
client = TVDBClient()
assert client.cache is not None
class TestTVDBClientAuthenticate:
@patch("src.TVDBProvider.tvdb_client.requests.post")
def test_auth_success(self, mock_post):
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {"data": {"token": "abc123"}}
client = TVDBClient()
assert client.authenticate("my_api_key") is True
assert client.token == "abc123"
assert "Bearer abc123" in client.headers["Authorization"]
@patch("src.TVDBProvider.tvdb_client.requests.post")
def test_auth_failure_status(self, mock_post):
mock_post.return_value.status_code = 401
client = TVDBClient()
assert client.authenticate("bad_key") is False
assert client.token is None
@patch("src.TVDBProvider.tvdb_client.requests.post")
def test_auth_exception(self, mock_post):
mock_post.side_effect = Exception("network error")
client = TVDBClient()
assert client.authenticate("key") is False
class TestTVDBClientSearch:
@patch("src.TVDBProvider.tvdb_client.requests.get")
def test_search_success(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {
"data": [{"tvdb_id": 123, "name": "Test Show"}]
}
client = TVDBClient()
client.token = "tok"
result = client.search_series("Test Show")
assert result["tvdb_id"] == 123
@patch("src.TVDBProvider.tvdb_client.requests.get")
def test_search_no_results(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"data": []}
client = TVDBClient()
client.token = "tok"
result = client.search_series("NoMatch")
assert result is None
def test_search_not_authenticated(self):
client = TVDBClient()
result = client.search_series("Show")
assert result is None
@patch("src.TVDBProvider.tvdb_client.requests.get")
def test_search_error(self, mock_get):
mock_get.side_effect = Exception("timeout")
client = TVDBClient()
client.token = "tok"
result = client.search_series("Show")
assert result is None
class TestTVDBClientSeasonEpisodes:
@patch("src.TVDBProvider.tvdb_client.requests.get")
def test_get_season_episodes(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {
"data": {"episodes": [{"number": 1, "name": "E1"}]}
}
client = TVDBClient()
client.token = "tok"
result = client.get_season_episodes(123, 1)
assert len(result) == 1
def test_episodes_not_authenticated(self):
client = TVDBClient()
result = client.get_season_episodes(123, 1)
assert result is None
class TestTVDBClientSeriesInfo:
@patch.object(TVDBClient, "search_series")
def test_get_series_info(self, mock_search):
mock_search.return_value = {"tvdb_id": 123, "name": "Test", "slug": "test", "year": 2020}
client = TVDBClient()
client.token = "tok"
result = client.get_series_info("Test")
assert result["id"] == 123
assert result["name"] == "Test"
@patch.object(TVDBClient, "search_series")
def test_get_series_info_no_match(self, mock_search):
mock_search.return_value = None
client = TVDBClient()
client.token = "tok"
result = client.get_series_info("NoMatch")
assert result is None
class TestTVDBClientEpisodeDurations:
def test_get_episode_durations(self, tmp_path):
with patch("src.TVDBProvider.tvdb_client.TVDBClient.__init__") as mock_init:
mock_init.return_value = None
client = TVDBClient()
client.base_url = "https://api4.thetvdb.com/v4"
client.token = "tok"
client.headers = {"Content-Type": "application/json"}
mock_cache = MagicMock()
mock_cache.get_cached_episodes.return_value = None
client.cache = mock_cache
client.get_series_info = MagicMock(return_value={"id": 123})
client.get_season_episodes = MagicMock(return_value=[
{"number": 1, "name": "E1", "runtime": 45, "aired": "2020-01-01"},
{"number": 2, "name": "E2", "runtime": 50, "aired": "2020-01-08"},
])
result = client.get_episode_durations("Test", 1)
assert len(result) == 2
assert result[0]["episode_number"] == 1
assert result[0]["runtime"] == 45
mock_cache.cache_episodes.assert_called_once()
def test_get_episode_durations_cached(self, tmp_path):
with patch("src.TVDBProvider.tvdb_client.TVDBClient.__init__") as mock_init:
mock_init.return_value = None
client = TVDBClient()
client.token = "tok"
mock_cache = MagicMock()
mock_cache.get_cached_episodes.return_value = [
{"episode_number": 1, "runtime": 45}
]
client.cache = mock_cache
result = client.get_episode_durations("Test", 1)
assert len(result) == 1
def test_get_episode_durations_no_series(self, tmp_path):
with patch("src.TVDBProvider.tvdb_client.TVDBClient.__init__") as mock_init:
mock_init.return_value = None
client = TVDBClient()
client.token = "tok"
mock_cache = MagicMock()
mock_cache.get_cached_episodes.return_value = None
client.cache = mock_cache
client.get_series_info = MagicMock(return_value=None)
result = client.get_episode_durations("NoShow", 1)
assert result is None
class TestMockTVDBClient:
def test_mock_auth(self):
client = MockTVDBClient()
assert client.authenticate() is True
assert client.authenticated is True
def test_mock_auth_with_key(self):
client = MockTVDBClient()
assert client.authenticate("some_key") is True
def test_mock_episodes_unauthenticated(self):
client = MockTVDBClient()
result = client.get_episode_durations("Show", 1)
assert result is None
def test_mock_episodes_drama(self):
client = MockTVDBClient()
client.authenticate()
result = client.get_episode_durations("Game of Thrones", 1)
assert len(result) == 10
def test_mock_episodes_sitcom(self):
client = MockTVDBClient()
client.authenticate()
result = client.get_episode_durations("Friends", 1)
assert len(result) == 24
def test_mock_episodes_default(self):
client = MockTVDBClient()
client.authenticate()
result = client.get_episode_durations("Unknown Show", 1)
assert len(result) == 22
def test_mock_episode_structure(self):
client = MockTVDBClient()
client.authenticate()
result = client.get_episode_durations("Test", 1)
ep = result[0]
assert ep["episode_number"] == 1
assert ep["runtime"] == 45
assert "name" in ep
assert "aired" in ep
def test_mock_cached_episodes(self):
client = MockTVDBClient()
client.authenticate()
r1 = client.get_episode_durations("Test", 1)
r2 = client.get_episode_durations("Test", 1)
assert r1 is not None and r2 is not None