fix(install): actualización desatendida en Windows, con reversión
install.sh recibió la maquinaria de actualización segura y install.ps1 nunca recibió el equivalente. La asimetría se notaba en producción: actualizar desde el PANEL dejaba el servidor sin agente. - -UpdateInPlace: actualiza conservando la tarea y la configuración, sin correr el bootstrap (una segunda instancia purga el Temp\ de la que está viva). - No se interrumpe una restauración en curso: sale con 75 (EX_TEMPFAIL), que el PANEL traduce a "reintenta luego". Windows no tenía esta guarda y una reinstalación a destiempo dejaba el respaldo vetado y la base en SINGLE_USER. - Respaldo del binario anterior y reversión automática si el nuevo no arranca. - Rearranque garantizado en TODOS los modos: la detención corría siempre, pero solo -Service volvía a arrancar algo. - Espera de liberación del .exe de 5s a 30s con reintentos de la copia. - Se distingue "no es administrador" de "es administrador con el token filtrado por UAC", que es lo que recibe una sesión de OpenSSH. Se veían idénticos y el remedio es el opuesto. Y --headless deja de ser un no-op en Windows: _ensure_qt_platform() salía de inmediato en win32, así que la tarea ONSTART arrancaba como SYSTEM en la sesión 0 con el plugin Qt 'windows' intentando crear una ventana real. En Linux el unit fija QT_QPA_PLATFORM=offscreen por fuera, y esa asimetría escondió el defecto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
tests/test_runner_qt_platform.py
Normal file
69
tests/test_runner_qt_platform.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Selección del plugin de plataforma Qt.
|
||||
|
||||
Esto decide si el agente arranca o no en un servidor. El caso que motivó las pruebas: en
|
||||
Windows, `--headless` no hacía nada —la función salía de inmediato en `win32`—, así que la
|
||||
tarea programada ONSTART, que corre como SYSTEM en la sesión 0 y sin escritorio interactivo,
|
||||
arrancaba con el plugin 'windows' e intentaba crear una ventana real. En Linux el mismo modo
|
||||
funcionaba porque el unit de systemd fija QT_QPA_PLATFORM=offscreen por fuera, y esa asimetría
|
||||
escondió el defecto: la bandera parecía cubierta en las dos plataformas.
|
||||
"""
|
||||
|
||||
import runner
|
||||
|
||||
|
||||
def test_headless_fuerza_offscreen_en_windows(monkeypatch):
|
||||
# La regresión: la tarea ONSTART pasa --headless y necesita que sirva de algo.
|
||||
monkeypatch.setattr(runner.sys, "platform", "win32")
|
||||
monkeypatch.delenv("QT_QPA_PLATFORM", raising=False)
|
||||
|
||||
assert runner._ensure_qt_platform(headless=True) == "offscreen"
|
||||
assert runner.os.environ["QT_QPA_PLATFORM"] == "offscreen"
|
||||
|
||||
|
||||
def test_headless_fuerza_offscreen_en_linux(monkeypatch):
|
||||
monkeypatch.setattr(runner.sys, "platform", "linux")
|
||||
monkeypatch.delenv("QT_QPA_PLATFORM", raising=False)
|
||||
monkeypatch.setenv("DISPLAY", ":0")
|
||||
|
||||
# Con --headless da igual que haya display: lo pidió el llamador.
|
||||
assert runner._ensure_qt_platform(headless=True) == "offscreen"
|
||||
|
||||
|
||||
def test_windows_sin_headless_conserva_el_plugin_nativo(monkeypatch):
|
||||
# Quien abre la app con doble clic quiere su ventana. En Windows no existen DISPLAY ni
|
||||
# WAYLAND_DISPLAY, así que tomar su ausencia como señal mandaría a offscreen a todos.
|
||||
monkeypatch.setattr(runner.sys, "platform", "win32")
|
||||
monkeypatch.delenv("QT_QPA_PLATFORM", raising=False)
|
||||
monkeypatch.delenv("DISPLAY", raising=False)
|
||||
monkeypatch.delenv("WAYLAND_DISPLAY", raising=False)
|
||||
|
||||
assert runner._ensure_qt_platform(headless=False) == ""
|
||||
assert "QT_QPA_PLATFORM" not in runner.os.environ
|
||||
|
||||
|
||||
def test_linux_sin_display_cae_a_offscreen(monkeypatch):
|
||||
monkeypatch.setattr(runner.sys, "platform", "linux")
|
||||
monkeypatch.delenv("QT_QPA_PLATFORM", raising=False)
|
||||
monkeypatch.delenv("DISPLAY", raising=False)
|
||||
monkeypatch.delenv("WAYLAND_DISPLAY", raising=False)
|
||||
|
||||
assert runner._ensure_qt_platform(headless=False) == "offscreen"
|
||||
|
||||
|
||||
def test_una_eleccion_explicita_manda_sobre_headless(monkeypatch):
|
||||
# El instalador fija QT_QPA_PLATFORM para el bootstrap; un override del operador tiene que
|
||||
# sobrevivir, o depurar un arranque en un servidor ajeno se vuelve imposible.
|
||||
monkeypatch.setattr(runner.sys, "platform", "win32")
|
||||
monkeypatch.setenv("QT_QPA_PLATFORM", "minimal")
|
||||
|
||||
assert runner._ensure_qt_platform(headless=True) == "minimal"
|
||||
assert runner.os.environ["QT_QPA_PLATFORM"] == "minimal"
|
||||
|
||||
|
||||
def test_la_bandera_headless_existe_en_el_parser():
|
||||
# La tarea programada y el unit de systemd la pasan; si desaparece, argparse aborta el
|
||||
# arranque con código 2 y el agente no levanta en ningún servidor.
|
||||
args = runner.parse_args(["--start-engine", "--headless"])
|
||||
assert args.headless is True
|
||||
assert args.start_engine is True
|
||||
Reference in New Issue
Block a user