- 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>
71 lines
2.9 KiB
Python
71 lines
2.9 KiB
Python
"""Tests for check_api command."""
|
|
|
|
import json
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from coinhunter.commands import check_api
|
|
|
|
|
|
class TestMain:
|
|
def test_missing_api_key(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "secret")
|
|
rc = check_api.main()
|
|
assert rc == 1
|
|
out = json.loads(capsys.readouterr().out)
|
|
assert out["ok"] is False
|
|
assert "BINANCE_API_KEY" in out["error"]
|
|
|
|
def test_missing_api_secret(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "key")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "")
|
|
rc = check_api.main()
|
|
assert rc == 1
|
|
out = json.loads(capsys.readouterr().out)
|
|
assert out["ok"] is False
|
|
assert "BINANCE_API_SECRET" in out["error"]
|
|
|
|
def test_placholder_api_key(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "your_api_key")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "secret")
|
|
rc = check_api.main()
|
|
assert rc == 1
|
|
|
|
def test_balance_fetch_failure(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "key")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "secret")
|
|
mock_ex = MagicMock()
|
|
mock_ex.fetch_balance.side_effect = Exception("Network error")
|
|
with patch("coinhunter.commands.check_api.ccxt.binance", return_value=mock_ex):
|
|
rc = check_api.main()
|
|
assert rc == 1
|
|
out = json.loads(capsys.readouterr().out)
|
|
assert "Failed to connect" in out["error"]
|
|
|
|
def test_success_with_spot_trading(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "key")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "secret")
|
|
mock_ex = MagicMock()
|
|
mock_ex.fetch_balance.return_value = {"USDT": 100.0}
|
|
mock_ex.sapi_get_account_api_restrictions.return_value = {"enableSpotTrading": True}
|
|
with patch("coinhunter.commands.check_api.ccxt.binance", return_value=mock_ex):
|
|
rc = check_api.main()
|
|
assert rc == 0
|
|
out = json.loads(capsys.readouterr().out)
|
|
assert out["ok"] is True
|
|
assert out["read_permission"] is True
|
|
assert out["spot_trading_enabled"] is True
|
|
|
|
def test_success_restrictions_query_fails(self, monkeypatch, capsys):
|
|
monkeypatch.setenv("BINANCE_API_KEY", "key")
|
|
monkeypatch.setenv("BINANCE_API_SECRET", "secret")
|
|
mock_ex = MagicMock()
|
|
mock_ex.fetch_balance.return_value = {"USDT": 100.0}
|
|
mock_ex.sapi_get_account_api_restrictions.side_effect = Exception("no permission")
|
|
with patch("coinhunter.commands.check_api.ccxt.binance", return_value=mock_ex):
|
|
rc = check_api.main()
|
|
assert rc == 0
|
|
out = json.loads(capsys.readouterr().out)
|
|
assert out["spot_trading_enabled"] is None
|
|
assert "may be null" in out["note"]
|