hermes-agent/tests/hermes_cli/test_auth_ssl_macos.py
Teknium 3dd8059a05
fix(tests): eliminate flaky/broken tests — shadow sys.path inserts, unmocked network in compressor tests, stale-SDK feishu pin guard, quadratic redact regexes
- Remove tests/-shadowing sys.path.insert(dirname/'..') from 11 test files:
  it prepended the tests/ dir itself to sys.path, so 'import agent' /
  'import hermes_cli' resolved to the test packages and collection died
  with ModuleNotFoundError depending on import order (2 files failed in
  every full-suite run; 9 more were latent).
- Patch call_llm in 5 context-compressor tests that called compress()
  unmocked: each burned ~50s attempting live LLM traffic through the
  relay before falling back (572s file — the slowest in the suite, and
  flaky under the 300s per-file timeout). File now runs in ~5s.
- agent/redact.py: fix two catastrophically-backtracking regexes hit by
  the compressor's redaction pass on large payloads —
  _STRICT_URL_USERINFO_RE anchors on the mandatory '//' (optional-scheme
  prefix backtracked O(n^2): ~55s on a 320KB payload, now sub-ms;
  output-equivalence fuzz-verified on 20k random strings), and the
  _CFG_DOTTED_RE/_CFG_ANCHORED_RE subs gain an exact linear keyword
  pre-gate so secret-free text skips the quadratic pattern entirely.
- tests/gateway/test_feishu.py: version-guard the extra_ua_tags SDK
  signature check; the repo pins lark-oapi==1.6.8 but stale local
  installs (1.5.3) fail the assertion — skip below the pin.
- tests/tools/test_managed_browserbase_and_modal.py: stub
  agent.redact + agent.credential_persistence in the fake agent package
  (empty __path__ blocks all real agent.* imports added since the fake
  was written).
- tests/gateway/test_startup_restart_race.py: raise wait_for timeouts
  2s -> 30s; 2s wall-clock on a loaded 40-worker box flaked in the
  baseline run (passes instantly when the box is quiet).
2026-07-29 12:18:07 -07:00

113 lines
4.2 KiB
Python

"""Tests for hermes_cli.auth._default_verify platform-aware fallback.
On macOS with Homebrew Python, the system OpenSSL cannot locate the
system trust store, so we explicitly load certifi's bundle. On other
platforms we defer to httpx's own default (which itself uses certifi).
Most tests use monkeypatching — no real SSL handshakes. A handful use
an openssl-generated self-signed cert via the `real_bundle_file`
fixture because `ssl.create_default_context(cafile=...)` parses the
bundle and refuses stubs.
"""
import shutil
import ssl
import subprocess
import sys
from pathlib import Path
import pytest
from hermes_cli.auth import _default_verify, _resolve_verify
@pytest.fixture
def real_bundle_file(tmp_path: Path) -> str:
"""Return a path to a real openssl-generated self-signed cert.
Skips the test when the `openssl` binary isn't on PATH, so CI images
without it degrade gracefully instead of erroring out.
"""
if shutil.which("openssl") is None:
pytest.skip("openssl binary not available")
cert = tmp_path / "ca.pem"
key = tmp_path / "key.pem"
result = subprocess.run(
[
"openssl", "req", "-x509", "-newkey", "rsa:2048",
"-keyout", str(key), "-out", str(cert),
"-sha256", "-days", "1", "-nodes",
"-subj", "/CN=test",
],
capture_output=True,
timeout=10,
)
if result.returncode != 0:
pytest.skip(f"openssl failed: {result.stderr.decode('utf-8', 'ignore')[:200]}")
return str(cert)
class TestDefaultVerify:
def test_returns_ssl_context_on_darwin(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
result = _default_verify()
assert isinstance(result, ssl.SSLContext)
def test_returns_true_on_linux(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "linux")
assert _default_verify() is True
def test_returns_true_on_windows(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert _default_verify() is True
def test_darwin_falls_back_to_true_when_certifi_missing(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
real_import = __import__
def fake_import(name, *args, **kwargs):
if name == "certifi":
raise ImportError("simulated missing certifi")
return real_import(name, *args, **kwargs)
monkeypatch.setattr("builtins.__import__", fake_import)
assert _default_verify() is True
class TestResolveVerifyIntegration:
"""_resolve_verify should defer to _default_verify in the no-CA path."""
def test_no_ca_uses_default_verify_on_darwin(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
for var in ("HERMES_CA_BUNDLE", "SSL_CERT_FILE", "REQUESTS_CA_BUNDLE"):
monkeypatch.delenv(var, raising=False)
result = _resolve_verify()
assert isinstance(result, ssl.SSLContext)
def test_no_ca_uses_default_verify_on_linux(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "linux")
for var in ("HERMES_CA_BUNDLE", "SSL_CERT_FILE", "REQUESTS_CA_BUNDLE"):
monkeypatch.delenv(var, raising=False)
assert _resolve_verify() is True
def test_requests_ca_bundle_respected(self, monkeypatch, real_bundle_file):
for var in ("HERMES_CA_BUNDLE", "SSL_CERT_FILE"):
monkeypatch.delenv(var, raising=False)
monkeypatch.setenv("REQUESTS_CA_BUNDLE", real_bundle_file)
result = _resolve_verify()
assert isinstance(result, ssl.SSLContext)
def test_missing_ca_path_falls_back_to_default_verify(self, monkeypatch, tmp_path):
monkeypatch.setattr(sys, "platform", "linux")
monkeypatch.setenv("HERMES_CA_BUNDLE", str(tmp_path / "missing.pem"))
for var in ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE"):
monkeypatch.delenv(var, raising=False)
assert _resolve_verify() is True
def test_insecure_wins_over_everything(self, monkeypatch, tmp_path):
bundle = tmp_path / "ca.pem"
bundle.write_text("stub")
monkeypatch.setenv("HERMES_CA_BUNDLE", str(bundle))
assert _resolve_verify(insecure=True) is False