hermes-agent/tests/hermes_cli/test_subparser_routing_fallback.py
Teknium 2367c6ffd5
test: remove 169 change-detector tests across 21 files (#11472)
First pass of test-suite reduction to address flaky CI and bloat.

Removed tests that fall into these change-detector patterns:

1. Source-grep tests (tests/gateway/test_feishu.py, test_email.py): tests
   that call inspect.getsource() on production modules and grep for string
   literals. Break on any refactor/rename even when behavior is correct.

2. Platform enum tautologies (every gateway/test_X.py): assertions like
   `Platform.X.value == 'x'` duplicated across ~9 adapter test files.

3. Toolset/PLATFORM_HINTS/setup-wizard registry-presence checks: tests that
   only verify a key exists in a dict. Data-layout tests, not behavior.

4. Argparse wiring tests (test_argparse_flag_propagation, test_subparser_routing
   _fallback): tests that do parser.parse_args([...]) then assert args.field.
   Tests Python's argparse, not our code.

5. Pure dispatch tests (test_plugins_cmd.TestPluginsCommandDispatch): patch
   cmd_X, call plugins_command with matching action, assert mock called.
   Tests the if/elif chain, not behavior.

6. Kwarg-to-mock verification (test_auxiliary_client ~45 tests,
   test_web_tools_config, test_gemini_cloudcode, test_retaindb_plugin): tests
   that mock the external API client, call our function, and assert exact
   kwargs. Break on refactor even when behavior is preserved.

7. Schedule-internal "function-was-called" tests (acp/test_server scheduling
   tests): tests that patch own helper method, then assert it was called.

Kept behavioral tests throughout: error paths (pytest.raises), security
tests (path traversal, SSRF, redaction), message alternation invariants,
provider API format conversion, streaming logic, memory contract, real
config load/merge tests.

Net reduction: 169 tests removed. 38 empty classes cleaned up.

Collected before: 12,522 tests
Collected after:  12,353 tests
2026-04-17 01:05:09 -07:00

66 lines
2.4 KiB
Python

"""Tests for the defensive subparser routing workaround (bpo-9338).
The main() function in hermes_cli/main.py sets subparsers.required=True
when argv contains a known subcommand name. This forces deterministic
routing on Python versions where argparse fails to match subcommand tokens
when the parent parser has nargs='?' optional arguments (--continue).
If the subcommand token is consumed as a flag value (e.g. `hermes -c model`
to resume a session named 'model'), the required=True parse raises
SystemExit and the code falls back to the default required=False behaviour.
"""
import argparse
import io
import sys
import pytest
def _build_parser():
"""Build a minimal replica of the hermes top-level parser."""
parser = argparse.ArgumentParser(prog="hermes")
parser.add_argument("--version", "-V", action="store_true")
parser.add_argument("--resume", "-r", metavar="SESSION", default=None)
parser.add_argument(
"--continue", "-c",
dest="continue_last",
nargs="?",
const=True,
default=None,
metavar="SESSION_NAME",
)
parser.add_argument("--worktree", "-w", action="store_true", default=False)
parser.add_argument("--skills", "-s", action="append", default=None)
parser.add_argument("--yolo", action="store_true", default=False)
parser.add_argument("--pass-session-id", action="store_true", default=False)
subparsers = parser.add_subparsers(dest="command", help="Command to run")
chat_p = subparsers.add_parser("chat")
chat_p.add_argument("-q", "--query", default=None)
subparsers.add_parser("model")
subparsers.add_parser("gateway")
subparsers.add_parser("setup")
return parser, subparsers
def _safe_parse(parser, subparsers, argv):
"""Replica of the defensive parsing logic from main()."""
known_cmds = set(subparsers.choices.keys()) if hasattr(subparsers, "choices") else set()
has_cmd_token = any(t in known_cmds for t in argv if not t.startswith("-"))
if has_cmd_token:
subparsers.required = True
saved_stderr = sys.stderr
try:
sys.stderr = io.StringIO()
args = parser.parse_args(argv)
sys.stderr = saved_stderr
return args
except SystemExit:
sys.stderr = saved_stderr
subparsers.required = False
return parser.parse_args(argv)
else:
subparsers.required = False
return parser.parse_args(argv)