EpisodeMatcher/tests/test_config.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

150 lines
5.6 KiB
Python

"""Tests for src/config.py - ConfigManager."""
import json
import os
from pathlib import Path
import sys
from unittest.mock import patch, MagicMock
sys.path.insert(0, str(Path(__file__).parent.parent))
import pytest
from src.config import ConfigManager
class TestConfigManagerInit:
def test_init_no_config_file(self, tmp_path, monkeypatch):
cfg = ConfigManager(str(tmp_path / "nonexistent.json"))
assert cfg.config["tvdb_api_key"] == ""
assert cfg.config["default_episode_duration"] == 45
assert cfg.config["classification_thresholds"]["size_threshold_ratio"] == 0.3
def test_init_existing_config(self, config_file):
cfg = ConfigManager(str(config_file))
assert cfg.config["tvdb_api_key"] == "test_api_key_12345"
assert cfg.config["default_episode_duration"] == 45
def test_init_invalid_json(self, tmp_path):
bad = tmp_path / "config.json"
bad.write_text("{invalid json")
cfg = ConfigManager(str(bad))
assert cfg.config["tvdb_api_key"] == ""
def test_init_missing_keys_merged(self, tmp_path):
partial = tmp_path / "config.json"
partial.write_text(json.dumps({"tvdb_api_key": "abc"}))
cfg = ConfigManager(str(partial))
assert cfg.config["default_episode_duration"] == 45
assert "classification_thresholds" in cfg.config
def test_init_io_error(self, tmp_path, monkeypatch):
cfg_path = tmp_path / "config.json"
cfg_path.write_text("test")
monkeypatch.setattr(Path, "exists", lambda self: True)
original_open = open
def mock_open(*args, **kwargs):
raise PermissionError("denied")
with patch("builtins.open", mock_open):
cfg = ConfigManager(str(cfg_path))
assert cfg.config["tvdb_api_key"] == ""
class TestConfigManagerApiKeys:
def test_get_api_key_from_config(self, config_file):
cfg = ConfigManager(str(config_file))
with patch.dict(os.environ, {}, clear=True):
key = cfg.get_tvdb_api_key()
assert key == "test_api_key_12345"
def test_get_api_key_from_env(self, config_file, monkeypatch):
cfg = ConfigManager(str(config_file))
monkeypatch.setenv("TVDB_API_KEY", "env_key_999")
key = cfg.get_tvdb_api_key()
assert key == "env_key_999"
def test_get_api_key_empty_env(self, config_file, monkeypatch):
cfg = ConfigManager(str(config_file))
monkeypatch.setenv("TVDB_API_KEY", " ")
key = cfg.get_tvdb_api_key()
assert key == "test_api_key_12345"
def test_get_api_key_placeholder_rejected(self, empty_config_file):
cfg = ConfigManager(str(empty_config_file))
cfg.config["tvdb_api_key"] = "YOUR_TVDB_API_KEY_HERE"
assert cfg.get_tvdb_api_key() is None
def test_get_api_key_env_placeholder_rejected(self, empty_config_file, monkeypatch):
cfg = ConfigManager(str(empty_config_file))
monkeypatch.setenv("TVDB_API_KEY", "YOUR_TVDB_API_KEY_HERE")
assert cfg.get_tvdb_api_key() is None
def test_env_key_takes_priority(self, config_file, monkeypatch):
cfg = ConfigManager(str(config_file))
monkeypatch.setenv("TVDB_API_KEY", "env_priority")
assert cfg.get_tvdb_api_key() == "env_priority"
class TestConfigManagerSettings:
def test_get_default_episode_duration(self, config_file):
cfg = ConfigManager(str(config_file))
assert cfg.get_default_episode_duration() == 45
def test_get_default_episode_duration_custom(self, tmp_path):
f = tmp_path / "config.json"
f.write_text(json.dumps({"default_episode_duration": 60}))
cfg = ConfigManager(str(f))
assert cfg.get_default_episode_duration() == 60
def test_get_classification_thresholds(self, config_file):
cfg = ConfigManager(str(config_file))
t = cfg.get_classification_thresholds()
assert t["size_threshold_ratio"] == 0.3
assert t["duration_threshold_ratio"] == 0.4
def test_get_classification_thresholds_defaults(self, tmp_path):
cfg = ConfigManager(str(tmp_path / "no.json"))
t = cfg.get_classification_thresholds()
assert t["size_threshold_ratio"] == 0.3
class TestConfigManagerUpdate:
def test_update_api_key_success(self, tmp_path):
cfg = ConfigManager(str(tmp_path / "config.json"))
result = cfg.update_api_key("new_key_abc")
assert result is True
assert cfg.config["tvdb_api_key"] == "new_key_abc"
stored = json.loads((tmp_path / "config.json").read_text())
assert stored["tvdb_api_key"] == "new_key_abc"
def test_update_api_key_io_error(self, tmp_path):
cfg = ConfigManager(str(tmp_path / "config.json"))
cfg.update_api_key("new_key")
cfg.config_file = Path("/nonexistent_dir/config.json")
result = cfg.update_api_key("another_key")
assert result is False
class TestConfigManagerStatus:
def test_print_config_status_no_key(self, tmp_path, capsys):
cfg = ConfigManager(str(tmp_path / "no.json"))
cfg.print_config_status()
out = capsys.readouterr().out
assert "Configuration file:" in out
assert "Not configured" in out
def test_print_config_status_with_key(self, config_file, capsys):
cfg = ConfigManager(str(config_file))
cfg.print_config_status()
out = capsys.readouterr().out
assert "configured" in out
def test_print_config_status_masked_short_key(self, tmp_path):
f = tmp_path / "config.json"
f.write_text(json.dumps({"tvdb_api_key": "ab"}))
cfg = ConfigManager(str(f))
cfg.print_config_status()