hermes-agent/tests/agent/test_learning_mutations.py
Teknium 6b81590c55
test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions
Systematic prune per AGENTS.md test policy, one pass over every major
test tree (gateway, hermes_cli, tools, agent, run_agent, plugins, cli,
cron, tui_gateway, honcho/openviking, root-level):

- DELETE: source-reading tests (read_text/getsource on prod files),
  change-detector tests (exact catalog counts, model-name snapshots,
  config version literals), mock-echo tests (assert a mock returns what
  it was told), assertion-free/trivial tests, near-duplicate
  parametrizations (boundaries + one representative kept), async/sync
  twin duplicates, cosmetic within-file variations.
- KEEP (mandatory): security/redaction/approval guards, message-role
  alternation invariants, prompt-caching/deterministic-call-id
  invariants, issue-number regression tests (deduped), E2E tests.
- 6 test files deleted outright (script-style/no-assert or fully
  redundant); conftest.py, fakes/, fixtures/ untouched.
- tests/acp/conftest.py added: autouse fixture stubs the live
  models.dev/GitHub/Copilot/Anthropic inventory fetches that ACP server
  tests performed on every session create — test_server.py 147s → 3.4s,
  and the tests are now genuinely hermetic.
- Sleep-based slowness shrunk where safe (codex_ttfb_watchdog,
  compression_concurrent_fork, etc.); no wall-clock assertion tightened.

Verification: full hermetic suite via scripts/run_tests.sh —
2439 files, 31,130 tests passed, 0 failed, 0 flaky retries, 315s wall
(baseline: 583s wall, 13,564s subprocess CPU).
2026-07-29 13:10:23 -07:00

93 lines
2.4 KiB
Python

"""Behavior contracts for journey node edit/delete (agent.learning_mutations).
Exercises the real on-disk resolution (skills dir + MEMORY.md/USER.md chunking)
against a temp HERMES_HOME, never mocks — the id→file mapping is the whole point.
"""
from __future__ import annotations
import pytest
from agent import learning_mutations as lm
from hermes_constants import get_hermes_home
_SKILL = """---
name: my-skill
description: A test skill.
---
# My Skill
Body.
"""
@pytest.fixture
def home():
base = get_hermes_home()
(base / "memories").mkdir(parents=True, exist_ok=True)
(base / "memories" / "MEMORY.md").write_text("alpha note\nline two\n§\nbeta note", encoding="utf-8")
(base / "memories" / "USER.md").write_text("user profile note", encoding="utf-8")
skill = base / "skills" / "my-skill"
skill.mkdir(parents=True, exist_ok=True)
(skill / "SKILL.md").write_text(_SKILL, encoding="utf-8")
return base
def test_parse_node_kind():
assert lm.parse_node_kind("memory:memory:0") == "memory"
assert lm.parse_node_kind("memory:profile:3") == "memory"
assert lm.parse_node_kind("debugging-hermes") == "skill"
def test_edit_memory_replaces_chunk(home):
assert lm.edit_node("memory:profile:2", "rewritten profile")["ok"]
assert (home / "memories" / "USER.md").read_text(encoding="utf-8").strip() == "rewritten profile"
def test_skill_detail_returns_skill_md(home):
d = lm.node_detail("my-skill")
assert d["ok"] and d["kind"] == "skill"
assert "name: my-skill" in d["content"]
def test_delete_pinned_skill_refused(home):
from tools import skill_usage
skill_usage.set_pinned("my-skill", True)
res = lm.delete_node("my-skill")
assert not res["ok"]
assert "pinned" in res["message"]
assert (home / "skills" / "my-skill").exists()
def test_memory_writes_match_memory_tool_format(home):
"""A journey mutation must leave the file byte-identical to what the memory
tool itself writes — same §-join, no trailing-newline drift — so the two
surfaces never fight over format and indices stay aligned."""
from tools.memory_tool import ENTRY_DELIMITER, MemoryStore
assert lm.edit_node("memory:memory:0", "alpha rewritten")["ok"]
path = home / "memories" / "MEMORY.md"
entries = MemoryStore._read_file(path)
assert entries == ["alpha rewritten", "beta note"]
assert path.read_text(encoding="utf-8") == ENTRY_DELIMITER.join(entries)