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

223 lines
8.7 KiB
Python

"""Regression tests for the compressed-summary metadata flag (#38389).
The compressor marks summary messages with ``COMPRESSED_SUMMARY_METADATA_KEY``
so frontends (CLI, Desktop, gateway, TUI) can distinguish them from real
assistant/user messages without content-prefix heuristics.
Two invariants:
1. The flag is present on exactly the summary-bearing message after compress()
(standalone insertion AND merge-into-tail).
2. The key is underscore-prefixed so the chat-completions wire sanitizer
strips it — strict gateways (Fireworks, Mistral, Moonshot/Kimi,
opencode-go) reject unknown message keys with "Extra inputs are not
permitted", poisoning the session.
"""
from unittest.mock import MagicMock, patch
import pytest
from agent.context_compressor import (
COMPRESSED_SUMMARY_HAS_USER_TURN_KEY,
COMPRESSED_SUMMARY_METADATA_KEY,
ContextCompressor,
)
def _make_compressor():
with patch(
"agent.context_compressor.get_model_context_length", return_value=8000
):
return ContextCompressor(
model="test-model", quiet_mode=True, config_context_length=8000
)
def _make_messages(n_turns=30):
msgs = [{"role": "system", "content": "sys"}]
for i in range(n_turns):
msgs.append({"role": "user", "content": f"question {i} " + "x" * 400})
msgs.append({"role": "assistant", "content": f"answer {i} " + "y" * 400})
return msgs
def _compress(cc, msgs):
resp = MagicMock()
resp.choices[0].message.content = "## Active Task\nstuff"
with patch("agent.context_compressor.call_llm", return_value=resp):
return cc.compress(msgs, current_tokens=100_000, force=True)
class TestMetadataFlagSet:
def test_exactly_one_flagged_message_after_compress(self):
cc = _make_compressor()
out = _compress(cc, _make_messages())
flagged = [
m for m in out
if isinstance(m, dict) and m.get(COMPRESSED_SUMMARY_METADATA_KEY)
]
assert len(flagged) == 1
# The flagged message is the one carrying the compaction handoff.
assert "[CONTEXT COMPACTION" in flagged[0]["content"]
def test_helper_detects_flag(self):
assert ContextCompressor._has_compressed_summary_metadata(
{COMPRESSED_SUMMARY_METADATA_KEY: True}
)
assert not ContextCompressor._has_compressed_summary_metadata(
{"role": "assistant", "content": "hi"}
)
assert not ContextCompressor._has_compressed_summary_metadata("not a dict")
assert not ContextCompressor._has_compressed_summary_metadata(None)
class TestMetadataFlagNeverReachesWire:
def test_key_is_underscore_prefixed(self):
"""The wire sanitizers strip every top-level message key starting
with '_'. A bare key would reach strict gateways (Fireworks etc.)
and 400 with 'Extra inputs are not permitted'."""
assert COMPRESSED_SUMMARY_METADATA_KEY.startswith("_")
assert COMPRESSED_SUMMARY_HAS_USER_TURN_KEY.startswith("_")
def test_chat_completions_transport_strips_flag(self):
from agent.transports.chat_completions import ChatCompletionsTransport
cc = _make_compressor()
out = _compress(cc, _make_messages())
wire = ChatCompletionsTransport().convert_messages(out, model="some-model")
assert not any(
isinstance(m, dict)
and (
COMPRESSED_SUMMARY_METADATA_KEY in m
or COMPRESSED_SUMMARY_HAS_USER_TURN_KEY in m
)
for m in wire
)
# Sanitization must not destroy the in-process flag on the originals.
assert any(
isinstance(m, dict) and m.get(COMPRESSED_SUMMARY_METADATA_KEY)
for m in out
)
class TestClassifySummaryContent:
"""classify_summary_content distinguishes standalone handoffs from
merge-into-tail messages so wire consumers (ACP replay) can flag them
differently — collapsing a merged message would hide the preserved
tail content that precedes the summary."""
def test_standalone_summary(self):
from agent.context_compressor import SUMMARY_PREFIX
content = SUMMARY_PREFIX + "\n## Active Task\nstuff"
assert ContextCompressor.classify_summary_content(content) == "standalone"
assert ContextCompressor._is_context_summary_content(content) is True
def test_merged_tail_summary(self):
from agent.context_compressor import (
SUMMARY_PREFIX,
_MERGED_PRIOR_CONTEXT_HEADER,
_MERGED_SUMMARY_DELIMITER,
_SUMMARY_END_MARKER,
)
merged = (
_MERGED_PRIOR_CONTEXT_HEADER + "\n"
"old tail content\n\n"
+ _MERGED_SUMMARY_DELIMITER + "\n\n"
+ SUMMARY_PREFIX + "\nBODY\n\n"
+ _SUMMARY_END_MARKER
)
assert ContextCompressor.classify_summary_content(merged) == "merged"
assert ContextCompressor._is_context_summary_content(merged) is True
class TestClassifyAgreesWithPredicatesOnLiveEmissions:
"""Behavior contract: classify_summary_content must agree with the
boolean summary predicates (``_is_context_summary_content`` and the
module-level ``is_compaction_summary_message``) on every handoff shape
the CURRENT compressor actually emits — not just hand-built fixtures.
If the emission format drifts (prefix rewording, new merge framing),
these tests fail on the real output rather than on a stale snapshot,
signalling that ACP replay flagging and the internal summary detectors
have diverged.
"""
@staticmethod
def _live_compress(msgs):
# Match the emission-probe compressor shape (wide context, minimal
# protection) so the transcript geometry — not protection budgets —
# decides the merge-vs-standalone path deterministically.
with patch(
"agent.context_compressor.get_model_context_length",
return_value=100_000,
):
cc = ContextCompressor(
model="test-model",
threshold_percent=0.85,
protect_first_n=1,
protect_last_n=1,
quiet_mode=True,
)
out = _compress(cc, msgs)
flagged = [
m for m in out
if isinstance(m, dict) and m.get(COMPRESSED_SUMMARY_METADATA_KEY)
]
return flagged
@staticmethod
def _assert_agreement(message):
from agent.context_compressor import is_compaction_summary_message
kind = ContextCompressor.classify_summary_content(message.get("content"))
detected = ContextCompressor._is_context_summary_content(
message.get("content")
)
assert (kind is not None) == detected
# is_compaction_summary_message also honors the metadata flag, so it
# must detect every message classify flags — and on a flag-stripped
# copy (the DB-reload shape) content classification alone must carry.
assert is_compaction_summary_message(message) is True
stripped = {
k: v for k, v in message.items()
if k != COMPRESSED_SUMMARY_METADATA_KEY
}
assert is_compaction_summary_message(stripped) == (kind is not None)
return kind
def test_merged_emission_classifies_merged_and_predicates_agree(self):
"""Alternating transcripts take the merge-into-tail path on current
main — the emitted handoff must classify 'merged'."""
flagged = self._live_compress(_make_messages())
assert len(flagged) == 1
kind = self._assert_agreement(flagged[0])
assert kind == "merged"
def test_standalone_emission_classifies_standalone_and_predicates_agree(self):
"""A degenerate all-user transcript forces the standalone-insertion
path — the emitted handoff must classify 'standalone'."""
msgs = [{"role": "system", "content": "sys"}]
msgs.extend(
{"role": "user", "content": f"user only {i} " + "x" * 400}
for i in range(60)
)
flagged = self._live_compress(msgs)
assert len(flagged) == 1
kind = self._assert_agreement(flagged[0])
assert kind == "standalone"
def test_non_summary_messages_agree_on_none(self):
from agent.context_compressor import is_compaction_summary_message
for msg in (
{"role": "user", "content": "plain question"},
{"role": "assistant", "content": ""},
{"role": "assistant", "content": None},
):
assert ContextCompressor.classify_summary_content(msg["content"]) is None
assert ContextCompressor._is_context_summary_content(msg["content"]) is False
assert is_compaction_summary_message(msg) is False