- Fix TOCTOU race conditions by wrapping read-modify-write cycles under single-file locks in execution_state, portfolio_service, precheck_state, state_manager, and precheck_service. - Add missing test coverage (96 tests total): - test_review_service.py (15 tests) - test_check_api.py (6 tests) - test_external_gate.py main branches (+10 tests) - test_trade_execution.py new commands (+8 tests) - Unify all agent-consumed JSON messages to English. - Config-ize hardcoded values (volume filter, schema_version) via get_user_config with sensible defaults. - Add 1-hour TTL to exchange cache with force_new override. - Add ruff and mypy to dev dependencies; fix all type errors. - Add __all__ declarations to 11 service modules. - Sync README with new commands, config tuning docs, and PyPI badge. - Publish package as coinhunter==1.0.0 on PyPI with MIT license. - Deprecate coinhunter-cli==1.0.1 with runtime warning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
"""Tests for CLI routing and parser behavior."""
|
|
|
|
import pytest
|
|
|
|
from coinhunter.cli import ALIASES, MODULE_MAP, build_parser, run_python_module
|
|
|
|
|
|
class TestAliases:
|
|
def test_all_aliases_resolve_to_canonical(self):
|
|
for alias, canonical in ALIASES.items():
|
|
assert canonical in MODULE_MAP, f"alias {alias!r} points to missing canonical {canonical!r}"
|
|
|
|
def test_no_alias_is_itself_an_alias_loop(self):
|
|
for alias in ALIASES:
|
|
assert alias not in ALIASES.values() or alias in MODULE_MAP
|
|
|
|
|
|
class TestModuleMap:
|
|
def test_all_modules_exist(self):
|
|
import importlib
|
|
|
|
for command, module_name in MODULE_MAP.items():
|
|
full = f"coinhunter.{module_name}"
|
|
mod = importlib.import_module(full)
|
|
assert hasattr(mod, "main"), f"{full} missing main()"
|
|
|
|
|
|
class TestBuildParser:
|
|
def test_help_includes_commands(self):
|
|
parser = build_parser()
|
|
help_text = parser.format_help()
|
|
assert "coinhunter diag" in help_text
|
|
assert "coinhunter exec" in help_text
|
|
|
|
def test_parses_command_and_args(self):
|
|
parser = build_parser()
|
|
ns = parser.parse_args(["exec", "bal"])
|
|
assert ns.command == "exec"
|
|
assert "bal" in ns.args
|
|
|
|
def test_version_action_exits(self):
|
|
parser = build_parser()
|
|
with pytest.raises(SystemExit) as exc:
|
|
parser.parse_args(["--version"])
|
|
assert exc.value.code == 0
|
|
|
|
|
|
class TestRunPythonModule:
|
|
def test_runs_module_main_and_returns_int(self):
|
|
result = run_python_module("commands.paths", [], "coinhunter paths")
|
|
assert result == 0
|
|
|
|
def test_mutates_sys_argv(self):
|
|
import sys
|
|
|
|
original = sys.argv[:]
|
|
run_python_module("commands.paths", ["--help"], "coinhunter paths")
|
|
assert sys.argv == original
|