Files
zyt/app/tests/test_local_launcher.py
2026-10-23 10:17:09 +08:00

107 lines
4.9 KiB
Python

"""The explicit local launcher must validate the effective endpoint after preferences."""
from __future__ import annotations
import importlib.util
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
spec = importlib.util.spec_from_file_location(
"local_launcher", Path(__file__).parents[1] / "scripts/run_local_workstation.py"
)
launcher = importlib.util.module_from_spec(spec)
spec.loader.exec_module(launcher)
@pytest.fixture(autouse=True)
def loopback_dns(monkeypatch):
monkeypatch.setattr(launcher.socket, "getaddrinfo", lambda *_: [(None, None, None, None, ("127.0.0.1", 443))])
@pytest.mark.parametrize("url", [
"http://local.test", "https://user:password@local.test", "https://local.test?token=private",
"https://local.test#fragment", "https://local.test:invalid", "https://local.test:70000",
])
def test_local_url_rejects_unsecured_or_credential_url(url):
with pytest.raises(ValueError):
launcher.validate_local_url(url)
def test_local_url_rejects_external_dns(monkeypatch):
monkeypatch.setattr(launcher.socket, "getaddrinfo", lambda *_: [(None, None, None, None, ("192.0.2.1", 443))])
with pytest.raises(ValueError):
launcher.validate_local_url("https://local.test")
@pytest.mark.parametrize("effective", [
"http://local.test/adminapi", "https://local.test:8443/adminapi", "https://local.test/other/adminapi",
])
def test_preferences_cannot_change_validated_scheme_port_or_path(effective):
with pytest.raises(ValueError):
launcher.validate_effective_config(SimpleNamespace(api_base_url=effective, verify_ssl=True), "https://local.test")
def test_local_config_keeps_certificate_verification():
launcher.validate_effective_config(SimpleNamespace(api_base_url="https://local.test/adminapi", verify_ssl=True), "https://local.test")
with pytest.raises(ValueError):
launcher.validate_effective_config(SimpleNamespace(api_base_url="https://local.test/adminapi", verify_ssl=False), "https://local.test")
@pytest.mark.parametrize("debug_mode", [False, True])
def test_local_entry_repairs_stale_preferences_and_locks_later_updates(tmp_path, monkeypatch, debug_mode):
from doctor_workstation import config as config_module
monkeypatch.setattr(config_module, "DEBUG_MODE", debug_mode)
monkeypatch.setenv("DOCTOR_CONFIG_DIR", str(tmp_path))
monkeypatch.setenv("DOCTOR_API_BASE_URL", "https://production.example.test")
(tmp_path / "preferences.json").write_text(json.dumps({
"api_base_url": "https://production.example.test/adminapi",
"demo_mode": True, "verify_ssl": False, "request_timeout": 55,
}), encoding="utf-8")
local_type = launcher.local_config_type("https://local.test")
config = local_type.load()
launcher.validate_effective_config(config, "https://local.test")
assert config.api_base_url == "https://local.test/adminapi"
assert not config.debug_mode and not config.demo_mode and config.verify_ssl
assert config.request_timeout == 55
updated = config.with_updates(api_base_url="https://production.example.test", verify_ssl=False, demo_mode=True)
launcher.validate_effective_config(updated, "https://local.test")
assert isinstance(updated, local_type)
assert not updated.demo_mode
updated.save_preferences()
persisted = json.loads((tmp_path / "preferences.json").read_text(encoding="utf-8"))
assert persisted["api_base_url"] == config.api_base_url and persisted["verify_ssl"]
def test_local_login_cannot_restore_or_save_a_production_server(tmp_path, monkeypatch):
from PySide6.QtCore import QSettings
from PySide6.QtWidgets import QApplication
from doctor_workstation.ui.login import LoginWindow
monkeypatch.setenv("DOCTOR_CONFIG_DIR", str(tmp_path))
config = launcher.local_config_type("https://local.test").load()
application = QApplication.instance() or QApplication([])
settings = QSettings(str(tmp_path / "stale-login.ini"), QSettings.Format.IniFormat)
settings.setValue("server/base_url", "https://production.example.test")
settings.setValue("server/verify_ssl", False)
repository = object()
window = LoginWindow(repository, config=config, settings=settings)
try:
assert window.server_url_edit.text() == config.api_base_url
assert window._credential_scope() == config.api_base_url
assert not window.allow_self_signed_check.isChecked()
assert not window.demo_check.isChecked()
window.server_url_edit.setText("https://production.example.test")
window.allow_self_signed_check.setChecked(True)
assert window._apply_server_settings(announce=False)
assert window.server_url_edit.text() == config.api_base_url
assert window.active_repository is repository
launcher.validate_effective_config(window.config, "https://local.test")
finally:
window.close()
window.deleteLater()
application.processEvents()