test_config.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. """Tests for daemon.config — Config.load() and _get_mac_id()."""
  2. import pytest
  3. from daemon.config import Config, _get_mac_id
  4. class TestConfigLoad:
  5. """Config.load() reads env vars and validates required fields."""
  6. def test_load_with_all_env_vars(self, monkeypatch):
  7. monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://10.0.0.1:5000")
  8. monkeypatch.setenv("SPOOLBUDDY_API_KEY", "test-key-123")
  9. monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "my-device")
  10. monkeypatch.setenv("SPOOLBUDDY_HOSTNAME", "my-host")
  11. cfg = Config.load()
  12. assert cfg.backend_url == "http://10.0.0.1:5000"
  13. assert cfg.api_key == "test-key-123"
  14. assert cfg.device_id == "my-device"
  15. assert cfg.hostname == "my-host"
  16. def test_load_missing_backend_url_raises(self, monkeypatch):
  17. monkeypatch.delenv("SPOOLBUDDY_BACKEND_URL", raising=False)
  18. monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
  19. with pytest.raises(RuntimeError, match="SPOOLBUDDY_BACKEND_URL is required"):
  20. Config.load()
  21. def test_load_missing_api_key_raises(self, monkeypatch):
  22. monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
  23. monkeypatch.delenv("SPOOLBUDDY_API_KEY", raising=False)
  24. with pytest.raises(RuntimeError, match="SPOOLBUDDY_API_KEY is required"):
  25. Config.load()
  26. def test_load_both_missing_raises_backend_url_first(self, monkeypatch):
  27. monkeypatch.delenv("SPOOLBUDDY_BACKEND_URL", raising=False)
  28. monkeypatch.delenv("SPOOLBUDDY_API_KEY", raising=False)
  29. with pytest.raises(RuntimeError, match="SPOOLBUDDY_BACKEND_URL"):
  30. Config.load()
  31. def test_load_defaults_device_id_from_mac(self, monkeypatch, tmp_path):
  32. monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
  33. monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
  34. monkeypatch.delenv("SPOOLBUDDY_DEVICE_ID", raising=False)
  35. monkeypatch.delenv("SPOOLBUDDY_HOSTNAME", raising=False)
  36. # Mock /sys/class/net with a fake interface
  37. net_dir = tmp_path / "sys" / "class" / "net"
  38. eth0 = net_dir / "eth0"
  39. eth0.mkdir(parents=True)
  40. (eth0 / "address").write_text("aa:bb:cc:dd:ee:ff\n")
  41. import daemon.config as config_mod
  42. monkeypatch.setattr(config_mod, "_get_mac_id", lambda: "sb-aabbccddeeff")
  43. cfg = Config.load()
  44. assert cfg.device_id == "sb-aabbccddeeff"
  45. def test_load_defaults_hostname_from_socket(self, monkeypatch):
  46. monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
  47. monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
  48. monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "dev-1")
  49. monkeypatch.delenv("SPOOLBUDDY_HOSTNAME", raising=False)
  50. cfg = Config.load()
  51. # Should fall back to socket.gethostname()
  52. import socket
  53. assert cfg.hostname == socket.gethostname()
  54. def test_load_default_intervals(self, monkeypatch):
  55. monkeypatch.setenv("SPOOLBUDDY_BACKEND_URL", "http://localhost:5000")
  56. monkeypatch.setenv("SPOOLBUDDY_API_KEY", "key")
  57. monkeypatch.setenv("SPOOLBUDDY_DEVICE_ID", "dev-1")
  58. cfg = Config.load()
  59. assert cfg.nfc_poll_interval == 0.3
  60. assert cfg.scale_read_interval == 0.1
  61. assert cfg.scale_report_interval == 1.0
  62. assert cfg.heartbeat_interval == 10.0
  63. assert cfg.tare_offset == 0
  64. assert cfg.calibration_factor == 1.0
  65. class TestGetMacId:
  66. """_get_mac_id() reads MAC from /sys/class/net."""
  67. def test_reads_first_non_lo_interface(self, monkeypatch, tmp_path):
  68. net_dir = tmp_path / "sys" / "class" / "net"
  69. lo = net_dir / "lo"
  70. lo.mkdir(parents=True)
  71. (lo / "address").write_text("00:00:00:00:00:00\n")
  72. eth0 = net_dir / "eth0"
  73. eth0.mkdir(parents=True)
  74. (eth0 / "address").write_text("de:ad:be:ef:00:01\n")
  75. from pathlib import Path
  76. import daemon.config as config_mod
  77. monkeypatch.setattr(
  78. config_mod, "Path", lambda p: tmp_path / "sys" / "class" / "net" if p == "/sys/class/net" else Path(p)
  79. )
  80. _get_mac_id()
  81. def test_skips_loopback(self, monkeypatch, tmp_path):
  82. """lo interface is skipped even if it has a MAC — result is uuid fallback."""
  83. # When only lo exists and /sys/class/net points to our tmp dir,
  84. # _get_mac_id should skip lo and fall back to uuid.
  85. # We test the real function by patching Path at the module level.
  86. from pathlib import Path
  87. import daemon.config as config_mod
  88. net_dir = tmp_path / "net"
  89. lo = net_dir / "lo"
  90. lo.mkdir(parents=True)
  91. (lo / "address").write_text("00:00:00:00:00:00\n")
  92. real_path = Path
  93. def fake_path(p):
  94. if p == "/sys/class/net":
  95. return real_path(net_dir)
  96. return real_path(p)
  97. monkeypatch.setattr(config_mod, "Path", fake_path)
  98. result = _get_mac_id()
  99. assert result.startswith("sb-")
  100. assert len(result) == 15 # "sb-" + 12 hex uuid chars
  101. def test_skips_all_zero_mac(self, monkeypatch, tmp_path):
  102. """Interfaces with all-zero MAC are skipped."""
  103. net_dir = tmp_path / "net"
  104. eth0 = net_dir / "eth0"
  105. eth0.mkdir(parents=True)
  106. (eth0 / "address").write_text("00:00:00:00:00:00\n")
  107. def test_fallback_to_uuid_when_no_interfaces(self, monkeypatch):
  108. """When /sys/class/net doesn't exist, falls back to uuid."""
  109. from pathlib import Path
  110. import daemon.config as config_mod
  111. # Make Path("/sys/class/net") point to nonexistent dir
  112. real_path = Path
  113. def fake_path(p):
  114. if p == "/sys/class/net":
  115. return real_path("/nonexistent/path/that/does/not/exist")
  116. return real_path(p)
  117. monkeypatch.setattr(config_mod, "Path", fake_path)
  118. result = _get_mac_id()
  119. assert result.startswith("sb-")
  120. assert len(result) == 15 # "sb-" + 12 hex chars