"""Unit tests for UpdateService. Tests the UpdateService business logic layer, ensuring: - Update checking - Update downloading - Update installation - Progress monitoring - Release notes retrieval - Error handling and error codes """ import pytest from unittest.mock import AsyncMock, Mock from app.backend.services.update_service import UpdateService from app.backend.managers.update_manager import UpdateProgress, UpdateStatus class TestUpdateServiceCheckForUpdates: """Test update checking operations.""" @pytest.mark.asyncio async def test_check_for_updates_available(self, mock_update_manager): """Test checking for updates when update is available.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": True, "current_version": "1.0.0", "latest_version": "1.1.0", "release_date": "2025-01-15", "changelog": "## New Features\n- Feature 1\n- Feature 2", "is_prerelease": False, "download_size": 10485760 # 10MB } service = UpdateService(mock_update_manager) # Act result = await service.check_for_updates() # Assert assert result.success is True assert result.data["update_available"] is True assert result.data["latest_version"] == "1.1.0" assert result.data["download_size"] == 10485760 @pytest.mark.asyncio async def test_check_for_updates_none_available(self, mock_update_manager): """Test checking for updates when system is up to date.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": False, "current_version": "1.1.0", "latest_version": "1.1.0", "message": "System is up to date" } service = UpdateService(mock_update_manager) # Act result = await service.check_for_updates() # Assert assert result.success is True assert result.data["update_available"] is False assert result.data["message"] == "System is up to date" @pytest.mark.asyncio async def test_check_for_updates_exception(self, mock_update_manager): """Test update check with exception.""" # Arrange mock_update_manager.check_for_updates.side_effect = Exception("Network error") service = UpdateService(mock_update_manager) # Act result = await service.check_for_updates() # Assert assert result.success is False assert result.error.code == "update_check_error" assert "Network error" in result.error.details class TestUpdateServiceDownload: """Test update download operations.""" @pytest.mark.asyncio async def test_download_update_success(self, mock_update_manager): """Test successful update download.""" # Arrange mock_update_manager.download_update.return_value = True service = UpdateService(mock_update_manager) # Act result = await service.download_update() # Assert assert result.success is True assert "successfully" in result.data["message"].lower() assert result.data["ready_to_install"] is True @pytest.mark.asyncio async def test_download_update_no_update_available(self, mock_update_manager): """Test download when no update is available.""" # Arrange mock_update_manager.download_update.side_effect = ValueError("No update available to download") service = UpdateService(mock_update_manager) # Act result = await service.download_update() # Assert assert result.success is False assert result.error.code == "no_update_available" @pytest.mark.asyncio async def test_download_update_failure(self, mock_update_manager): """Test download failure.""" # Arrange mock_update_manager.download_update.side_effect = Exception("Download failed") service = UpdateService(mock_update_manager) # Act result = await service.download_update() # Assert assert result.success is False assert result.error.code == "update_download_error" class TestUpdateServiceInstall: """Test update installation operations.""" @pytest.mark.asyncio async def test_install_update_success(self, mock_update_manager): """Test successful update installation.""" # Arrange mock_update_manager.install_update.return_value = True service = UpdateService(mock_update_manager) # Act result = await service.install_update() # Assert assert result.success is True assert "successfully" in result.data["message"].lower() assert result.data["requires_restart"] is True @pytest.mark.asyncio async def test_install_update_no_download(self, mock_update_manager): """Test install when no update has been downloaded.""" # Arrange mock_update_manager.install_update.side_effect = ValueError("No update downloaded") service = UpdateService(mock_update_manager) # Act result = await service.install_update() # Assert assert result.success is False assert result.error.code == "no_update_downloaded" @pytest.mark.asyncio async def test_install_update_failure(self, mock_update_manager): """Test installation failure.""" # Arrange mock_update_manager.install_update.side_effect = Exception("Installation failed") service = UpdateService(mock_update_manager) # Act result = await service.install_update() # Assert assert result.success is False assert result.error.code == "update_install_error" class TestUpdateServiceProgress: """Test progress monitoring.""" @pytest.mark.asyncio async def test_get_progress_idle(self, mock_update_manager): """Test getting progress when idle.""" # Arrange mock_update_manager.get_progress.return_value = UpdateProgress( status=UpdateStatus.IDLE, current_version="1.0.0", available_version=None, download_progress=0.0, error_message=None, last_check="2025-01-15T10:00:00" ) service = UpdateService(mock_update_manager) # Act result = await service.get_progress() # Assert assert result.success is True assert result.data["status"] == "idle" assert result.data["current_version"] == "1.0.0" assert result.data["download_progress"] == 0.0 @pytest.mark.asyncio async def test_get_progress_downloading(self, mock_update_manager): """Test getting progress during download.""" # Arrange mock_update_manager.get_progress.return_value = UpdateProgress( status=UpdateStatus.DOWNLOADING, current_version="1.0.0", available_version="1.1.0", download_progress=45.5, error_message=None, last_check="2025-01-15T10:00:00" ) service = UpdateService(mock_update_manager) # Act result = await service.get_progress() # Assert assert result.success is True assert result.data["status"] == "downloading" assert result.data["available_version"] == "1.1.0" assert result.data["download_progress"] == 45.5 @pytest.mark.asyncio async def test_get_progress_failed(self, mock_update_manager): """Test getting progress after failure.""" # Arrange mock_update_manager.get_progress.return_value = UpdateProgress( status=UpdateStatus.FAILED, current_version="1.0.0", available_version="1.1.0", download_progress=0.0, error_message="Network timeout", last_check="2025-01-15T10:00:00" ) service = UpdateService(mock_update_manager) # Act result = await service.get_progress() # Assert assert result.success is True assert result.data["status"] == "failed" assert result.data["error_message"] == "Network timeout" class TestUpdateServiceReleaseNotes: """Test release notes retrieval.""" @pytest.mark.asyncio async def test_get_release_notes_latest(self, mock_update_manager): """Test getting latest release notes.""" # Arrange mock_update_manager.get_release_notes.return_value = "## Version 1.1.0\n- New feature" service = UpdateService(mock_update_manager) # Act result = await service.get_release_notes(version=None) # Assert assert result.success is True assert result.data["version"] == "latest" assert "New feature" in result.data["release_notes"] @pytest.mark.asyncio async def test_get_release_notes_specific_version(self, mock_update_manager): """Test getting notes for specific version.""" # Arrange mock_update_manager.get_release_notes.return_value = "## Version 1.0.0\n- Initial release" service = UpdateService(mock_update_manager) # Act result = await service.get_release_notes(version="1.0.0") # Assert assert result.success is True assert result.data["version"] == "1.0.0" assert "Initial release" in result.data["release_notes"] mock_update_manager.get_release_notes.assert_called_once_with("1.0.0") class TestUpdateServiceCombinedOperations: """Test combined update operations.""" @pytest.mark.asyncio async def test_check_and_download_update_available(self, mock_update_manager): """Test combined check and download when update is available.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": True, "current_version": "1.0.0", "latest_version": "1.1.0" } mock_update_manager.download_update.return_value = True service = UpdateService(mock_update_manager) # Act result = await service.check_and_download_update() # Assert assert result.success is True assert result.data["ready_to_install"] is True assert "update_info" in result.data mock_update_manager.download_update.assert_called_once() @pytest.mark.asyncio async def test_check_and_download_update_not_available(self, mock_update_manager): """Test combined check and download when no update available.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": False, "current_version": "1.1.0" } service = UpdateService(mock_update_manager) # Act result = await service.check_and_download_update() # Assert assert result.success is True assert result.data["update_available"] is False mock_update_manager.download_update.assert_not_called() @pytest.mark.asyncio async def test_full_update_success(self, mock_update_manager): """Test full update workflow.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": True, "current_version": "1.0.0", "latest_version": "1.1.0" } mock_update_manager.download_update.return_value = True mock_update_manager.install_update.return_value = True service = UpdateService(mock_update_manager) # Act result = await service.full_update() # Assert assert result.success is True assert result.data["update_performed"] is True assert result.data["previous_version"] == "1.0.0" assert result.data["new_version"] == "1.1.0" assert result.data["requires_restart"] is True # Verify all steps were called mock_update_manager.check_for_updates.assert_called_once() mock_update_manager.download_update.assert_called_once() mock_update_manager.install_update.assert_called_once() @pytest.mark.asyncio async def test_full_update_already_up_to_date(self, mock_update_manager): """Test full update when already up to date.""" # Arrange mock_update_manager.check_for_updates.return_value = { "update_available": False, "current_version": "1.1.0" } service = UpdateService(mock_update_manager) # Act result = await service.full_update() # Assert assert result.success is True assert result.data["update_performed"] is False mock_update_manager.download_update.assert_not_called() mock_update_manager.install_update.assert_not_called()