feat(gui): add optional tkinter and Qt6 desktop dashboards
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
This commit is contained in:
parent
9e3575461b
commit
457c7604ba
15 changed files with 1270 additions and 0 deletions
40
tests/test_gui_imports.py
Normal file
40
tests/test_gui_imports.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Boundary tests for the desktop GUI package.
|
||||
|
||||
The logic module must not import tkinter so it can be tested on headless
|
||||
runners; only app.py may import GUI libraries.
|
||||
"""
|
||||
import re
|
||||
import tomllib
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
GUI = ROOT / "enodia_sentinel" / "gui"
|
||||
|
||||
|
||||
class TestGuiBoundaries(unittest.TestCase):
|
||||
def test_model_has_no_tkinter_imports(self):
|
||||
text = (GUI / "model.py").read_text()
|
||||
self.assertFalse(
|
||||
re.search(r"\b(import tkinter|from tkinter)\b", text),
|
||||
"model.py must not import tkinter",
|
||||
)
|
||||
|
||||
def test_model_imports_without_tkinter(self):
|
||||
# Must succeed even when tkinter is unavailable.
|
||||
from enodia_sentinel.gui import model # noqa: F401
|
||||
|
||||
def test_app_module_imports_without_tkinter(self):
|
||||
# The shell is importable without a display/GUI libraries; runtime
|
||||
# use raises a graceful SystemExit when tkinter is absent.
|
||||
from enodia_sentinel.gui import app # noqa: F401
|
||||
|
||||
def test_pyproject_declares_gui_script(self):
|
||||
data = tomllib.loads((ROOT / "pyproject.toml").read_text())
|
||||
self.assertEqual(
|
||||
data["project"]["scripts"]["enodia-sentinel-gui"],
|
||||
"enodia_sentinel.gui.app:main",
|
||||
)
|
||||
# Core runtime deps stay empty; tkinter is stdlib.
|
||||
self.assertEqual(data["project"]["dependencies"], [])
|
||||
Loading…
Add table
Add a link
Reference in a new issue