Files
daynivo-kiosk-os/tests/conftest.py
T
rothmanandClaude Opus 5.5 eb0242c182
CI / test-bookworm (push) Successful in 29s
CI / test-latest (push) Successful in 31s
Rename every remaining voxip name to daynivo, with a one-time migration
Services (daynivo-kiosk-*, daynivo-network-guard, daynivo-boot-import),
programs in /usr/lib/daynivo-kiosk, folders /etc, /var/lib, /var/log and
/var/cache daynivo-kiosk*, the system user daynivo, DAYNIVO_* settings,
the plymouth theme, network profiles daynivo-eth0/wlan0, the image stage
and its first user daynivoadmin.

install.sh moves a kiosk installed up to 1.2.x over once: old services
stopped and removed, folders moved (configuration, password, browser
profile, logs) with symlinks under the old names, the voxip user renamed
with the same uid, network profiles renamed unless a change awaits
confirmation, and old leftovers removed. Tested by upgrading a configured
1.2.0 installation in a container, and running the install a second time.

Old names keep working where people type or place them: voxip-config.json,
voxip-authorized_keys, voxip-network-reset, voxip-kiosk-ctl, and
VOXIP_DEFAULT_URL / VOXIP_DEVICE_NAME for image builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:58:03 +01:00

143 lines
4.8 KiB
Python

import json
import re
from pathlib import Path
import pytest
from daynivo_kiosk.paths import Paths
ROOT = Path(__file__).resolve().parents[1]
@pytest.fixture
def paths(tmp_path) -> Paths:
p = Paths(
config_dir=tmp_path / "etc",
state_dir=tmp_path / "state",
network_state_dir=tmp_path / "netstate",
agent_run_dir=tmp_path / "run-agent",
browser_run_dir=tmp_path / "run-browser",
health_run_dir=tmp_path / "run-health",
share_dir=tmp_path / "share",
log_dir=tmp_path / "log",
helper=tmp_path / "helper",
nm_connections_dir=tmp_path / "nm",
)
for d in (p.config_dir, p.state_dir, p.agent_run_dir, p.browser_run_dir, p.health_run_dir, p.share_dir / "www",
p.log_dir, p.nm_connections_dir):
d.mkdir(parents=True)
(p.share_dir / "VERSION").write_text("9.9.9-test\n")
default = json.loads((ROOT / "config" / "config.default.json").read_text())
p.config_file.write_text(json.dumps(default))
return p
class FakeHelper:
def __init__(self):
self.calls = []
self.fail = None
self.responses = {}
def call(self, verb, *args, payload=None, timeout=60):
from daynivo_kiosk.helper_client import HelperError
self.calls.append((verb, args, payload))
if self.fail:
raise HelperError(self.fail)
return {"ok": True, "message": f"{verb} ok", **self.responses.get(verb, {})}
class FakeCDP:
def __init__(self):
self.calls = []
self.fail = None
def _do(self, name, *args):
from daynivo_kiosk.cdp import CDPError
self.calls.append((name, args))
if self.fail:
raise CDPError(self.fail)
def reload(self, ignore_cache=False):
self._do("reload")
def screenshot(self, quality=70):
self._do("screenshot")
return b"\xff\xd8fakejpeg"
@pytest.fixture
def fake_helper():
return FakeHelper()
@pytest.fixture
def fake_cdp():
return FakeCDP()
@pytest.fixture
def quiet_system(monkeypatch):
"""Keep the web tests independent of the machine they run on."""
from daynivo_kiosk import netinfo, services, sysinfo
monkeypatch.setattr(services, "summary", lambda: {
"browser": {"unit": services.BROWSER_UNIT, "status": "ok", "label": "Actief", "restarts": "2", "since": ""},
"x": {"unit": services.X_UNIT, "status": "ok", "label": "Actief", "restarts": "0", "since": ""},
"agent": {"unit": services.AGENT_UNIT, "status": "ok", "label": "Actief", "restarts": "0", "since": ""},
"manager": {"unit": services.MANAGER_UNIT, "status": "ok", "label": "Actief", "restarts": "0", "since": ""},
})
monkeypatch.setattr(services, "recent_errors", lambda lines=20: [])
monkeypatch.setattr(services, "journal", lambda source, lines=100, priority=None: f"log:{source}:{lines}:{priority}")
monkeypatch.setattr(sysinfo, "ip_addresses", lambda: {"eth0": ["192.168.10.20/24"]})
monkeypatch.setattr(sysinfo, "throttled", lambda: {"raw": "0x50000", "now": False, "ever": True, "flags": ["Onderspanning opgetreden"]})
monkeypatch.setattr(netinfo, "overview", lambda: {
"available": True, "wifi_country": "NL",
"devices": {
iface: {"interface": iface, "kind": kind, "present": True, "state": "connected", "state_label": "Verbonden",
"connected": True, "connection": f"daynivo-{iface}", "addresses": ["192.168.10.20/24"], "gateway": "192.168.10.1",
"dns": ["192.168.10.1"], "hwaddr": "B8:27:EB:00:00:01",
"profile": {"exists": True, "method": "dhcp", "address": "", "gateway": "", "dns": "", "ssid": "Kantoor", "hidden": False}}
for iface, kind in (("eth0", "ethernet"), ("wlan0", "wifi"))
},
})
@pytest.fixture
def app(paths, fake_helper, fake_cdp, quiet_system):
from daynivo_kiosk.web import create_app
application = create_app(paths, helper=fake_helper, cdp=fake_cdp)
application.config["TESTING"] = True
return application
@pytest.fixture
def client(app):
return app.test_client()
def csrf_from(response) -> str:
match = re.search(r'name="csrf-token" content="([^"]+)"', response.get_data(as_text=True))
assert match, "csrf meta tag missing"
return match.group(1)
PASSWORD = "Correct-Paard-Batterij-42"
@pytest.fixture
def logged_in(app, client, paths):
"""A client with a configured admin password and an active session."""
app.extensions["daynivo"].credentials.set_password(PASSWORD)
page = client.get("/login")
token = csrf_from(page)
resp = client.post("/login", data={"password": PASSWORD, "csrf_token": token})
assert resp.status_code == 302 and resp.headers["Location"].endswith("/")
return client
def token(client) -> str:
return csrf_from(client.get("/"))