""" Tests for the Version Checker UI component (Story 7.26). """ import pytest from datetime import datetime from pathlib import Path import tempfile from PyQt6.QtWidgets import QApplication from src.ui.version_checker import ( UpdateSeverity, VersionInfo, CurrentVersion, VersionCheckResult, VersionCheckThread, CURRENT_VERSION, get_version_checker, ) @pytest.fixture def app(qtbot): """Create QApplication for tests.""" return QApplication.instance() or QApplication([]) @pytest.fixture def sample_version_info(): """Create sample version info.""" return VersionInfo( version="1.2.0", release_date=datetime.now(), download_url="https://example.com/download/v1.2.0", release_notes="New features and bug fixes", file_size=50000000, severity=UpdateSeverity.OPTIONAL ) @pytest.fixture def sample_current_version(): """Create sample current version.""" return CurrentVersion( version="1.0.0", build_date=datetime.now(), commit_hash="abc123", channel="stable" ) class TestUpdateSeverity: """Tests for UpdateSeverity enum.""" def test_severity_values(self): """Test UpdateSeverity enum values.""" assert UpdateSeverity.OPTIONAL.value == "optional" assert UpdateSeverity.RECOMMENDED.value == "recommended" assert UpdateSeverity.CRITICAL.value == "critical" class TestVersionInfo: """Tests for VersionInfo.""" def test_creation(self): """Test creating version info.""" info = VersionInfo( version="1.0.0", release_date=datetime.now(), download_url="https://example.com", release_notes="Release notes" ) assert info.version == "1.0.0" assert info.severity == UpdateSeverity.OPTIONAL def test_with_all_fields(self): """Test creating with all fields.""" info = VersionInfo( version="2.0.0", release_date=datetime.now(), download_url="https://example.com", release_notes="Notes", file_size=1000000, checksum="abc123", severity=UpdateSeverity.CRITICAL, minimum_compatible_version="1.5.0", breaking_changes=True ) assert info.file_size == 1000000 assert info.severity == UpdateSeverity.CRITICAL assert info.breaking_changes is True class TestCurrentVersion: """Tests for CurrentVersion.""" def test_creation(self): """Test creating current version.""" version = CurrentVersion( version="1.0.0", build_date=datetime.now(), commit_hash="abc123", channel="stable" ) assert version.version == "1.0.0" assert version.channel == "stable" def test_partial_fields(self): """Test creating with partial fields.""" version = CurrentVersion(version="1.0.0") assert version.version == "1.0.0" assert version.build_date is None class TestVersionCheckResult: """Tests for VersionCheckResult.""" def test_has_update_true(self, sample_current_version, sample_version_info): """Test has_update when update is available.""" result = VersionCheckResult(sample_current_version, sample_version_info) assert result.has_update is True def test_has_update_false(self, sample_current_version): """Test has_update when no update available.""" old_version = VersionInfo( version="0.9.0", release_date=datetime.now(), download_url="https://example.com", release_notes="Old version" ) result = VersionCheckResult(sample_current_version, old_version) assert result.has_update is False def test_has_update_no_latest(self, sample_current_version): """Test has_update when latest is None.""" result = VersionCheckResult(sample_current_version, None) assert result.has_update is False def test_is_critical(self, sample_current_version): """Test is_critical property.""" critical_version = VersionInfo( version="2.0.0", release_date=datetime.now(), download_url="https://example.com", release_notes="Critical update", severity=UpdateSeverity.CRITICAL ) result = VersionCheckResult(sample_current_version, critical_version) assert result.is_critical is True def test_version_comparison(self, sample_current_version): """Test version comparison logic.""" result = VersionCheckResult(sample_current_version) # Test greater than assert result._compare_versions("1.2.0", "1.0.0") > 0 assert result._compare_versions("2.0.0", "1.9.9") > 0 # Test less than assert result._compare_versions("1.0.0", "1.2.0") < 0 assert result._compare_versions("1.0.0", "2.0.0") < 0 # Test equal assert result._compare_versions("1.0.0", "1.0.0") == 0 # Test with different lengths assert result._compare_versions("1.0", "1.0.0") == 0 assert result._compare_versions("1.0.0", "1.0") == 0 class TestVersionCheckThread: """Tests for VersionCheckThread.""" def test_initialization(self, sample_current_version): """Test thread initialization.""" thread = VersionCheckThread( sample_current_version, "https://api.example.com/version" ) assert thread._current == sample_current_version assert thread._check_url == "https://api.example.com" def test_with_custom_timeout(self, sample_current_version): """Test with custom timeout.""" thread = VersionCheckThread( sample_current_version, "https://api.example.com/version", timeout=30.0 ) assert thread._timeout == 30.0 class TestVersionChecker: """Tests for VersionChecker.""" def test_initialization(self, sample_current_version): """Test checker initialization.""" from src.ui.version_checker import VersionChecker checker = VersionChecker(sample_current_version) assert checker._current == sample_current_version assert checker._auto_check is True def test_set_check_url(self, sample_current_version): """Test setting check URL.""" from src.ui.version_checker import VersionChecker checker = VersionChecker(sample_current_version) checker.set_check_url("https://custom.api.com/version") assert checker._check_url == "https://custom.api.com/version" def test_set_auto_check(self, sample_current_version): """Test setting auto check.""" from src.ui.version_checker import VersionChecker checker = VersionChecker(sample_current_version) checker.set_auto_check(False) assert checker._auto_check is False def test_skip_version(self, sample_current_version): """Test skipping a version.""" from src.ui.version_checker import VersionChecker checker = VersionChecker(sample_current_version) checker.skip_version("1.2.0") # Version should be in settings from PyQt6.QtCore import QSettings settings = QSettings("BMAD", "NovelTranslator") skipped = settings.value("update/skip_version", "") assert skipped == "1.2.0" def test_clear_skipped_version(self, sample_current_version): """Test clearing skipped version.""" from src.ui.version_checker import VersionChecker from PyQt6.QtCore import QSettings checker = VersionChecker(sample_current_version) # First skip a version checker.skip_version("1.2.0") # Then clear it checker.clear_skipped_version() settings = QSettings("BMAD", "NovelTranslator") skipped = settings.value("update/skip_version", "") assert skipped == "" class TestCurrentVersionModule: """Tests for the module-level CURRENT_VERSION.""" def test_current_version_exists(self): """Test that CURRENT_VERSION is defined.""" assert CURRENT_VERSION is not None assert isinstance(CURRENT_VERSION, CurrentVersion) def test_current_version_format(self): """Test that version string is valid.""" # Should be valid semver parts = CURRENT_VERSION.version.split(".") assert len(parts) >= 2 class TestGetVersionChecker: """Tests for get_version_checker function.""" def test_returns_instance(self): """Test that get_version_checker returns an instance.""" from src.ui.version_checker import VersionChecker checker = get_version_checker() assert isinstance(checker, VersionChecker) def test_singleton_behavior(self): """Test that repeated calls return new instances (default).""" checker1 = get_version_checker() checker2 = get_version_checker() # Each call creates a new instance with CURRENT_VERSION assert isinstance(checker1, VersionChecker) assert isinstance(checker2, VersionChecker)