mirror of
https://github.com/jorijn/meshcore-stats.git
synced 2026-03-28 17:42:55 +01:00
229 lines
8.6 KiB
Python
229 lines
8.6 KiB
Python
"""Tests for environment variable parsing and Config class."""
|
|
|
|
|
|
import pytest
|
|
|
|
from meshmon.env import (
|
|
Config,
|
|
get_bool,
|
|
get_config,
|
|
get_int,
|
|
get_str,
|
|
)
|
|
|
|
|
|
class TestGetStrEdgeCases:
|
|
"""Additional edge case tests for get_str."""
|
|
|
|
def test_whitespace_value_preserved(self, monkeypatch):
|
|
"""Whitespace-only value is preserved."""
|
|
monkeypatch.setenv("TEST_VAR", " ")
|
|
assert get_str("TEST_VAR") == " "
|
|
|
|
def test_special_characters(self, monkeypatch):
|
|
"""Special characters are preserved."""
|
|
monkeypatch.setenv("TEST_VAR", "hello@world#123!")
|
|
assert get_str("TEST_VAR") == "hello@world#123!"
|
|
|
|
|
|
class TestGetIntEdgeCases:
|
|
"""Additional edge case tests for get_int."""
|
|
|
|
def test_leading_zeros(self, monkeypatch):
|
|
"""Leading zeros work (not octal)."""
|
|
monkeypatch.setenv("TEST_INT", "042")
|
|
assert get_int("TEST_INT", 0) == 42
|
|
|
|
def test_whitespace_around_number(self, monkeypatch):
|
|
"""Whitespace around number is tolerated by int()."""
|
|
monkeypatch.setenv("TEST_INT", " 42 ")
|
|
# Python's int() handles whitespace
|
|
assert get_int("TEST_INT", 0) == 42
|
|
|
|
|
|
class TestGetBoolEdgeCases:
|
|
"""Additional edge case tests for get_bool."""
|
|
|
|
def test_mixed_case(self, monkeypatch):
|
|
"""Mixed case variants work."""
|
|
monkeypatch.setenv("TEST_BOOL", "TrUe")
|
|
assert get_bool("TEST_BOOL") is True
|
|
|
|
def test_with_spaces(self, monkeypatch):
|
|
"""Whitespace causes a non-match since get_bool does not strip."""
|
|
monkeypatch.setenv("TEST_BOOL", " yes ")
|
|
# .lower() doesn't strip, so " yes " != "yes"
|
|
# This will return False
|
|
assert get_bool("TEST_BOOL") is False
|
|
|
|
|
|
class TestConfigComplete:
|
|
"""Complete Config class tests."""
|
|
|
|
def test_all_connection_settings(self, clean_env, monkeypatch):
|
|
"""All connection settings are loaded."""
|
|
monkeypatch.setenv("MESH_TRANSPORT", "tcp")
|
|
monkeypatch.setenv("MESH_SERIAL_PORT", "/dev/ttyUSB0")
|
|
monkeypatch.setenv("MESH_SERIAL_BAUD", "9600")
|
|
monkeypatch.setenv("MESH_TCP_HOST", "192.168.1.1")
|
|
monkeypatch.setenv("MESH_TCP_PORT", "8080")
|
|
monkeypatch.setenv("MESH_BLE_ADDR", "AA:BB:CC:DD:EE:FF")
|
|
monkeypatch.setenv("MESH_BLE_PIN", "1234")
|
|
monkeypatch.setenv("MESH_DEBUG", "true")
|
|
|
|
config = Config()
|
|
|
|
assert config.mesh_transport == "tcp"
|
|
assert config.mesh_serial_port == "/dev/ttyUSB0"
|
|
assert config.mesh_serial_baud == 9600
|
|
assert config.mesh_tcp_host == "192.168.1.1"
|
|
assert config.mesh_tcp_port == 8080
|
|
assert config.mesh_ble_addr == "AA:BB:CC:DD:EE:FF"
|
|
assert config.mesh_ble_pin == "1234"
|
|
assert config.mesh_debug is True
|
|
|
|
def test_all_repeater_settings(self, clean_env, monkeypatch):
|
|
"""All repeater identity settings are loaded."""
|
|
monkeypatch.setenv("REPEATER_NAME", "HilltopRepeater")
|
|
monkeypatch.setenv("REPEATER_KEY_PREFIX", "abc123")
|
|
monkeypatch.setenv("REPEATER_PASSWORD", "secret")
|
|
monkeypatch.setenv("REPEATER_DISPLAY_NAME", "Hilltop Relay")
|
|
monkeypatch.setenv("REPEATER_PUBKEY_PREFIX", "!abc123")
|
|
monkeypatch.setenv("REPEATER_HARDWARE", "RAK4631 with Solar")
|
|
|
|
config = Config()
|
|
|
|
assert config.repeater_name == "HilltopRepeater"
|
|
assert config.repeater_key_prefix == "abc123"
|
|
assert config.repeater_password == "secret"
|
|
assert config.repeater_display_name == "Hilltop Relay"
|
|
assert config.repeater_pubkey_prefix == "!abc123"
|
|
assert config.repeater_hardware == "RAK4631 with Solar"
|
|
|
|
def test_all_timeout_settings(self, clean_env, monkeypatch):
|
|
"""All timeout and retry settings are loaded."""
|
|
monkeypatch.setenv("REMOTE_TIMEOUT_S", "30")
|
|
monkeypatch.setenv("REMOTE_RETRY_ATTEMPTS", "5")
|
|
monkeypatch.setenv("REMOTE_RETRY_BACKOFF_S", "10")
|
|
monkeypatch.setenv("REMOTE_CB_FAILS", "10")
|
|
monkeypatch.setenv("REMOTE_CB_COOLDOWN_S", "7200")
|
|
|
|
config = Config()
|
|
|
|
assert config.remote_timeout_s == 30
|
|
assert config.remote_retry_attempts == 5
|
|
assert config.remote_retry_backoff_s == 10
|
|
assert config.remote_cb_fails == 10
|
|
assert config.remote_cb_cooldown_s == 7200
|
|
|
|
def test_all_telemetry_settings(self, clean_env, monkeypatch):
|
|
"""All telemetry settings are loaded."""
|
|
monkeypatch.setenv("TELEMETRY_ENABLED", "yes")
|
|
monkeypatch.setenv("TELEMETRY_TIMEOUT_S", "20")
|
|
monkeypatch.setenv("TELEMETRY_RETRY_ATTEMPTS", "3")
|
|
monkeypatch.setenv("TELEMETRY_RETRY_BACKOFF_S", "5")
|
|
|
|
config = Config()
|
|
|
|
assert config.telemetry_enabled is True
|
|
assert config.telemetry_timeout_s == 20
|
|
assert config.telemetry_retry_attempts == 3
|
|
assert config.telemetry_retry_backoff_s == 5
|
|
|
|
def test_display_unit_system_defaults_to_metric(self, clean_env):
|
|
"""DISPLAY_UNIT_SYSTEM defaults to metric."""
|
|
config = Config()
|
|
assert config.display_unit_system == "metric"
|
|
|
|
def test_display_unit_system_accepts_imperial(self, clean_env, monkeypatch):
|
|
"""DISPLAY_UNIT_SYSTEM=imperial is honored."""
|
|
monkeypatch.setenv("DISPLAY_UNIT_SYSTEM", "imperial")
|
|
config = Config()
|
|
assert config.display_unit_system == "imperial"
|
|
|
|
def test_display_unit_system_invalid_falls_back_to_metric(self, clean_env, monkeypatch):
|
|
"""Invalid DISPLAY_UNIT_SYSTEM falls back to metric."""
|
|
monkeypatch.setenv("DISPLAY_UNIT_SYSTEM", "kelvin")
|
|
config = Config()
|
|
assert config.display_unit_system == "metric"
|
|
|
|
def test_all_location_settings(self, clean_env, monkeypatch):
|
|
"""All location/report settings are loaded."""
|
|
monkeypatch.setenv("REPORT_LOCATION_NAME", "Mountain Peak Observatory")
|
|
monkeypatch.setenv("REPORT_LOCATION_SHORT", "Mountain Peak")
|
|
monkeypatch.setenv("REPORT_LAT", "46.8523")
|
|
monkeypatch.setenv("REPORT_LON", "9.5369")
|
|
monkeypatch.setenv("REPORT_ELEV", "2500")
|
|
monkeypatch.setenv("REPORT_ELEV_UNIT", "ft")
|
|
|
|
config = Config()
|
|
|
|
assert config.report_location_name == "Mountain Peak Observatory"
|
|
assert config.report_location_short == "Mountain Peak"
|
|
assert config.report_lat == pytest.approx(46.8523)
|
|
assert config.report_lon == pytest.approx(9.5369)
|
|
assert config.report_elev == pytest.approx(2500)
|
|
assert config.report_elev_unit == "ft"
|
|
|
|
def test_all_radio_settings(self, clean_env, monkeypatch):
|
|
"""All radio configuration settings are loaded."""
|
|
monkeypatch.setenv("RADIO_FREQUENCY", "915.000 MHz")
|
|
monkeypatch.setenv("RADIO_BANDWIDTH", "125 kHz")
|
|
monkeypatch.setenv("RADIO_SPREAD_FACTOR", "SF12")
|
|
monkeypatch.setenv("RADIO_CODING_RATE", "CR5")
|
|
|
|
config = Config()
|
|
|
|
assert config.radio_frequency == "915.000 MHz"
|
|
assert config.radio_bandwidth == "125 kHz"
|
|
assert config.radio_spread_factor == "SF12"
|
|
assert config.radio_coding_rate == "CR5"
|
|
|
|
def test_companion_settings(self, clean_env, monkeypatch):
|
|
"""Companion display settings are loaded."""
|
|
monkeypatch.setenv("COMPANION_DISPLAY_NAME", "Base Station")
|
|
monkeypatch.setenv("COMPANION_PUBKEY_PREFIX", "!def456")
|
|
monkeypatch.setenv("COMPANION_HARDWARE", "T-Beam Supreme")
|
|
|
|
config = Config()
|
|
|
|
assert config.companion_display_name == "Base Station"
|
|
assert config.companion_pubkey_prefix == "!def456"
|
|
assert config.companion_hardware == "T-Beam Supreme"
|
|
|
|
|
|
class TestGetConfigSingleton:
|
|
"""Tests for get_config singleton behavior."""
|
|
|
|
def test_config_persists_across_calls(self, clean_env, monkeypatch):
|
|
"""Config values persist across multiple get_config calls."""
|
|
monkeypatch.setenv("MESH_TRANSPORT", "tcp")
|
|
|
|
config1 = get_config()
|
|
assert config1.mesh_transport == "tcp"
|
|
|
|
# Change env var - should NOT affect cached config
|
|
monkeypatch.setenv("MESH_TRANSPORT", "ble")
|
|
|
|
config2 = get_config()
|
|
assert config2.mesh_transport == "tcp" # Still tcp, cached
|
|
assert config1 is config2
|
|
|
|
def test_reset_allows_new_config(self, clean_env, monkeypatch):
|
|
"""Resetting singleton allows new config."""
|
|
monkeypatch.setenv("MESH_TRANSPORT", "tcp")
|
|
|
|
config1 = get_config()
|
|
assert config1.mesh_transport == "tcp"
|
|
|
|
# Reset singleton
|
|
import meshmon.env
|
|
meshmon.env._config = None
|
|
|
|
# Change env var
|
|
monkeypatch.setenv("MESH_TRANSPORT", "ble")
|
|
|
|
config2 = get_config()
|
|
assert config2.mesh_transport == "ble"
|
|
assert config1 is not config2
|