871a3cc174
- Add config.py with multi-source config (YAML, .env, env vars) - Add .env.example and config.yaml.example - Add settings API endpoints (GET/POST /api/v1/settings) - Add status endpoint (GET /api/v1/status) with hardware subsystem check - Add settings screen to web client (protected by API token) - Add localization strings for settings UI - Add tests for config module and new endpoints - Update README with config documentation
135 lines
3.9 KiB
Python
135 lines
3.9 KiB
Python
"""Tests for the configuration module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from ..config import Config
|
|
|
|
|
|
@pytest.fixture
|
|
def config() -> Config:
|
|
return Config()
|
|
|
|
|
|
class TestConfigDefaults:
|
|
def test_defaults(self, config: Config) -> None:
|
|
assert config.dashboard_pin == ""
|
|
assert config.api_token == ""
|
|
assert config.host == "0.0.0.0"
|
|
assert config.port == 8921
|
|
assert config.log_level == "INFO"
|
|
|
|
|
|
class TestConfigLoad:
|
|
def test_load_from_env(self, config: Config) -> None:
|
|
os.environ["DASHBOARD_PIN"] = "5678"
|
|
os.environ["API_TOKEN"] = "test-token"
|
|
os.environ["HOST"] = "127.0.0.1"
|
|
os.environ["PORT"] = "9000"
|
|
os.environ["LOG_LEVEL"] = "DEBUG"
|
|
|
|
config.load()
|
|
|
|
assert config.dashboard_pin == "5678"
|
|
assert config.api_token == "test-token"
|
|
assert config.host == "127.0.0.1"
|
|
assert config.port == 9000
|
|
assert config.log_level == "DEBUG"
|
|
|
|
# Cleanup
|
|
del os.environ["DASHBOARD_PIN"]
|
|
del os.environ["API_TOKEN"]
|
|
del os.environ["HOST"]
|
|
del os.environ["PORT"]
|
|
del os.environ["LOG_LEVEL"]
|
|
|
|
def test_load_from_env_file(self, config: Config) -> None:
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".env", delete=False) as f:
|
|
f.write("DASHBOARD_PIN=1111\n")
|
|
f.write("API_TOKEN=env-token\n")
|
|
f.write("# Comment\n")
|
|
f.write("HOST=0.0.0.0\n")
|
|
f.write("PORT=8921\n")
|
|
env_file = f.name
|
|
|
|
try:
|
|
config._load_env(Path(env_file))
|
|
assert config.dashboard_pin == "1111"
|
|
assert config.api_token == "env-token"
|
|
finally:
|
|
os.unlink(env_file)
|
|
|
|
def test_load_from_yaml_file(self, config: Config) -> None:
|
|
"""Test YAML loading (requires PyYAML)."""
|
|
try:
|
|
import yaml # noqa: F401
|
|
except ImportError:
|
|
pytest.skip("PyYAML not installed")
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
|
yaml.dump( # type: ignore[possibly-undefined]
|
|
{
|
|
"dashboard_pin": "2222",
|
|
"api_token": "yaml-token",
|
|
"host": "0.0.0.0",
|
|
"port": 8921,
|
|
"log_level": "INFO",
|
|
},
|
|
f,
|
|
)
|
|
yaml_file = f.name
|
|
|
|
try:
|
|
config._load_yaml(Path(yaml_file))
|
|
assert config.dashboard_pin == "2222"
|
|
assert config.api_token == "yaml-token"
|
|
finally:
|
|
os.unlink(yaml_file)
|
|
|
|
|
|
class TestConfigSave:
|
|
def test_save_to_yaml(self, config: Config) -> None:
|
|
"""Test YAML saving (requires PyYAML)."""
|
|
try:
|
|
import yaml # noqa: F401
|
|
except ImportError:
|
|
pytest.skip("PyYAML not installed")
|
|
|
|
config.dashboard_pin = "3333"
|
|
config.api_token = "save-token"
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
|
save_path = f.name
|
|
|
|
try:
|
|
config.save(Path(save_path))
|
|
|
|
with open(save_path) as f:
|
|
data = yaml.safe_load(f) # type: ignore[possibly-undefined]
|
|
|
|
assert data["dashboard_pin"] == "3333"
|
|
assert data["api_token"] == "save-token"
|
|
finally:
|
|
os.unlink(save_path)
|
|
|
|
|
|
class TestConfigToDict:
|
|
def test_to_dict(self, config: Config) -> None:
|
|
config.dashboard_pin = "4444"
|
|
config.api_token = "dict-token"
|
|
config.host = "127.0.0.1"
|
|
config.port = 9000
|
|
config.log_level = "DEBUG"
|
|
|
|
d = config.to_dict()
|
|
assert d["dashboard_pin"] == "4444"
|
|
assert d["api_token"] == "dict-token"
|
|
assert d["host"] == "127.0.0.1"
|
|
assert d["port"] == 9000
|
|
assert d["log_level"] == "DEBUG"
|