Files
timmy-config/tests/test_config_template.py

134 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Tests for config_template.py — issue #696"""
import os
import sys
import tempfile
import pytest
from pathlib import Path
# Add parent dir for import
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
from config_template import ConfigTemplate, _deep_merge, _get_dotted, _diff_dicts, _validate_config
@pytest.fixture
def tmp_config_dir(tmp_path):
"""Create a temp config dir with base + overlay files."""
import yaml
base = {
"model": {"name": "base-model", "temperature": 0.7},
"cron": {"enabled": False, "interval": 300},
"display": {"colors": True},
}
overlay = {
"model": {"temperature": 0.9},
"cron": {"enabled": True},
"logging": {"level": "DEBUG"},
}
with open(tmp_path / "base.yaml", "w") as f:
yaml.dump(base, f)
with open(tmp_path / "test.overlay.yaml", "w") as f:
yaml.dump(overlay, f)
return tmp_path
class TestDeepMerge:
def test_overlay_wins(self):
base = {"a": 1, "b": 2}
overlay = {"b": 99}
result = _deep_merge(base, overlay)
assert result == {"a": 1, "b": 99}
def test_deep_merge_nested(self):
base = {"model": {"name": "x", "temp": 0.7}}
overlay = {"model": {"temp": 0.9}}
result = _deep_merge(base, overlay)
assert result["model"]["name"] == "x"
assert result["model"]["temp"] == 0.9
def test_new_keys_added(self):
base = {"a": 1}
overlay = {"b": 2}
result = _deep_merge(base, overlay)
assert result == {"a": 1, "b": 2}
def test_originals_unchanged(self):
base = {"a": {"inner": 1}}
overlay = {"a": {"inner": 99}}
_deep_merge(base, overlay)
assert base["a"]["inner"] == 1
class TestDottedAccess:
def test_simple_key(self):
assert _get_dotted({"a": 1}, "a") == 1
def test_nested_key(self):
assert _get_dotted({"a": {"b": {"c": 42}}}, "a.b.c") == 42
def test_missing_key_returns_default(self):
assert _get_dotted({"a": 1}, "x", "fallback") == "fallback"
def test_partial_path(self):
assert _get_dotted({"a": 1}, "a.b.c", None) is None
class TestDiff:
def test_no_diff(self):
assert _diff_dicts({"a": 1}, {"a": 1}) == []
def test_changed_value(self):
diffs = _diff_dicts({"a": 1}, {"a": 2})
assert len(diffs) == 1
assert diffs[0]["type"] == "changed"
def test_added_key(self):
diffs = _diff_dicts({"a": 1}, {"a": 1, "b": 2})
added = [d for d in diffs if d["type"] == "added_in_overlay"]
assert len(added) == 1
assert added[0]["key"] == "b"
class TestValidation:
def test_valid_config(self):
config = {"model": {"name": "x"}, "provider": {"name": "y"}}
assert _validate_config(config) == []
def test_missing_model(self):
warnings = _validate_config({"provider": {}})
assert any("model" in w for w in warnings)
class TestConfigTemplate:
def test_load(self, tmp_config_dir):
t = ConfigTemplate(str(tmp_config_dir))
merged = t.load("test")
assert merged["model"]["name"] == "base-model"
assert merged["model"]["temperature"] == 0.9
assert merged["cron"]["enabled"] is True
assert merged["logging"]["level"] == "DEBUG"
def test_get_dotted(self, tmp_config_dir):
t = ConfigTemplate(str(tmp_config_dir))
t.load("test")
assert t.get("model.temperature") == 0.9
assert t.get("nonexistent", "default") == "default"
def test_diff(self, tmp_config_dir):
t = ConfigTemplate(str(tmp_config_dir))
t.load("test")
diffs = t.diff()
assert len(diffs) > 0
def test_unknown_env_raises(self, tmp_config_dir):
t = ConfigTemplate(str(tmp_config_dir))
with pytest.raises(ValueError, match="Unknown environment"):
t.load("nonexistent")
def test_list_environments(self):
envs = ConfigTemplate.list_environments()
assert "dev" in envs
assert "prod" in envs
assert "cron" in envs
assert "gateway" in envs