import pytest from edge.config import (frame_interval_from_env, load_config, queue_db_path, require_upload_config) def test_defaults_when_env_empty(): cfg = load_config({}) assert cfg.device_id == "pi-unknown" assert cfg.fall.prone_confirm_s == 3.0 assert cfg.fall.no_motion_s == 10.0 assert cfg.camera_id == "cam-01" assert cfg.cafe24_url == "" assert cfg.secret == "" assert cfg.ghost.reappear_s == 10.0 assert cfg.zone.debounce_s == 0.75 assert cfg.engine.cooldown_s == 30.0 def test_env_overrides(): env = { "DEVICE_ID": "pi-w1", "CAMERA_ID": "cam-9", "CAFE24_URL": "https://x/api/events", "DEVICE_SECRET": "s", "FALL_PRONE_S": "2.0", "FALL_NO_MOTION_S": "8", "IMMOBILE_S": "90", "COOLDOWN_S": "45", "GHOST_REAPPEAR_S": "7", "ZONE_DEBOUNCE_S": "1.5", } cfg = load_config(env) assert cfg.device_id == "pi-w1" assert cfg.camera_id == "cam-9" assert cfg.fall.prone_confirm_s == 2.0 assert cfg.fall.no_motion_s == 8.0 assert cfg.immobility.immobile_s == 90.0 assert cfg.engine.cooldown_s == 45.0 assert cfg.engine.camera_id == "cam-9" assert cfg.ghost.reappear_s == 7.0 assert cfg.zone.debounce_s == 1.5 def test_require_upload_config_rejects_blank_url_and_secret(): # A blank CAFE24_URL or DEVICE_SECRET must fail LOUD at startup. Left silent, the daemon # runs green while every upload fails (bad URL -> requests error, caught) and the offline # queue grows until the SD card is full. with pytest.raises(ValueError, match="CAFE24_URL"): require_upload_config(load_config({"DEVICE_SECRET": "s"})) with pytest.raises(ValueError, match="DEVICE_SECRET"): require_upload_config(load_config({"CAFE24_URL": "https://x/move_detect"})) # Whitespace-only is still blank. with pytest.raises(ValueError): require_upload_config(load_config({"CAFE24_URL": " ", "DEVICE_SECRET": " "})) def test_require_upload_config_passes_when_both_present(): cfg = load_config({"CAFE24_URL": "https://x/move_detect", "DEVICE_SECRET": "s"}) require_upload_config(cfg) # must not raise def test_frame_interval_defaults_and_validates(): assert frame_interval_from_env({}) == pytest.approx(1 / 12) assert frame_interval_from_env({"TARGET_FPS": "24"}) == pytest.approx(1 / 24) # TARGET_FPS=0 -> ZeroDivisionError today -> infinite systemd crash loop; must be rejected. with pytest.raises(ValueError): frame_interval_from_env({"TARGET_FPS": "0"}) with pytest.raises(ValueError): frame_interval_from_env({"TARGET_FPS": "12fps"}) with pytest.raises(ValueError): frame_interval_from_env({"TARGET_FPS": "-5"}) def test_queue_db_path_defaults_absolute_under_state_dir(): # A relative 'queue.db' is unwritable under the hardened unit (ProtectSystem=strict, # WorkingDirectory read-only) -> crash loop. Default to the writable StateDirectory. assert queue_db_path({}) == "/var/lib/move-detect/queue.db" assert queue_db_path({"QUEUE_DB": "/custom/q.db"}) == "/custom/q.db"