145 lines
5.1 KiB
Python
145 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
from PySide6.QtTest import QTest
|
|
from PySide6.QtWidgets import QApplication, QPushButton
|
|
|
|
from cmbuyer_client.localstate.models import ProfileSettings
|
|
from cmbuyer_client.ui.settings import SettingsPage
|
|
|
|
|
|
DEVICE_ID = "f3c9f507-7473-4fa6-8d71-8786c34c6301"
|
|
TOKEN = "b" * 64
|
|
|
|
|
|
class FakeStore:
|
|
def __init__(self, error: Exception | None = None) -> None:
|
|
self.calls: list[tuple[ProfileSettings, object]] = []
|
|
self.error = error
|
|
|
|
def save_profile(self, settings: ProfileSettings, token: object) -> None:
|
|
self.calls.append((settings, token))
|
|
if self.error is not None:
|
|
raise self.error
|
|
|
|
|
|
class SettingsPageTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls.app = QApplication.instance() or QApplication([])
|
|
|
|
def setUp(self) -> None:
|
|
self.directory = tempfile.TemporaryDirectory()
|
|
self.adb = Path(self.directory.name) / "adb.exe"
|
|
self.adb.touch()
|
|
|
|
def tearDown(self) -> None:
|
|
self.directory.cleanup()
|
|
|
|
def fill(self, page: SettingsPage) -> None:
|
|
page.device_id.setText(DEVICE_ID)
|
|
page.adb_path.setText(str(self.adb))
|
|
page.adb_serial.setText("device-serial")
|
|
|
|
def existing_settings(self) -> ProfileSettings:
|
|
return ProfileSettings(
|
|
"default",
|
|
"http://127.0.0.1:8080",
|
|
DEVICE_ID,
|
|
str(self.adb),
|
|
"device-serial",
|
|
"usb",
|
|
)
|
|
|
|
def test_first_save_requires_token_and_performs_zero_store_write(self) -> None:
|
|
store = FakeStore()
|
|
page = SettingsPage(store)
|
|
page.show()
|
|
QTest.qWait(10)
|
|
self.fill(page)
|
|
page.save()
|
|
self.assertEqual(store.calls, [])
|
|
self.assertTrue(page.device_token.hasFocus())
|
|
self.assertIn("首次保存", page.feedback.text())
|
|
|
|
def test_visual_order_focuses_invalid_device_before_missing_token_or_adb(self) -> None:
|
|
store = FakeStore()
|
|
page = SettingsPage(store)
|
|
page.show()
|
|
QTest.qWait(10)
|
|
page.device_id.setText("not-a-uuid")
|
|
page.save()
|
|
self.assertEqual(store.calls, [])
|
|
self.assertTrue(page.device_id.hasFocus())
|
|
self.assertIn("UUIDv4", page.feedback.text())
|
|
|
|
def test_existing_blank_token_passes_none_and_success_never_refills(self) -> None:
|
|
store = FakeStore()
|
|
page = SettingsPage(store, settings=self.existing_settings(), has_stored_device_token=True)
|
|
page.save()
|
|
self.assertEqual(len(store.calls), 1)
|
|
self.assertIsNone(store.calls[0][1])
|
|
self.assertEqual(page.device_token.text(), "")
|
|
self.assertEqual(page.token_status.text(), "已保存")
|
|
|
|
def test_nonempty_token_replaces_once_then_is_cleared(self) -> None:
|
|
store = FakeStore()
|
|
page = SettingsPage(store, settings=self.existing_settings(), has_stored_device_token=True)
|
|
page.device_token.setText(TOKEN)
|
|
page.save()
|
|
self.assertEqual(len(store.calls), 1)
|
|
self.assertEqual(store.calls[0][1].value, TOKEN)
|
|
self.assertEqual(page.device_token.text(), "")
|
|
self.assertNotIn(TOKEN, page.feedback.text())
|
|
self.assertNotIn(TOKEN, page.token_status.text())
|
|
|
|
def test_failed_save_preserves_token_and_focuses_secret_field_without_echo(self) -> None:
|
|
store = FakeStore(RuntimeError("must-not-appear-" + TOKEN))
|
|
page = SettingsPage(store, settings=self.existing_settings(), has_stored_device_token=True)
|
|
page.show()
|
|
QTest.qWait(10)
|
|
page.device_token.setText(TOKEN)
|
|
page.save()
|
|
self.assertEqual(page.device_token.text(), TOKEN)
|
|
self.assertTrue(page.device_token.hasFocus())
|
|
self.assertNotIn(TOKEN, page.feedback.text())
|
|
|
|
def test_pending_or_active_freezes_every_non_token_field_and_preserves_exact_settings(self) -> None:
|
|
original = self.existing_settings()
|
|
store = FakeStore()
|
|
page = SettingsPage(
|
|
store,
|
|
settings=original,
|
|
has_stored_device_token=True,
|
|
identity_frozen=True,
|
|
)
|
|
for field in (
|
|
page.device_id,
|
|
page.adb_path,
|
|
page.adb_serial,
|
|
page.transport,
|
|
page.poll_interval,
|
|
page.failure_threshold,
|
|
page.http_timeout,
|
|
page.step_timeout,
|
|
):
|
|
self.assertFalse(field.isEnabled())
|
|
self.assertTrue(page.device_token.isEnabled())
|
|
page.device_token.setText(TOKEN)
|
|
page.save()
|
|
self.assertEqual(store.calls[0][0], original)
|
|
|
|
def test_page_has_no_connection_probe_command_and_explains_deferred_validation(self) -> None:
|
|
page = SettingsPage(FakeStore())
|
|
button_texts = [button.text() for button in page.findChildren(QPushButton)]
|
|
self.assertEqual(button_texts, ["保存配置"])
|
|
self.assertIn("首次真实领取", page.validation_hint.text())
|
|
self.assertEqual(page.service_url.text(), "http://127.0.0.1:8080")
|
|
self.assertTrue(page.service_url.isReadOnly())
|