Merge branch 'develop' into ci/ccxt.pro
This commit is contained in:
+1
-1
@@ -238,7 +238,6 @@ def patched_configuration_load_config_file(mocker, config) -> None:
|
||||
def patch_exchange(
|
||||
mocker, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True
|
||||
) -> None:
|
||||
mocker.patch(f"{EXMS}._load_async_markets", return_value={})
|
||||
mocker.patch(f"{EXMS}.validate_config", MagicMock())
|
||||
mocker.patch(f"{EXMS}.validate_timeframes", MagicMock())
|
||||
mocker.patch(f"{EXMS}.id", PropertyMock(return_value=id))
|
||||
@@ -248,6 +247,7 @@ def patch_exchange(
|
||||
mocker.patch("freqtrade.exchange.bybit.Bybit.cache_leverage_tiers")
|
||||
|
||||
if mock_markets:
|
||||
mocker.patch(f"{EXMS}._load_async_markets", return_value={})
|
||||
if isinstance(mock_markets, bool):
|
||||
mock_markets = get_markets()
|
||||
mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=mock_markets))
|
||||
|
||||
@@ -181,7 +181,7 @@ def test_remove_exchange_credentials(default_conf) -> None:
|
||||
|
||||
|
||||
def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
aei_mock = mocker.patch(f"{EXMS}.additional_exchange_init")
|
||||
|
||||
@@ -518,7 +518,7 @@ def test__load_async_markets(default_conf, mocker, caplog):
|
||||
mocker.patch(f"{EXMS}._init_ccxt")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_markets")
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
exchange = Exchange(default_conf)
|
||||
@@ -527,28 +527,26 @@ def test__load_async_markets(default_conf, mocker, caplog):
|
||||
assert exchange._api_async.load_markets.call_count == 1
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef"))
|
||||
exchange._load_async_markets()
|
||||
|
||||
assert log_has("Could not load async markets. Reason: deadbeef", caplog)
|
||||
exchange._api_async.load_markets = get_mock_coro(side_effect=ccxt.BaseError("deadbeef"))
|
||||
with pytest.raises(ccxt.BaseError, match="deadbeef"):
|
||||
exchange._load_async_markets()
|
||||
|
||||
|
||||
def test__load_markets(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError("SomeError"))
|
||||
api_mock.load_markets = get_mock_coro(side_effect=ccxt.BaseError("SomeError"))
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
Exchange(default_conf)
|
||||
assert log_has("Unable to initialize markets.", caplog)
|
||||
assert log_has("Could not load markets.", caplog)
|
||||
|
||||
expected_return = {"ETH/BTC": "available"}
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(return_value=expected_return)
|
||||
api_mock.load_markets = get_mock_coro(return_value=expected_return)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
default_conf["exchange"]["pair_whitelist"] = ["ETH/BTC"]
|
||||
ex = Exchange(default_conf)
|
||||
@@ -563,12 +561,12 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine):
|
||||
start_dt = dt_now()
|
||||
time_machine.move_to(start_dt, tick=False)
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(return_value=initial_markets)
|
||||
api_mock.load_markets = get_mock_coro(return_value=initial_markets)
|
||||
default_conf["exchange"]["markets_refresh_interval"] = 10
|
||||
exchange = get_patched_exchange(
|
||||
mocker, default_conf, api_mock, id="binance", mock_markets=False
|
||||
)
|
||||
exchange._load_async_markets = MagicMock()
|
||||
lam_spy = mocker.spy(exchange, "_load_async_markets")
|
||||
assert exchange._last_markets_refresh == dt_ts()
|
||||
|
||||
assert exchange.markets == initial_markets
|
||||
@@ -577,42 +575,45 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine):
|
||||
# less than 10 minutes have passed, no reload
|
||||
exchange.reload_markets()
|
||||
assert exchange.markets == initial_markets
|
||||
assert exchange._load_async_markets.call_count == 0
|
||||
assert lam_spy.call_count == 0
|
||||
|
||||
api_mock.load_markets = MagicMock(return_value=updated_markets)
|
||||
api_mock.load_markets = get_mock_coro(return_value=updated_markets)
|
||||
# more than 10 minutes have passed, reload is executed
|
||||
time_machine.move_to(start_dt + timedelta(minutes=11), tick=False)
|
||||
exchange.reload_markets()
|
||||
assert exchange.markets == updated_markets
|
||||
assert exchange._load_async_markets.call_count == 1
|
||||
assert lam_spy.call_count == 1
|
||||
assert log_has("Performing scheduled market reload..", caplog)
|
||||
|
||||
# Not called again
|
||||
exchange._load_async_markets.reset_mock()
|
||||
lam_spy.reset_mock()
|
||||
|
||||
exchange.reload_markets()
|
||||
assert exchange._load_async_markets.call_count == 0
|
||||
assert lam_spy.call_count == 0
|
||||
|
||||
|
||||
def test_reload_markets_exception(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(side_effect=ccxt.NetworkError("LoadError"))
|
||||
api_mock.load_markets = get_mock_coro(side_effect=ccxt.NetworkError("LoadError"))
|
||||
default_conf["exchange"]["markets_refresh_interval"] = 10
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
|
||||
exchange = get_patched_exchange(
|
||||
mocker, default_conf, api_mock, id="binance", mock_markets=False
|
||||
)
|
||||
|
||||
exchange._last_markets_refresh = 2
|
||||
# less than 10 minutes have passed, no reload
|
||||
exchange.reload_markets()
|
||||
assert exchange._last_markets_refresh == 0
|
||||
assert log_has_re(r"Could not reload markets.*", caplog)
|
||||
assert exchange._last_markets_refresh == 2
|
||||
assert log_has_re(r"Could not load markets\..*", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stake_currency", ["ETH", "BTC", "USDT"])
|
||||
def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog):
|
||||
default_conf["stake_currency"] = stake_currency
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -623,7 +624,6 @@ def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog):
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
Exchange(default_conf)
|
||||
|
||||
@@ -631,7 +631,7 @@ def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog):
|
||||
def test_validate_stakecurrency_error(default_conf, mocker, caplog):
|
||||
default_conf["stake_currency"] = "XRP"
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -642,14 +642,13 @@ def test_validate_stakecurrency_error(default_conf, mocker, caplog):
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
with pytest.raises(
|
||||
ConfigurationError,
|
||||
match=r"XRP is not available as stake on .*Available currencies are: BTC, ETH, USDT",
|
||||
):
|
||||
Exchange(default_conf)
|
||||
|
||||
type(api_mock).load_markets = MagicMock(side_effect=ccxt.NetworkError("No connection."))
|
||||
type(api_mock).load_markets = get_mock_coro(side_effect=ccxt.NetworkError("No connection."))
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
|
||||
with pytest.raises(
|
||||
@@ -694,24 +693,26 @@ def test_get_pair_base_currency(default_conf, mocker, pair, expected):
|
||||
assert ex.get_pair_base_currency(pair) == expected
|
||||
|
||||
|
||||
def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly
|
||||
def test_validate_pairs(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
"XRP/BTC": {"quote": "BTC"},
|
||||
"NEO/BTC": {"quote": "BTC"},
|
||||
}
|
||||
)
|
||||
id_mock = PropertyMock(return_value="test_exchange")
|
||||
type(api_mock).id = id_mock
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(
|
||||
f"{EXMS}._load_async_markets",
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
"XRP/BTC": {"quote": "BTC"},
|
||||
"NEO/BTC": {"quote": "BTC"},
|
||||
},
|
||||
)
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
# test exchange.validate_pairs directly
|
||||
# No assert - but this should not fail (!)
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
@@ -751,7 +752,7 @@ def test_validate_pairs_exception(default_conf, mocker, caplog):
|
||||
|
||||
def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -761,7 +762,6 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
||||
)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
|
||||
@@ -774,9 +774,9 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
||||
)
|
||||
|
||||
|
||||
def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog):
|
||||
def test_validate_pairs_stakecompatibility(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -787,17 +787,16 @@ def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog):
|
||||
)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog):
|
||||
def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
default_conf["stake_currency"] = ""
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -808,7 +807,6 @@ def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, ca
|
||||
)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
|
||||
@@ -816,10 +814,10 @@ def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, ca
|
||||
assert type(api_mock).load_markets.call_count == 1
|
||||
|
||||
|
||||
def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog):
|
||||
def test_validate_pairs_stakecompatibility_fail(default_conf, mocker):
|
||||
default_conf["exchange"]["pair_whitelist"].append("HELLO-WORLD")
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).load_markets = MagicMock(
|
||||
type(api_mock).load_markets = get_mock_coro(
|
||||
return_value={
|
||||
"ETH/BTC": {"quote": "BTC"},
|
||||
"LTC/BTC": {"quote": "BTC"},
|
||||
@@ -830,7 +828,6 @@ def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog):
|
||||
)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}._load_async_markets")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"Stake-currency 'BTC' not compatible with.*"):
|
||||
@@ -847,7 +844,7 @@ def test_validate_timeframes(default_conf, mocker, timeframe):
|
||||
type(api_mock).timeframes = timeframes
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
@@ -865,7 +862,7 @@ def test_validate_timeframes_failed(default_conf, mocker):
|
||||
type(api_mock).timeframes = timeframes
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
@@ -895,7 +892,7 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
|
||||
del api_mock.timeframes
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
with pytest.raises(
|
||||
@@ -917,7 +914,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker):
|
||||
del api_mock.timeframes
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={"timeframes": None}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs", MagicMock())
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
with pytest.raises(
|
||||
@@ -939,7 +936,7 @@ def test_validate_timeframes_not_in_config(default_conf, mocker):
|
||||
type(api_mock).timeframes = timeframes
|
||||
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
@@ -955,7 +952,7 @@ def test_validate_pricing(default_conf, mocker):
|
||||
}
|
||||
type(api_mock).has = PropertyMock(return_value=has)
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_trading_mode_and_margin_mode")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
@@ -991,7 +988,7 @@ def test_validate_ordertypes(default_conf, mocker):
|
||||
|
||||
type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True})
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
@@ -1050,7 +1047,7 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name,
|
||||
default_conf["margin_mode"] = MarginMode.ISOLATED
|
||||
type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True})
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}.validate_stakecurrency")
|
||||
@@ -1075,7 +1072,7 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name,
|
||||
def test_validate_order_types_not_in_config(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock))
|
||||
mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={}))
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.validate_pairs")
|
||||
mocker.patch(f"{EXMS}.validate_timeframes")
|
||||
mocker.patch(f"{EXMS}.validate_pricing")
|
||||
@@ -1947,7 +1944,9 @@ def test_fetch_trading_fees(default_conf, mocker):
|
||||
assert api_mock.fetch_trading_fees.call_count == 1
|
||||
|
||||
api_mock.fetch_trading_fees.reset_mock()
|
||||
|
||||
# Reload-markets calls fetch_trading_fees, too - so the explicit calls in the below
|
||||
# exception test would be called twice.
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
ccxt_exceptionhandlers(
|
||||
mocker, default_conf, api_mock, exchange_name, "fetch_trading_fees", "fetch_trading_fees"
|
||||
)
|
||||
|
||||
@@ -699,18 +699,20 @@ def test_process_trade_creation(
|
||||
|
||||
|
||||
def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> None:
|
||||
# TODO: Move this test to test_worker
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
fetch_ticker=ticker_usdt,
|
||||
reload_markets=MagicMock(side_effect=TemporaryError),
|
||||
reload_markets=MagicMock(),
|
||||
create_order=MagicMock(side_effect=TemporaryError),
|
||||
)
|
||||
sleep_mock = mocker.patch("time.sleep")
|
||||
|
||||
worker = Worker(args=None, config=default_conf_usdt)
|
||||
patch_get_signal(worker.freqtrade)
|
||||
mocker.patch(f"{EXMS}.reload_markets", MagicMock(side_effect=TemporaryError))
|
||||
|
||||
worker._process_running()
|
||||
assert sleep_mock.called is True
|
||||
|
||||
@@ -1063,7 +1063,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None
|
||||
def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path, fee) -> None:
|
||||
mocker.patch(f"{EXMS}.validate_config", MagicMock())
|
||||
mocker.patch(f"{EXMS}.get_fee", fee)
|
||||
mocker.patch(f"{EXMS}._load_markets")
|
||||
mocker.patch(f"{EXMS}.reload_markets")
|
||||
mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=get_markets()))
|
||||
(tmp_path / "hyperopt_results").mkdir(parents=True)
|
||||
# Dummy-reduce points to ensure scikit-learn is forced to generate new values
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
|
||||
Describe "Setup and Tests" {
|
||||
BeforeAll {
|
||||
# Setup variables
|
||||
$SetupScriptPath = Join-Path $PSScriptRoot "..\setup.ps1"
|
||||
$Global:LogFilePath = Join-Path $env:TEMP "script_log.txt"
|
||||
|
||||
# Check if the setup script exists
|
||||
if (-Not (Test-Path -Path $SetupScriptPath)) {
|
||||
Write-Host "Error: setup.ps1 script not found at path: $SetupScriptPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Mock main to prevent it from running
|
||||
Mock Main {}
|
||||
|
||||
. $SetupScriptPath
|
||||
}
|
||||
|
||||
Context "Write-Log Tests" -Tag "Unit" {
|
||||
It "should write INFO level log" {
|
||||
if (Test-Path $Global:LogFilePath){
|
||||
Remove-Item $Global:LogFilePath -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Log -Message "Test Info Message" -Level "INFO"
|
||||
$Global:LogFilePath | Should -Exist
|
||||
|
||||
$LogContent = Get-Content $Global:LogFilePath
|
||||
$LogContent | Should -Contain "INFO: Test Info Message"
|
||||
}
|
||||
|
||||
It "should write ERROR level log" {
|
||||
if (Test-Path $Global:LogFilePath){
|
||||
Remove-Item $Global:LogFilePath -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Log -Message "Test Error Message" -Level "ERROR"
|
||||
$Global:LogFilePath | Should -Exist
|
||||
|
||||
$LogContent = Get-Content $Global:LogFilePath
|
||||
$LogContent | Should -Contain "ERROR: Test Error Message"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Get-UserSelection Tests" {
|
||||
Context "Valid input" {
|
||||
It "Should return the correct index for a valid single selection" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "B" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options
|
||||
$Result | Should -Be 1
|
||||
}
|
||||
|
||||
It "Should return the correct index for a valid single selection" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "b" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options
|
||||
$Result | Should -Be 1
|
||||
}
|
||||
|
||||
It "Should return the default choice when no input is provided" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options -defaultChoice "C"
|
||||
$Result | Should -Be 2
|
||||
}
|
||||
}
|
||||
|
||||
Context "Invalid input" {
|
||||
It "Should return -1 for an invalid letter selection" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "X" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options
|
||||
$Result | Should -Be -1
|
||||
}
|
||||
|
||||
It "Should return -1 for a selection outside the valid range" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "D" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options
|
||||
$Result | Should -Be -1
|
||||
}
|
||||
|
||||
It "Should return -1 for a non-letter input" {
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
Mock Read-Host { return "1" }
|
||||
$Result = Get-UserSelection -prompt "Select an option" -options $Options
|
||||
$Result | Should -Be -1
|
||||
}
|
||||
|
||||
It "Should return -1 for mixed valid and invalid input" {
|
||||
Mock Read-Host { return "A,X,B,Y,C,Z" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options -defaultChoice "A"
|
||||
$Indices | Should -Be -1
|
||||
}
|
||||
}
|
||||
|
||||
Context "Multiple selections" {
|
||||
It "Should handle valid input correctly" {
|
||||
Mock Read-Host { return "A, B, C" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options -defaultChoice "A"
|
||||
$Indices | Should -Be @(0, 1, 2)
|
||||
}
|
||||
|
||||
It "Should handle valid input without whitespace correctly" {
|
||||
Mock Read-Host { return "A,B,C" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options -defaultChoice "A"
|
||||
$Indices | Should -Be @(0, 1, 2)
|
||||
}
|
||||
|
||||
It "Should return indices for selected options" {
|
||||
Mock Read-Host { return "a,b" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options
|
||||
$Indices | Should -Be @(0, 1)
|
||||
}
|
||||
|
||||
It "Should return default choice if no input" {
|
||||
Mock Read-Host { return "" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options -defaultChoice "C"
|
||||
$Indices | Should -Be @(2)
|
||||
}
|
||||
|
||||
It "Should handle invalid input gracefully" {
|
||||
Mock Read-Host { return "x,y,z" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options -defaultChoice "A"
|
||||
$Indices | Should -Be -1
|
||||
}
|
||||
|
||||
It "Should handle input without whitespace" {
|
||||
Mock Read-Host { return "a,b,c" }
|
||||
$Options = @("Option1", "Option2", "Option3")
|
||||
$Indices = Get-UserSelection -prompt "Select options" -options $Options
|
||||
$Indices | Should -Be @(0, 1, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Exit-Script Tests" -Tag "Unit" {
|
||||
BeforeEach {
|
||||
Mock Write-Log {}
|
||||
Mock Start-Process {}
|
||||
Mock Read-Host { return "Y" }
|
||||
}
|
||||
|
||||
It "should exit with the given exit code without waiting for key press" {
|
||||
$ExitCode = Exit-Script -ExitCode 0 -isSubShell $true -waitForKeypress $false
|
||||
$ExitCode | Should -Be 0
|
||||
}
|
||||
|
||||
It "should prompt to open log file on error" {
|
||||
Exit-Script -ExitCode 1 -isSubShell $true -waitForKeypress $false
|
||||
Assert-MockCalled Read-Host -Exactly 1
|
||||
Assert-MockCalled Start-Process -Exactly 1
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Find-PythonExecutable' {
|
||||
It 'Returns the first valid Python executable' {
|
||||
Mock Test-PythonExecutable { $true } -ParameterFilter { $PythonExecutable -eq 'python' }
|
||||
$Result = Find-PythonExecutable
|
||||
$Result | Should -Be 'python'
|
||||
}
|
||||
|
||||
It 'Returns null if no valid Python executable is found' {
|
||||
Mock Test-PythonExecutable { $false }
|
||||
$Result = Find-PythonExecutable
|
||||
$Result | Should -Be $null
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user