Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
369
tests/unit/services/test_update_service.py
Normal file
369
tests/unit/services/test_update_service.py
Normal file
@@ -0,0 +1,369 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user