plate-config-app/tests/test_start_tab.py
Danil Iarosh 35c9693740 fix: stabilize OTA updates in v2.0.2
- Исправлен перезапуск приложения после замены файлов и добавлено подтверждение запуска новой версии.
- Обеспечены откат при ошибке и корректное завершение фоновых задач.
- Исключены посторонние DLL из Windows-сборки, обновлены тесты и инструкция ручного перехода с v2.0.1.
2026-09-17 10:30:18 +03:00

161 lines
5.5 KiB
Python

from __future__ import annotations
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication
from app.services.update_service import UpdateError, UpdateInfo
from app.widgets.start_tab import StartTab, _UpdateCheckThread, _UpdateDownloadThread
class StartTabUpdateTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def test_update_button_is_below_settings_and_hidden_initially(self) -> None:
tab = StartTab(lambda: None)
self.assertTrue(tab._update_btn.isHidden())
self.assertLess(
tab._settings_column_layout.indexOf(tab._settings_btn),
tab._settings_column_layout.indexOf(tab._update_btn),
)
tab.shutdown()
def test_available_update_shows_download_button(self) -> None:
tab = StartTab(lambda: None)
info = UpdateInfo(
tag="v2.1.0",
version="2.1.0",
asset_name="plate_config_app-windows-x86_64.zip",
download_url="https://git.robotail.pro/download/test",
)
tab._on_update_check_done(info)
self.assertFalse(tab._update_btn.isHidden())
self.assertEqual("Скачать обновление", tab._update_btn.text())
self.assertIn("v2.1.0", tab._update_btn.toolTip())
tab.shutdown()
def test_no_update_hides_previously_visible_button(self) -> None:
tab = StartTab(lambda: None)
tab._on_update_check_done(self._update_info())
tab._on_update_check_done(None)
self.assertTrue(tab._update_btn.isHidden())
self.assertIsNone(tab._available_update)
tab.shutdown()
def test_progress_updates_button_text(self) -> None:
tab = StartTab(lambda: None)
tab._on_update_progress(57)
self.assertEqual("Скачивание 57%", tab._update_btn.text())
tab.shutdown()
def test_download_failure_resets_button_and_warns(self) -> None:
tab = StartTab(lambda: None)
tab._update_btn.setEnabled(False)
tab._update_btn.setText("Скачивание 42%")
with patch("app.widgets.start_tab.QMessageBox.warning") as warning:
tab._on_update_failed("ошибка сети")
self.assertTrue(tab._update_btn.isEnabled())
self.assertEqual("Скачать обновление", tab._update_btn.text())
warning.assert_called_once()
self.assertIn("ошибка сети", warning.call_args.args[2])
tab.shutdown()
def test_launch_failure_removes_download_and_warns(self) -> None:
tab = StartTab(lambda: None)
with tempfile.TemporaryDirectory() as directory:
downloaded = Path(directory) / ".downloaded-update"
downloaded.write_bytes(b"MZupdate")
with (
patch(
"app.widgets.start_tab.launch_update",
side_effect=UpdateError("не удалось запустить helper"),
),
patch("app.widgets.start_tab.QMessageBox.warning") as warning,
):
tab._on_update_downloaded(downloaded)
self.assertFalse(downloaded.exists())
self.assertEqual("Скачать обновление", tab._update_btn.text())
self.assertTrue(tab._update_btn.isEnabled())
warning.assert_called_once()
tab.shutdown()
def test_successful_download_launches_update_and_closes_window(self) -> None:
tab = StartTab(lambda: None)
downloaded = Path("downloaded-update")
with (
patch("app.widgets.start_tab.launch_update") as launcher,
patch.object(tab, "close") as close_window,
):
tab._on_update_downloaded(downloaded)
launcher.assert_called_once_with(downloaded)
close_window.assert_called_once_with()
self.assertEqual("Перезапуск…", tab._update_btn.text())
tab.shutdown()
def test_update_check_thread_hides_network_errors(self) -> None:
thread = _UpdateCheckThread()
results: list[UpdateInfo | None] = []
thread.result_ready.connect(results.append)
with patch(
"app.widgets.start_tab.check_for_update",
side_effect=UpdateError("offline"),
):
thread.run()
self.assertEqual([None], results)
def test_download_thread_emits_progress_and_result(self) -> None:
info = self._update_info()
result_path = Path("downloaded-update")
thread = _UpdateDownloadThread(info, Path("."))
progress_values: list[int] = []
results: list[Path] = []
thread.progress_changed.connect(progress_values.append)
thread.result_ready.connect(results.append)
def fake_download(_info, _directory, *, progress, cancelled):
self.assertFalse(cancelled())
progress(75)
return result_path
with patch("app.widgets.start_tab.download_update", side_effect=fake_download):
thread.run()
self.assertEqual([75], progress_values)
self.assertEqual([result_path], results)
@staticmethod
def _update_info() -> UpdateInfo:
return UpdateInfo(
tag="v2.1.0",
version="2.1.0",
asset_name="plate_config_app-windows-x86_64.zip",
download_url="https://git.robotail.pro/download/test",
)
if __name__ == "__main__":
unittest.main()