refactor: rewrite to CoinHunter V2 flat architecture
Replace the V1 commands/services split with a flat, direct architecture: - cli.py dispatches directly to service functions - New services: account, market, trade, opportunity - Thin Binance wrappers: spot_client, um_futures_client - Add audit logging, runtime paths, and TOML config - Remove legacy V1 code: commands/, precheck, review engine, smart executor - Add ruff + mypy toolchain and fix edge cases in trade params Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
79
tests/test_config_runtime.py
Normal file
79
tests/test_config_runtime.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Config and runtime tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from coinhunter.config import ensure_init_files, get_binance_credentials, load_config, load_env_file
|
||||
from coinhunter.runtime import get_runtime_paths
|
||||
|
||||
|
||||
class ConfigRuntimeTestCase(unittest.TestCase):
|
||||
def test_init_files_created_in_coinhunter_home(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir, patch.dict(os.environ, {"COINHUNTER_HOME": str(Path(tmp_dir) / "home")}, clear=False):
|
||||
paths = get_runtime_paths()
|
||||
payload = ensure_init_files(paths)
|
||||
self.assertTrue(paths.config_file.exists())
|
||||
self.assertTrue(paths.env_file.exists())
|
||||
self.assertTrue(paths.logs_dir.exists())
|
||||
self.assertEqual(payload["root"], str(paths.root))
|
||||
|
||||
def test_load_config_and_env(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir, patch.dict(
|
||||
os.environ,
|
||||
{"COINHUNTER_HOME": str(Path(tmp_dir) / "home")},
|
||||
clear=False,
|
||||
):
|
||||
paths = get_runtime_paths()
|
||||
ensure_init_files(paths)
|
||||
paths.env_file.write_text("BINANCE_API_KEY=abc\nBINANCE_API_SECRET=def\n", encoding="utf-8")
|
||||
|
||||
config = load_config(paths)
|
||||
loaded = load_env_file(paths)
|
||||
|
||||
self.assertEqual(config["market"]["default_quote"], "USDT")
|
||||
self.assertEqual(loaded["BINANCE_API_KEY"], "abc")
|
||||
self.assertEqual(os.environ["BINANCE_API_SECRET"], "def")
|
||||
|
||||
def test_env_file_overrides_existing_environment(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir, patch.dict(
|
||||
os.environ,
|
||||
{"COINHUNTER_HOME": str(Path(tmp_dir) / "home"), "BINANCE_API_KEY": "old_key"},
|
||||
clear=False,
|
||||
):
|
||||
paths = get_runtime_paths()
|
||||
ensure_init_files(paths)
|
||||
paths.env_file.write_text("BINANCE_API_KEY=new_key\nBINANCE_API_SECRET=new_secret\n", encoding="utf-8")
|
||||
|
||||
load_env_file(paths)
|
||||
|
||||
self.assertEqual(os.environ["BINANCE_API_KEY"], "new_key")
|
||||
self.assertEqual(os.environ["BINANCE_API_SECRET"], "new_secret")
|
||||
|
||||
def test_missing_credentials_raise(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir, patch.dict(
|
||||
os.environ,
|
||||
{"COINHUNTER_HOME": str(Path(tmp_dir) / "home")},
|
||||
clear=False,
|
||||
):
|
||||
os.environ.pop("BINANCE_API_KEY", None)
|
||||
os.environ.pop("BINANCE_API_SECRET", None)
|
||||
paths = get_runtime_paths()
|
||||
ensure_init_files(paths)
|
||||
with self.assertRaisesRegex(RuntimeError, "Missing BINANCE_API_KEY"):
|
||||
get_binance_credentials(paths)
|
||||
|
||||
def test_permission_error_is_explained(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir, patch.dict(
|
||||
os.environ,
|
||||
{"COINHUNTER_HOME": str(Path(tmp_dir) / "home")},
|
||||
clear=False,
|
||||
):
|
||||
paths = get_runtime_paths()
|
||||
with patch("coinhunter.config.ensure_runtime_dirs", side_effect=PermissionError("no write access")):
|
||||
with self.assertRaisesRegex(RuntimeError, "Set COINHUNTER_HOME to a writable directory"):
|
||||
ensure_init_files(paths)
|
||||
Reference in New Issue
Block a user