Add two optional windowed frontends over the same local state the TUI and web console read: `enodia-sentinel gui` (stdlib tkinter) and `enodia-sentinel gui-qt` (PySide6, new `[qt]` extra). Both share `gui/model.py`, a GUI-free presentation model over `tui.collect_model`, so every formatter is unit-testable without a display server. Only `app.py` and `qt_app.py` import GUI libraries, enforced by import-guard tests mirroring the tray applet's boundary. Tabs cover status, alerts, incidents, posture, integrity, response plans, and the event tail, plus daemon-control buttons via systemctl. Response plans are display-only; the GUIs never execute containment commands. Core runtime dependencies stay empty. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JX86xeoBJVBb16qHkDf53K
37 lines
1.3 KiB
Python
37 lines
1.3 KiB
Python
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Boundary tests for the Qt GUI frontend.
|
|
|
|
The shared model must stay GUI-free; only qt_app.py may reference PySide6.
|
|
"""
|
|
import re
|
|
import tomllib
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
GUI = ROOT / "enodia_sentinel" / "gui"
|
|
|
|
|
|
class TestQtGuiBoundaries(unittest.TestCase):
|
|
def test_model_has_no_qt_imports(self):
|
|
text = (GUI / "model.py").read_text()
|
|
self.assertFalse(
|
|
re.search(r"\b(import PySide6|from PySide6)\b", text),
|
|
"model.py must not import PySide6",
|
|
)
|
|
|
|
def test_tkinter_app_has_no_qt_imports(self):
|
|
text = (GUI / "app.py").read_text()
|
|
self.assertNotIn("PySide6", text, "app.py must not import PySide6")
|
|
|
|
def test_qt_app_imports_without_runtime_gui_deps(self):
|
|
# qt_app.py guards PySide6 and is importable even when Qt is absent.
|
|
from enodia_sentinel.gui import qt_app # noqa: F401
|
|
|
|
def test_pyproject_declares_qt_extra_and_script(self):
|
|
data = tomllib.loads((ROOT / "pyproject.toml").read_text())
|
|
self.assertIn("qt", data["project"]["optional-dependencies"])
|
|
self.assertEqual(
|
|
data["project"]["scripts"]["enodia-sentinel-gui-qt"],
|
|
"enodia_sentinel.gui.qt_app:main",
|
|
)
|