Files
hermes-agent/tests/hermes_cli/test_config_effective.py
teknium1 5aa1a50c57 fix(config): good-config backup only for the active home; fixture-based effective-config contract
- load_user_config_effective wrote backups/config/*.good.* into ANY home it
  read, so doctor and the TUI cwd lookup created backup dirs inside other
  profiles. The copy is now taken only when the path is the active home's
  config (the only home load_config ever backed up).
- The effective-config test re-composed the implementation's own primitives
  (a mirror); it now pins a literal expected dict for a fixture of user file +
  managed overlay + env, and fail_closed asserts yaml.YAMLError.
- Four repointed gateway tests carried duplicate _load_gateway_config setattr
  lines (one silently overriding the other); deduped to the intended dict.
2026-09-13 05:09:06 -07:00

125 lines
4.3 KiB
Python

"""Invariants for ``hermes_cli.config_effective.load_user_config_effective`` — the one loader every
defaults-free config reader (gateway runtime, TUI gateway, cron, ``hermes send`` bridge, doctor,
bootstrap modules) goes through."""
import textwrap
import pytest
import yaml
@pytest.fixture
def homes(tmp_path, monkeypatch):
home = tmp_path / "home"
home.mkdir()
managed = tmp_path / "managed"
managed.mkdir()
monkeypatch.setenv("HERMES_HOME", str(home))
monkeypatch.setenv("HERMES_MANAGED_DIR", str(managed))
monkeypatch.setenv("FIXTURE_USER_KEY", "user-secret")
monkeypatch.setenv("FIXTURE_MANAGED_URL", "https://managed.example")
_reset_caches()
return home, managed
def _reset_caches():
import hermes_cli.config as cfg
from hermes_cli import config_effective, managed_scope
cfg._LOAD_CONFIG_CACHE.clear()
cfg._RAW_CONFIG_CACHE.clear()
config_effective._EFFECTIVE_CACHE.clear()
config_effective._LAST_GOOD_USER_RAW.clear()
managed_scope.invalidate_managed_cache()
def _write(path, body):
path.write_text(textwrap.dedent(body), encoding="utf-8")
_reset_caches()
USER_YAML = """
model:
name: user/model
api_key: ${FIXTURE_USER_KEY}
provider: custom
display:
skin: user-skin
"""
MANAGED_YAML = """
model:
base_url: ${FIXTURE_MANAGED_URL}
display:
skin: managed-skin
"""
def test_effective_is_user_plus_managed_plus_env_with_no_defaults(homes):
"""Contract as a fixture: given user config.yaml X, managed overlay Y and env Z, the effective
dict is exactly this literal — ``${VAR}`` expanded on both layers, managed keys winning,
root ``provider`` migrated under ``model``, and no DEFAULT_CONFIG key introduced (a missing
key stays missing). Per-message gateway reads (and the system prompt built from them) are
pinned by this shape, not by re-running the implementation's primitives."""
from hermes_cli.config import DEFAULT_CONFIG
from hermes_cli.config_effective import load_user_config_effective
home, managed = homes
_write(home / "config.yaml", USER_YAML)
_write(managed / "config.yaml", MANAGED_YAML)
effective = load_user_config_effective(home / "config.yaml")
assert effective == {
"model": {
"default": "user/model",
"provider": "custom",
"api_key": "user-secret",
"base_url": "https://managed.example",
},
"display": {"skin": "managed-skin"},
}
assert "agent" in DEFAULT_CONFIG # would be present if defaults had been merged
def test_broken_yaml_serves_last_good_and_fail_closed_raises(homes):
"""A torn mid-edit write must not silently drop user overrides: the fail-open path serves the last
successfully parsed user file through the same pipeline; ``fail_closed`` surfaces the error to
callers that keep their own last-good state."""
from hermes_cli.config_effective import load_user_config_effective
home, _ = homes
_write(home / "config.yaml", USER_YAML)
good = load_user_config_effective(home / "config.yaml")
(home / "config.yaml").write_text("model: [unterminated", encoding="utf-8")
_reset_caches_keep_last_good()
assert load_user_config_effective(home / "config.yaml") == good
with pytest.raises(yaml.YAMLError): # the type _refresh_fallback_model's own last-good path keys on
load_user_config_effective(home / "config.yaml", fail_closed=True)
def test_good_backup_is_written_only_for_the_active_home(homes, tmp_path):
"""Reading ANOTHER profile's config (doctor, TUI cwd lookup) is a read: it must not create
``backups/config/`` inside that profile. The active home keeps the last-good copy."""
from hermes_cli.config_effective import load_user_config_effective
home, _ = homes
other = tmp_path / "other-profile"
other.mkdir()
_write(home / "config.yaml", USER_YAML)
_write(other / "config.yaml", USER_YAML)
load_user_config_effective(other / "config.yaml")
load_user_config_effective(home / "config.yaml")
assert not (other / "backups").exists()
assert list((home / "backups" / "config").glob("config.yaml.good.*"))
def _reset_caches_keep_last_good():
import hermes_cli.config as cfg
from hermes_cli import config_effective
cfg._RAW_CONFIG_CACHE.clear()
config_effective._EFFECTIVE_CACHE.clear()