from __future__ import annotations import importlib.util import json from pathlib import Path import yaml ROOT = Path(__file__).resolve().parents[1] PATCH_PATH = ROOT / "hermes-agent" / "patches" / "enforce_leoclean_learning_policy.py" def _load_module(): spec = importlib.util.spec_from_file_location("enforce_leoclean_learning_policy", PATCH_PATH) assert spec and spec.loader module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def test_learning_policy_disables_automatic_reviews_without_removing_explicit_tools(tmp_path: Path) -> None: module = _load_module() config_path = tmp_path / "config.yaml" original = """model: provider: openrouter gateway: telegram: bot_token: private-test-token memory: memory_enabled: true user_profile_enabled: true nudge_interval: 10 skills: external_dirs: [] creation_nudge_interval: 10 """ config_path.write_text(original, encoding="utf-8") config_path.chmod(0o600) result, code = module.enforce_policy(config_path) assert code == 0 assert result["status"] == "updated" assert result["policy"] == { "memory_nudge_interval": 0, "skill_creation_nudge_interval": 0, } assert "private-test-token" not in json.dumps(result) updated = yaml.safe_load(config_path.read_text(encoding="utf-8")) assert updated["memory"] == { "memory_enabled": True, "user_profile_enabled": True, "nudge_interval": 0, } assert updated["skills"] == {"external_dirs": [], "creation_nudge_interval": 0} assert updated["gateway"]["telegram"]["bot_token"] == "private-test-token" assert config_path.stat().st_mode & 0o777 == 0o600 assert config_path.with_name(config_path.name + module.BACKUP_SUFFIX).read_text(encoding="utf-8") == original check_result, check_code = module.enforce_policy(config_path, check=True) assert check_code == 0 assert check_result["status"] == "compliant" def test_learning_policy_check_reports_drift_without_writing(tmp_path: Path) -> None: module = _load_module() config_path = tmp_path / "config.yaml" original = "memory:\n memory_enabled: true\n" config_path.write_text(original, encoding="utf-8") result, code = module.enforce_policy(config_path, check=True) assert code == 1 assert result["status"] == "change_required" assert config_path.read_text(encoding="utf-8") == original assert not config_path.with_name(config_path.name + module.BACKUP_SUFFIX).exists()