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

112 lines
4 KiB
Python

"""Tests for MoA trace aggregator-output capture across streaming modes.
The MoA full-turn trace (opt-in ``moa.save_traces``) must record the
aggregator's acting output whether the aggregator ran non-streaming (inline
capture at call time) or streaming (captured after the fact from the caller's
resolved assistant text). Before the streamed-capture fix, a streamed
aggregator left ``output: null`` in the trace and only pointed at state.db,
so an offline audit of a benchmark run (which drives the streaming display
path via ``hermes chat --query``) couldn't see what the aggregator actually
produced without joining to the session DB by hand.
These exercise the real ``consume_and_save_trace`` → ``save_moa_turn`` path
with real file I/O against a temp HERMES_HOME — no mocks on the write path.
"""
from __future__ import annotations
import json
import pytest
from agent.moa_loop import MoAChatCompletions
def _enable_traces(tmp_path, monkeypatch):
"""Point HERMES_HOME at a temp dir and turn moa.save_traces on."""
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
# save_moa_turn reads config via hermes_cli.config.load_config; stub it to
# return traces-on so the test doesn't depend on a real config file.
import agent.moa_trace as moa_trace
monkeypatch.setattr(
moa_trace,
"load_config",
lambda: {"moa": {"save_traces": True}},
raising=False,
)
# load_config is imported lazily inside _traces_enabled_and_dir; patch the
# source module attribute it imports from as well.
import hermes_cli.config as cfg
monkeypatch.setattr(
cfg, "load_config", lambda: {"moa": {"save_traces": True}}, raising=False
)
return hermes_home / "moa-traces"
def _make_completions_with_pending(streamed: bool, inline_output):
"""Build a MoAChatCompletions with a pending trace mimicking one turn."""
mc = MoAChatCompletions.__new__(MoAChatCompletions)
mc._pending_trace = {
"preset": "closed",
"reference_outputs": [], # references not under test here
"aggregator_label": "openrouter:anthropic/claude-opus-4.8",
"aggregator_slot": {
"model": "anthropic/claude-opus-4.8",
"provider": "openrouter",
},
"aggregator_temperature": 0.4,
"aggregator_input_messages": [
{"role": "system", "content": "sys"},
{"role": "user", "content": "do the thing"},
],
"aggregator_output": inline_output,
"aggregator_streamed": streamed,
}
return mc
def _read_single_trace(trace_dir, session_id):
path = trace_dir / f"{session_id}.jsonl"
assert path.exists(), f"trace file not written: {path}"
lines = path.read_text().strip().split("\n")
assert len(lines) == 1
return json.loads(lines[0])
def test_streamed_without_fallback_points_to_session_db(tmp_path, monkeypatch):
"""Streaming turn with no resolvable text falls back to the state.db pointer."""
trace_dir = _enable_traces(tmp_path, monkeypatch)
mc = _make_completions_with_pending(streamed=True, inline_output=None)
mc.consume_and_save_trace("sess_nofb", aggregator_output_fallback=None)
rec = _read_single_trace(trace_dir, "sess_nofb")
agg = rec["aggregator"]
assert agg["streamed"] is True
assert agg["output"] is None
assert agg["output_location"] == "assistant_message_in_session_db"
def test_pending_trace_cleared_after_flush(tmp_path, monkeypatch):
"""A second flush is a no-op (pending cleared) — never double-writes."""
trace_dir = _enable_traces(tmp_path, monkeypatch)
mc = _make_completions_with_pending(streamed=True, inline_output=None)
mc.consume_and_save_trace("sess_once", aggregator_output_fallback="x")
# Second call: pending is None now, must not append a second line.
mc.consume_and_save_trace("sess_once", aggregator_output_fallback="y")
path = trace_dir / "sess_once.jsonl"
lines = path.read_text().strip().split("\n")
assert len(lines) == 1