mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
170 lines
6.0 KiB
Python
170 lines
6.0 KiB
Python
"""Tests for daemon.config — Config.load() and _get_mac_id()."""
|
|
|
|
import pytest
|
|
from daemon.config import Config, _get_mac_id
|
|
|
|
|
|
class TestConfigLoad:
|
|
"""Config.load() reads env vars and validates required fields."""
|
|
|
|
def test_load_with_all_env_vars(self, monkeypatch):
|
|
monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://10.0.0.1:5000")
|
|
monkeypatch.setenv("SPOOLBUDDY_API_KEY", "test-key-123")
|
|
monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "my-device")
|
|
monkeypatch.setenv("SPOOLBUDDY_HOSTNAME", "my-host")
|
|
|
|
cfg = Config.load()
|
|
|
|
assert cfg.backend_url == "http://10.0.0.1:5000"
|
|
assert cfg.api_key == "test-key-123"
|
|
assert cfg.device_id == "my-device"
|
|
assert cfg.hostname == "my-host"
|
|
|
|
def test_load_missing_backend_url_raises(self, monkeypatch):
|
|
monkeypatch.delenv("SPOOLBUDDY_BACKEND_URL", raising=False)
|
|
monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
|
|
|
|
with pytest.raises(RuntimeError, match="SPOOLBUDDY_BACKEND_URL is required"):
|
|
Config.load()
|
|
|
|
def test_load_missing_api_key_raises(self, monkeypatch):
|
|
monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
|
|
monkeypatch.delenv("SPOOLBUDDY_API_KEY", raising=False)
|
|
|
|
with pytest.raises(RuntimeError, match="SPOOLBUDDY_API_KEY is required"):
|
|
Config.load()
|
|
|
|
def test_load_both_missing_raises_backend_url_first(self, monkeypatch):
|
|
monkeypatch.delenv("SPOOLBUDDY_BACKEND_URL", raising=False)
|
|
monkeypatch.delenv("SPOOLBUDDY_API_KEY", raising=False)
|
|
|
|
with pytest.raises(RuntimeError, match="SPOOLBUDDY_BACKEND_URL"):
|
|
Config.load()
|
|
|
|
def test_load_defaults_device_id_from_mac(self, monkeypatch, tmp_path):
|
|
monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
|
|
monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
|
|
monkeypatch.delenv("SPOOLBUDDY_DEVICE_ID", raising=False)
|
|
monkeypatch.delenv("SPOOLBUDDY_HOSTNAME", raising=False)
|
|
|
|
# Mock /sys/class/net with a fake interface
|
|
net_dir = tmp_path / "sys" / "class" / "net"
|
|
eth0 = net_dir / "eth0"
|
|
eth0.mkdir(parents=True)
|
|
(eth0 / "address").write_text("aa:bb:cc:dd:ee:ff\n")
|
|
|
|
import daemon.config as config_mod
|
|
|
|
monkeypatch.setattr(config_mod, "_get_mac_id", lambda: "sb-aabbccddeeff")
|
|
|
|
cfg = Config.load()
|
|
|
|
assert cfg.device_id == "sb-aabbccddeeff"
|
|
|
|
def test_load_defaults_hostname_from_socket(self, monkeypatch):
|
|
monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
|
|
monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
|
|
monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "dev-1")
|
|
monkeypatch.delenv("SPOOLBUDDY_HOSTNAME", raising=False)
|
|
|
|
cfg = Config.load()
|
|
|
|
# Should fall back to socket.gethostname()
|
|
import socket
|
|
|
|
assert cfg.hostname == socket.gethostname()
|
|
|
|
def test_load_default_intervals(self, monkeypatch):
|
|
monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
|
|
monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
|
|
monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "dev-1")
|
|
|
|
cfg = Config.load()
|
|
|
|
assert cfg.nfc_poll_interval == 0.3
|
|
assert cfg.scale_read_interval == 0.1
|
|
assert cfg.scale_report_interval == 1.0
|
|
assert cfg.heartbeat_interval == 10.0
|
|
assert cfg.tare_offset == 0
|
|
assert cfg.calibration_factor == 1.0
|
|
|
|
|
|
class TestGetMacId:
|
|
"""_get_mac_id() reads MAC from /sys/class/net."""
|
|
|
|
def test_reads_first_non_lo_interface(self, monkeypatch, tmp_path):
|
|
net_dir = tmp_path / "sys" / "class" / "net"
|
|
|
|
lo = net_dir / "lo"
|
|
lo.mkdir(parents=True)
|
|
(lo / "address").write_text("00:00:00:00:00:00\n")
|
|
|
|
eth0 = net_dir / "eth0"
|
|
eth0.mkdir(parents=True)
|
|
(eth0 / "address").write_text("de:ad:be:ef:00:01\n")
|
|
|
|
from pathlib import Path
|
|
|
|
import daemon.config as config_mod
|
|
|
|
monkeypatch.setattr(
|
|
config_mod, "Path", lambda p: tmp_path / "sys" / "class" / "net" if p == "/sys/class/net" else Path(p)
|
|
)
|
|
|
|
_get_mac_id()
|
|
|
|
def test_skips_loopback(self, monkeypatch, tmp_path):
|
|
"""lo interface is skipped even if it has a MAC — result is uuid fallback."""
|
|
# When only lo exists and /sys/class/net points to our tmp dir,
|
|
# _get_mac_id should skip lo and fall back to uuid.
|
|
# We test the real function by patching Path at the module level.
|
|
from pathlib import Path
|
|
|
|
import daemon.config as config_mod
|
|
|
|
net_dir = tmp_path / "net"
|
|
lo = net_dir / "lo"
|
|
lo.mkdir(parents=True)
|
|
(lo / "address").write_text("00:00:00:00:00:00\n")
|
|
|
|
real_path = Path
|
|
|
|
def fake_path(p):
|
|
if p == "/sys/class/net":
|
|
return real_path(net_dir)
|
|
return real_path(p)
|
|
|
|
monkeypatch.setattr(config_mod, "Path", fake_path)
|
|
|
|
result = _get_mac_id()
|
|
assert result.startswith("sb-")
|
|
assert len(result) == 15 # "sb-" + 12 hex uuid chars
|
|
|
|
def test_skips_all_zero_mac(self, monkeypatch, tmp_path):
|
|
"""Interfaces with all-zero MAC are skipped."""
|
|
net_dir = tmp_path / "net"
|
|
eth0 = net_dir / "eth0"
|
|
eth0.mkdir(parents=True)
|
|
(eth0 / "address").write_text("00:00:00:00:00:00\n")
|
|
|
|
def test_fallback_to_uuid_when_no_interfaces(self, monkeypatch):
|
|
"""When /sys/class/net doesn't exist, falls back to uuid."""
|
|
from pathlib import Path
|
|
|
|
import daemon.config as config_mod
|
|
|
|
# Make Path("/sys/class/net") point to nonexistent dir
|
|
real_path = Path
|
|
|
|
def fake_path(p):
|
|
if p == "/sys/class/net":
|
|
return real_path("/nonexistent/path/that/does/not/exist")
|
|
return real_path(p)
|
|
|
|
monkeypatch.setattr(config_mod, "Path", fake_path)
|
|
|
|
result = _get_mac_id()
|
|
|
|
assert result.startswith("sb-")
|
|
assert len(result) == 15 # "sb-" + 12 hex chars
|