hermes-agent/tests/agent/test_skill_invocation_description.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

133 lines
5.2 KiB
Python

"""describe_skill_invocation() — recovering what the user typed from a /skill turn.
A /skill invocation expands into a message that embeds the whole skill body. Any
surface that summarizes a user turn from its raw content (session titles, sidebar
previews, the /rewind picker) otherwise shows the SKILL's prose as if the user had
written it.
Every case here builds the scaffolding with the real message builders rather than
a hand-written literal, so the description can't silently drift from the format it
parses.
"""
import pytest
import agent.skill_bundles as skill_bundles
import agent.skill_commands as skill_commands
import tools.skills_tool as skills_tool
from agent.skill_commands import (
SKILL_EXCERPT_JOINT,
SKILL_SCAFFOLD_SQL_LIKE,
describe_skill_invocation,
)
SKILL_BODY = "Kick off a task in a fresh isolated git worktree instead of the current checkout."
def _write_skill(skills_dir, name, body=SKILL_BODY):
skill_dir = skills_dir / name
skill_dir.mkdir(parents=True, exist_ok=True)
(skill_dir / "SKILL.md").write_text(
f"---\nname: {name}\ndescription: Description for {name}\n---\n\n# {name}\n\n{body}\n"
)
return skill_dir
def _write_bundle(bundles_dir, slug, skills):
bundles_dir.mkdir(parents=True, exist_ok=True)
lines = [f"name: {slug}", "skills:"]
lines.extend(f" - {skill}" for skill in skills)
(bundles_dir / f"{slug}.yaml").write_text("\n".join(lines) + "\n")
@pytest.fixture()
def skills(tmp_path, monkeypatch):
"""Install a 'work' skill and a 'demo' bundle, with caches reset."""
skills_dir = tmp_path / "skills"
bundles_dir = tmp_path / "skill-bundles"
_write_skill(skills_dir, "work")
_write_skill(skills_dir, "clean", body="Polish your own diff by hand.")
_write_bundle(bundles_dir, "demo", ["work", "clean"])
monkeypatch.setattr(skills_tool, "SKILLS_DIR", skills_dir)
monkeypatch.setenv("HERMES_BUNDLES_DIR", str(bundles_dir))
monkeypatch.setattr(skill_commands, "_skill_commands", {})
monkeypatch.setattr(skill_commands, "_skill_commands_platform", None)
monkeypatch.setattr(skill_bundles, "_bundles_cache", {})
monkeypatch.setattr(skill_bundles, "_bundles_cache_mtime", None)
skill_commands.scan_skill_commands()
skill_bundles.scan_bundles()
return skills_dir
class TestDescribeSkillInvocation:
def test_ignores_non_string_content(self):
assert describe_skill_invocation(None) is None
assert describe_skill_invocation([{"type": "text", "text": "hi"}]) is None
def test_recovers_the_typed_instruction(self, skills):
message = skill_commands.build_skill_invocation_message(
"/work", user_instruction="fix the title leak"
)
assert describe_skill_invocation(message) == "/work — fix the title leak"
def test_bundle_carries_its_typed_keys(self, skills):
result = skill_bundles.build_bundle_invocation_message(
"/demo", user_instruction="fix the title leak"
)
assert result is not None
message, _, _ = result
described = describe_skill_invocation(message)
assert described.endswith("— fix the title leak")
assert "worktree" not in described
class TestExcerptedScaffolding:
"""Preview queries hand over a head+tail excerpt, not the whole message."""
def _excerpt(self, message, window=400):
"""Mirror what _preview_raw_select() hands to _shape_preview()."""
flat = message.replace("\n", " ").replace("\r", " ")
if len(message) <= window * 2:
return flat[: window * 2]
return flat[:window] + SKILL_EXCERPT_JOINT + flat[-window:]
def test_excerpt_still_recovers_the_instruction(self, skills):
message = skill_commands.build_skill_invocation_message(
"/work", user_instruction="fix the title leak"
)
described = describe_skill_invocation(self._excerpt(message))
assert described == "/work — fix the title leak"
def test_description_never_runs_across_the_joint(self, skills):
# A long body pushes the head window into the middle of the skill text;
# the instruction is only present on the tail side.
skill_md = skills / "work" / "SKILL.md"
skill_md.write_text(skill_md.read_text().replace(SKILL_BODY, "filler line.\n" * 200))
skill_commands._skill_commands = {}
skill_commands._skill_commands_platform = None
skill_commands.scan_skill_commands()
message = skill_commands.build_skill_invocation_message(
"/work", user_instruction="fix the title leak"
)
described = describe_skill_invocation(self._excerpt(message))
assert SKILL_EXCERPT_JOINT not in described
assert "filler line" not in described
class TestSqlLikePattern:
def test_matches_the_prefix_the_builders_emit(self, skills):
message = skill_commands.build_skill_invocation_message("/work")
assert message.startswith(SKILL_SCAFFOLD_SQL_LIKE.rstrip("%"))
def test_carries_no_like_wildcards_needing_escape(self):
# The pattern is interpolated into SQL without an ESCAPE clause, so the
# literal part must not contain '%' or '_'.
assert "%" not in SKILL_SCAFFOLD_SQL_LIKE[:-1]
assert "_" not in SKILL_SCAFFOLD_SQL_LIKE[:-1]