VoxIP Kiosk OS 1.0.0: Raspberry Pi kiosk with web management
Chromium kiosk without a desktop for the Raspberry Pi 3, a Flask web interface on port 8080 (kiosk, system and network management), a restricted root helper, network changes with automatic rollback, a healthcheck timer, install/update/uninstall scripts and a pi-gen image build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
a33c4822bf
96 files changed
+7535
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import services
|
||||
from voxip_kiosk.cdp import CDPError
|
||||
from voxip_kiosk.config import save_config
|
||||
from voxip_kiosk.healthcheck import CDP_FAILURES_BEFORE_RESTART, DISPLAY_RETRY_AFTER, HealthCheck
|
||||
|
||||
|
||||
class World:
|
||||
def __init__(self):
|
||||
self.units = {u: {"ActiveState": "active"} for u in (services.X_UNIT, services.BROWSER_UNIT, services.AGENT_UNIT, services.MANAGER_UNIT)}
|
||||
self.actions = []
|
||||
self.now = 10_000.0
|
||||
self.cdp_ok = True
|
||||
|
||||
def unit_status(self, unit):
|
||||
return self.units[unit]
|
||||
|
||||
def systemctl(self, *args):
|
||||
self.actions.append(args)
|
||||
|
||||
def version(self):
|
||||
if not self.cdp_ok:
|
||||
raise CDPError("timeout")
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def world(paths):
|
||||
w = World()
|
||||
hc = HealthCheck(paths, unit_status=w.unit_status, systemctl=w.systemctl, cdp=w, throttled=lambda: None,
|
||||
disk=lambda p: {"percent": 40}, now=lambda: w.now)
|
||||
return w, hc
|
||||
|
||||
|
||||
def test_all_healthy_no_actions(world):
|
||||
w, hc = world
|
||||
report = hc.run()
|
||||
assert report["actions"] == [] and w.actions == []
|
||||
|
||||
|
||||
def test_failed_browser_waits_for_cooldown(world):
|
||||
w, hc = world
|
||||
w.units[services.BROWSER_UNIT] = {"ActiveState": "failed", "Result": "start-limit-hit"}
|
||||
hc.run()
|
||||
assert w.actions == []
|
||||
w.now += DISPLAY_RETRY_AFTER
|
||||
hc.run()
|
||||
assert ("reset-failed", services.BROWSER_UNIT) in w.actions
|
||||
assert ("start", "--no-block", services.BROWSER_UNIT) in w.actions
|
||||
|
||||
|
||||
def test_failed_manager_restarts_immediately(world):
|
||||
w, hc = world
|
||||
w.units[services.MANAGER_UNIT] = {"ActiveState": "failed"}
|
||||
hc.run()
|
||||
assert ("start", "--no-block", services.MANAGER_UNIT) in w.actions
|
||||
|
||||
|
||||
def test_browser_restart_disabled_is_respected(world, paths):
|
||||
w, hc = world
|
||||
save_config(paths.config_file, {"browser_restart": False})
|
||||
w.units[services.BROWSER_UNIT] = {"ActiveState": "inactive"}
|
||||
hc.run()
|
||||
assert w.actions == []
|
||||
|
||||
|
||||
def test_unresponsive_devtools_restarts_browser(world):
|
||||
w, hc = world
|
||||
hc.run() # browser seen active
|
||||
w.now += 100
|
||||
w.cdp_ok = False
|
||||
for _ in range(CDP_FAILURES_BEFORE_RESTART - 1):
|
||||
hc.run()
|
||||
assert w.actions == []
|
||||
hc.run()
|
||||
assert ("restart", "--no-block", services.BROWSER_UNIT) in w.actions
|
||||
|
||||
|
||||
def test_maintenance_mode_blocks_actions(world, paths):
|
||||
w, hc = world
|
||||
(paths.health_run_dir / "maintenance").touch()
|
||||
w.units[services.AGENT_UNIT] = {"ActiveState": "inactive"}
|
||||
report = hc.run()
|
||||
assert report["maintenance"] is True and w.actions == []
|
||||
Reference in new issue
Block a user