mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-30 19:09:28 +00:00
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).
146 lines
5.1 KiB
Python
146 lines
5.1 KiB
Python
"""Tests for OpenRouter response caching header injection."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# build_or_headers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestBuildOrHeaders:
|
|
"""Test the build_or_headers() helper in agent/auxiliary_client.py."""
|
|
|
|
|
|
|
|
|
|
|
|
def test_ttl_default(self):
|
|
"""Default TTL (300) is included when cache is enabled."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
headers = build_or_headers(or_config={"response_cache": True, "response_cache_ttl": 300})
|
|
assert headers["X-OpenRouter-Cache-TTL"] == "300"
|
|
|
|
|
|
|
|
|
|
|
|
def test_ttl_negative(self):
|
|
"""Negative TTL is ignored."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
headers = build_or_headers(or_config={"response_cache": True, "response_cache_ttl": -5})
|
|
assert "X-OpenRouter-Cache-TTL" not in headers
|
|
|
|
|
|
|
|
def test_returns_fresh_dict(self):
|
|
"""Each call returns a new dict so mutations don't leak."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
cfg = {"response_cache": True}
|
|
h1 = build_or_headers(or_config=cfg)
|
|
h2 = build_or_headers(or_config=cfg)
|
|
assert h1 is not h2
|
|
assert h1 == h2
|
|
|
|
|
|
def test_none_config_load_config_fails_gracefully(self):
|
|
"""When load_config() fails, build_or_headers still returns base headers."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("boom")):
|
|
headers = build_or_headers(or_config=None)
|
|
# Should have base attribution but no cache headers
|
|
assert "HTTP-Referer" in headers
|
|
assert "X-OpenRouter-Cache" not in headers
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Environment variable overrides
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestEnvVarOverrides:
|
|
"""Test env var precedence over config.yaml for response caching."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("ttl", ["0", "86401", "abc", "-1", "12.5"])
|
|
def test_invalid_env_ttl_dropped(self, monkeypatch, ttl):
|
|
"""Invalid TTL env values are ignored; cache still enabled without TTL."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
monkeypatch.setenv("HERMES_OPENROUTER_CACHE", "1")
|
|
monkeypatch.setenv("HERMES_OPENROUTER_CACHE_TTL", ttl)
|
|
headers = build_or_headers(or_config={})
|
|
assert headers["X-OpenRouter-Cache"] == "true"
|
|
assert "X-OpenRouter-Cache-TTL" not in headers
|
|
|
|
@pytest.mark.parametrize("ttl", ["1", "300", "86400"])
|
|
def test_valid_env_ttl_boundaries(self, monkeypatch, ttl):
|
|
"""Boundary TTL values (1, 300, 86400) are accepted."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
monkeypatch.setenv("HERMES_OPENROUTER_CACHE", "yes")
|
|
monkeypatch.setenv("HERMES_OPENROUTER_CACHE_TTL", ttl)
|
|
assert build_or_headers(or_config={})["X-OpenRouter-Cache-TTL"] == ttl
|
|
|
|
def test_no_env_vars_falls_through_to_config(self, monkeypatch):
|
|
"""Without env vars, config.yaml controls behavior."""
|
|
from agent.auxiliary_client import build_or_headers
|
|
|
|
monkeypatch.delenv("HERMES_OPENROUTER_CACHE", raising=False)
|
|
monkeypatch.delenv("HERMES_OPENROUTER_CACHE_TTL", raising=False)
|
|
headers = build_or_headers(or_config={"response_cache": True, "response_cache_ttl": 600})
|
|
assert headers["X-OpenRouter-Cache"] == "true"
|
|
assert headers["X-OpenRouter-Cache-TTL"] == "600"
|
|
|
|
class TestDefaultConfig:
|
|
"""Verify the openrouter config section is in DEFAULT_CONFIG."""
|
|
|
|
def test_openrouter_section_exists(self):
|
|
from hermes_cli.config import DEFAULT_CONFIG
|
|
|
|
assert "openrouter" in DEFAULT_CONFIG
|
|
or_cfg = DEFAULT_CONFIG["openrouter"]
|
|
assert or_cfg["response_cache"] is True
|
|
assert or_cfg["response_cache_ttl"] == 300
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _check_openrouter_cache_status
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestCheckOpenrouterCacheStatus:
|
|
"""Test the _check_openrouter_cache_status method on AIAgent."""
|
|
|
|
def _make_agent(self):
|
|
"""Create a minimal AIAgent-like object with just the method under test."""
|
|
from run_agent import AIAgent
|
|
|
|
# Use object.__new__ to skip __init__, then set the attributes we need
|
|
agent = object.__new__(AIAgent)
|
|
agent._or_cache_hits = 0
|
|
return agent
|
|
|
|
|
|
|
|
def test_no_header_is_noop(self):
|
|
agent = self._make_agent()
|
|
resp = SimpleNamespace(headers={})
|
|
agent._check_openrouter_cache_status(resp)
|
|
assert getattr(agent, "_or_cache_hits", 0) == 0
|
|
|
|
|
|
|
|
def test_case_insensitive(self):
|
|
agent = self._make_agent()
|
|
resp = SimpleNamespace(headers={"x-openrouter-cache-status": "hit"})
|
|
agent._check_openrouter_cache_status(resp)
|
|
assert agent._or_cache_hits == 1
|