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

92 lines
3.2 KiB
Python

"""Tests for session reset completeness (fixes #2635).
/clear and /new must not carry stale state into the next session.
Two fields were added after reset_session_state() was written and were
therefore never cleared:
- ContextCompressor._previous_summary
- AIAgent._user_turn_count
"""
import sys
import types
from pathlib import Path
# Ensure repo root is importable
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
# Stub out optional heavy dependencies not installed in the test environment
sys.modules.setdefault("fire", types.SimpleNamespace(Fire=lambda *a, **k: None))
sys.modules.setdefault("firecrawl", types.SimpleNamespace(Firecrawl=object))
sys.modules.setdefault("fal_client", types.SimpleNamespace())
from run_agent import AIAgent
from agent.context_compressor import ContextCompressor
def _make_minimal_agent() -> AIAgent:
"""Return an AIAgent constructed with the absolute minimum args.
We pass dummy values that bypass network calls and filesystem access.
The object is never used to make API calls — only its attributes and
reset_session_state() are exercised.
"""
agent = AIAgent.__new__(AIAgent) # skip __init__ entirely
# Seed the exact attributes that reset_session_state() writes
agent.session_total_tokens = 0
agent.session_input_tokens = 0
agent.session_output_tokens = 0
agent.session_prompt_tokens = 0
agent.session_completion_tokens = 0
agent.session_cache_read_tokens = 0
agent.session_cache_write_tokens = 0
agent.session_reasoning_tokens = 0
agent.session_api_calls = 0
agent.session_estimated_cost_usd = 0.0
agent.session_cost_status = "unknown"
agent.session_cost_source = "none"
# The two fields under test
agent._user_turn_count = 0
agent.context_compressor = None # will be set per-test as needed
return agent
class TestResetSessionState:
"""reset_session_state() must clear ALL session-scoped state."""
def test_previous_summary_cleared_on_reset(self):
"""Compression summary from old session must not leak into new session."""
agent = _make_minimal_agent()
compressor = ContextCompressor.__new__(ContextCompressor)
compressor._previous_summary = "Old session summary about unrelated topic"
# Seed counter attributes that reset_session_state touches
compressor.last_prompt_tokens = 100
compressor.last_completion_tokens = 50
compressor.last_total_tokens = 150
compressor.compression_count = 3
compressor._context_probed = True
agent.context_compressor = compressor
agent.reset_session_state()
assert compressor._previous_summary is None, (
"_previous_summary must be None after reset; got: "
f"{compressor._previous_summary!r}"
)
def test_user_turn_count_cleared_on_reset(self):
"""Turn counter must reset to 0 on new session."""
agent = _make_minimal_agent()
agent._user_turn_count = 7 # simulates turns accumulated in previous session
agent.context_compressor = None
agent.reset_session_state()
assert agent._user_turn_count == 0, (
f"_user_turn_count must be 0 after reset; got: {agent._user_turn_count}"
)