Add terminal TUI and shell completion

This commit is contained in:
Luna 2026-07-08 20:47:58 -07:00
parent a7535cfc56
commit cab60cd633
11 changed files with 612 additions and 0 deletions

81
tests/test_tui.py Normal file
View file

@ -0,0 +1,81 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import io
import json
import os
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from enodia_sentinel.cli import main
from enodia_sentinel.config import Config
from enodia_sentinel.tui import (
TuiState,
collect_model,
complete_command,
render_lines,
run_tui_command,
)
class TestTuiCore(unittest.TestCase):
def setUp(self):
self.dir = tempfile.TemporaryDirectory()
self.tmp = Path(self.dir.name)
self.cfg = Config()
self.cfg.log_dir = self.tmp
def tearDown(self):
self.dir.cleanup()
def test_command_completion_unique_and_common_prefix(self):
self.assertEqual(complete_command("inc"), "incidents")
self.assertEqual(complete_command("r"), "r")
self.assertEqual(complete_command("zzz"), "zzz")
def test_run_tui_command_switches_views_and_filters(self):
state = TuiState()
run_tui_command(state, "incidents")
self.assertEqual(state.view, "incidents")
run_tui_command(state, "filter ssh")
self.assertEqual(state.filter_text, "ssh")
run_tui_command(state, "clear")
self.assertEqual(state.filter_text, "")
run_tui_command(state, "quit")
self.assertTrue(state.should_quit)
def test_collect_model_and_render_status(self):
model = collect_model(self.cfg)
state = TuiState(model=model)
lines = render_lines(state, width=80)
self.assertTrue(any("Daemon:" in line for line in lines))
self.assertTrue(any("Heartbeat:" in line for line in lines))
def test_render_alerts_and_filter(self):
(self.tmp / "alert-1.json").write_text(json.dumps({
"time": "2026-07-09T01:02:03-07:00",
"alerts": [{
"severity": "CRITICAL",
"sid": 100010,
"signature": "reverse_shell",
"detail": "bash connected to 8.8.8.8",
}],
}))
state = TuiState(view="alerts", model=collect_model(self.cfg))
self.assertTrue(any("reverse_shell" in line for line in render_lines(state)))
state.filter_text = "nomatch"
self.assertEqual(render_lines(state), ["No rows match filter: nomatch"])
def test_cli_completion_outputs_scripts(self):
for shell in ("bash", "zsh"):
with self.subTest(shell=shell):
buf = io.StringIO()
with redirect_stdout(buf):
code = main(["completion", shell])
self.assertEqual(code, 0)
self.assertIn("enodia-sentinel", buf.getvalue())
self.assertIn("tui", buf.getvalue())
if __name__ == "__main__":
unittest.main()