"""Guard: the systemd unit + env template must stay in sync with what the daemon actually reads. If someone renames an env var in edge/config.py or edge/daemon.py (or edits the unit), this fails instead of shipping a broken bundle. """ import os DEPLOY = os.path.join(os.path.dirname(__file__), "..", "deploy", "systemd") ENV_EXAMPLE = os.path.join(DEPLOY, "move-detect-edge.env.example") UNIT = os.path.join(DEPLOY, "move-detect-edge.service") # Keys the daemon consumes (edge/config.py:load_config + edge/daemon.py:main). REQUIRED_ENV = { "DEVICE_ID", "DEVICE_SECRET", "CAFE24_URL", "CAMERA_ID", "MOVENET_ONNX", "UVC_DEVICE", "TARGET_FPS", "QUEUE_DB", } def _env_keys(text): keys = set() for line in text.splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue keys.add(line.split("=", 1)[0].strip()) return keys def test_env_example_lists_every_required_key(): with open(ENV_EXAMPLE, encoding="utf-8") as f: keys = _env_keys(f.read()) missing = REQUIRED_ENV - keys assert not missing, f"env template missing required keys: {sorted(missing)}" def test_env_example_values_have_no_inline_comments(): # systemd EnvironmentFile= does NOT strip inline comments — a trailing "# ..." on # a KEY=value line becomes part of the value and breaks int()/float()/path/secret. with open(ENV_EXAMPLE, encoding="utf-8") as f: offenders = [] for line in f.read().splitlines(): s = line.strip() if not s or s.startswith("#") or "=" not in s: continue value = s.split("=", 1)[1] if "#" in value: offenders.append(s) assert not offenders, f"inline comments in env values break systemd: {offenders}" def test_systemd_unit_wiring(): with open(UNIT, encoding="utf-8") as f: unit = f.read() assert "EnvironmentFile=/etc/move-detect/edge.env" in unit assert "PYTHONPATH=/opt/move-detect/src" in unit assert "python -m edge" in unit # runs the package entrypoint assert "Restart=always" in unit # unattended resilience assert "WantedBy=multi-user.target" in unit