2018-07-29 14:09:44 +00:00
|
|
|
# pragma pylint: disable=missing-docstring
|
2018-02-04 09:21:16 +00:00
|
|
|
|
2018-06-09 02:29:48 +00:00
|
|
|
from copy import deepcopy
|
2020-09-28 17:43:15 +00:00
|
|
|
from pathlib import Path
|
2019-08-16 12:53:46 +00:00
|
|
|
from unittest.mock import MagicMock, PropertyMock
|
2018-03-17 21:44:47 +00:00
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
import pytest
|
2017-09-08 13:51:00 +00:00
|
|
|
|
2020-01-26 12:41:04 +00:00
|
|
|
from freqtrade.commands import Arguments
|
2021-06-08 19:20:35 +00:00
|
|
|
from freqtrade.enums import State
|
2024-03-19 06:15:14 +00:00
|
|
|
from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException
|
2019-04-30 17:32:03 +00:00
|
|
|
from freqtrade.freqtradebot import FreqtradeBot
|
2019-03-26 09:42:19 +00:00
|
|
|
from freqtrade.main import main
|
2019-04-30 17:32:03 +00:00
|
|
|
from freqtrade.worker import Worker
|
2024-05-12 13:08:40 +00:00
|
|
|
from tests.conftest import (
|
|
|
|
log_has,
|
|
|
|
log_has_re,
|
|
|
|
patch_exchange,
|
|
|
|
patched_configuration_load_config_file,
|
|
|
|
)
|
2018-01-06 10:21:09 +00:00
|
|
|
|
2018-01-10 09:25:45 +00:00
|
|
|
|
2019-09-16 04:44:20 +00:00
|
|
|
def test_parse_args_None(caplog) -> None:
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
main([])
|
2019-11-13 09:03:59 +00:00
|
|
|
assert log_has_re(r"Usage of Freqtrade requires a subcommand.*", caplog)
|
2019-09-16 04:44:20 +00:00
|
|
|
|
|
|
|
|
2018-02-04 09:21:16 +00:00
|
|
|
def test_parse_args_backtesting(mocker) -> None:
|
|
|
|
"""
|
|
|
|
Test that main() can start backtesting and also ensure we can pass some specific arguments
|
|
|
|
further argument parsing is done in test_arguments.py
|
|
|
|
"""
|
2020-02-14 19:04:05 +00:00
|
|
|
mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True]))
|
2024-05-12 13:29:14 +00:00
|
|
|
backtesting_mock = mocker.patch("freqtrade.commands.start_backtesting")
|
2019-08-16 12:53:46 +00:00
|
|
|
backtesting_mock.__name__ = PropertyMock("start_backtesting")
|
2019-05-28 20:25:53 +00:00
|
|
|
# it's sys.exit(0) at the end of backtesting
|
|
|
|
with pytest.raises(SystemExit):
|
2024-05-12 13:29:14 +00:00
|
|
|
main(["backtesting"])
|
2018-01-06 10:21:09 +00:00
|
|
|
assert backtesting_mock.call_count == 1
|
|
|
|
call_args = backtesting_mock.call_args[0][0]
|
2024-05-12 13:29:14 +00:00
|
|
|
assert call_args["config"] == ["config.json"]
|
|
|
|
assert call_args["verbosity"] == 0
|
|
|
|
assert call_args["command"] == "backtesting"
|
|
|
|
assert call_args["func"] is not None
|
|
|
|
assert callable(call_args["func"])
|
|
|
|
assert call_args["timeframe"] is None
|
2018-01-06 10:21:09 +00:00
|
|
|
|
|
|
|
|
2018-02-04 09:21:16 +00:00
|
|
|
def test_main_start_hyperopt(mocker) -> None:
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True]))
|
|
|
|
hyperopt_mock = mocker.patch("freqtrade.commands.start_hyperopt", MagicMock())
|
|
|
|
hyperopt_mock.__name__ = PropertyMock("start_hyperopt")
|
2019-05-28 20:25:53 +00:00
|
|
|
# it's sys.exit(0) at the end of hyperopt
|
|
|
|
with pytest.raises(SystemExit):
|
2024-05-12 13:29:14 +00:00
|
|
|
main(["hyperopt"])
|
2018-01-06 10:21:09 +00:00
|
|
|
assert hyperopt_mock.call_count == 1
|
|
|
|
call_args = hyperopt_mock.call_args[0][0]
|
2024-05-12 13:29:14 +00:00
|
|
|
assert call_args["config"] == ["config.json"]
|
|
|
|
assert call_args["verbosity"] == 0
|
|
|
|
assert call_args["command"] == "hyperopt"
|
|
|
|
assert call_args["func"] is not None
|
|
|
|
assert callable(call_args["func"])
|
2017-09-08 13:51:00 +00:00
|
|
|
|
|
|
|
|
2018-06-07 20:28:21 +00:00
|
|
|
def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
2018-06-17 20:38:31 +00:00
|
|
|
patch_exchange(mocker)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
|
|
|
|
mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=Exception))
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
|
2018-06-07 20:28:21 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
|
2018-03-03 21:39:39 +00:00
|
|
|
|
2018-02-04 09:21:16 +00:00
|
|
|
# Test Main + the KeyboardInterrupt exception
|
2018-06-07 20:28:21 +00:00
|
|
|
with pytest.raises(SystemExit):
|
2018-03-03 21:39:39 +00:00
|
|
|
main(args)
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
|
|
|
|
assert log_has("Fatal exception!", caplog)
|
2018-06-07 20:28:21 +00:00
|
|
|
|
2018-02-04 09:21:16 +00:00
|
|
|
|
2018-06-07 20:28:21 +00:00
|
|
|
def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
|
2018-06-17 20:38:31 +00:00
|
|
|
patch_exchange(mocker)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
|
|
|
|
mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=KeyboardInterrupt))
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
|
|
|
|
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
|
2018-06-07 20:28:21 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
|
2018-06-07 20:28:21 +00:00
|
|
|
|
|
|
|
# Test Main + the KeyboardInterrupt exception
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
main(args)
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
|
|
|
|
assert log_has("SIGINT received, aborting ...", caplog)
|
2018-06-07 20:28:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
2018-06-17 20:38:31 +00:00
|
|
|
patch_exchange(mocker)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
|
2019-03-29 23:19:43 +00:00
|
|
|
mocker.patch(
|
2024-05-12 13:29:14 +00:00
|
|
|
"freqtrade.worker.Worker._worker", MagicMock(side_effect=FreqtradeException("Oh snap!"))
|
2019-03-29 23:19:43 +00:00
|
|
|
)
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
|
2018-06-07 20:28:21 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
|
2018-06-07 20:28:21 +00:00
|
|
|
|
|
|
|
# Test Main + the KeyboardInterrupt exception
|
2018-02-04 09:21:16 +00:00
|
|
|
with pytest.raises(SystemExit):
|
2018-03-03 21:39:39 +00:00
|
|
|
main(args)
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
|
|
|
|
assert log_has("Oh snap!", caplog)
|
2018-06-09 02:29:48 +00:00
|
|
|
|
|
|
|
|
2020-05-08 10:34:24 +00:00
|
|
|
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
|
|
|
patch_exchange(mocker)
|
|
|
|
mocker.patch(
|
2024-10-03 03:33:52 +00:00
|
|
|
"freqtrade.exchange.list_available_exchanges",
|
2024-05-12 13:29:14 +00:00
|
|
|
MagicMock(side_effect=ValueError("Oh snap!")),
|
2020-05-08 10:34:24 +00:00
|
|
|
)
|
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = ["list-exchanges"]
|
2020-05-08 10:34:24 +00:00
|
|
|
|
|
|
|
# Test Main + the KeyboardInterrupt exception
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
main(args)
|
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has("Fatal exception!", caplog)
|
|
|
|
assert not log_has_re(r"SIGINT.*", caplog)
|
2020-05-08 10:34:24 +00:00
|
|
|
mocker.patch(
|
2024-10-03 03:33:52 +00:00
|
|
|
"freqtrade.exchange.list_available_exchanges",
|
2024-05-12 13:29:14 +00:00
|
|
|
MagicMock(side_effect=KeyboardInterrupt),
|
2020-05-08 10:34:24 +00:00
|
|
|
)
|
2020-05-08 12:24:30 +00:00
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
main(args)
|
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has_re(r"SIGINT.*", caplog)
|
2020-05-08 10:34:24 +00:00
|
|
|
|
|
|
|
|
2024-03-19 06:15:14 +00:00
|
|
|
def test_main_ConfigurationError(mocker, default_conf, caplog) -> None:
|
|
|
|
patch_exchange(mocker)
|
|
|
|
mocker.patch(
|
2024-10-03 03:33:52 +00:00
|
|
|
"freqtrade.exchange.list_available_exchanges",
|
2024-05-12 13:29:14 +00:00
|
|
|
MagicMock(side_effect=ConfigurationError("Oh snap!")),
|
2024-03-19 06:15:14 +00:00
|
|
|
)
|
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = ["list-exchanges"]
|
2024-03-19 06:15:14 +00:00
|
|
|
|
|
|
|
# Test Main + the KeyboardInterrupt exception
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
main(args)
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has_re("Configuration error: Oh snap!", caplog)
|
2024-03-19 06:15:14 +00:00
|
|
|
|
|
|
|
|
2020-06-09 21:03:55 +00:00
|
|
|
def test_main_reload_config(mocker, default_conf, caplog) -> None:
|
2018-06-17 20:38:31 +00:00
|
|
|
patch_exchange(mocker)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
|
2019-04-30 17:32:03 +00:00
|
|
|
# Simulate Running, reload, running workflow
|
2024-05-12 13:29:14 +00:00
|
|
|
worker_mock = MagicMock(
|
|
|
|
side_effect=[
|
|
|
|
State.RUNNING,
|
|
|
|
State.RELOAD_CONFIG,
|
|
|
|
State.RUNNING,
|
|
|
|
OperationalException("Oh snap!"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
mocker.patch("freqtrade.worker.Worker._worker", worker_mock)
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
|
|
|
reconfigure_mock = mocker.patch("freqtrade.worker.Worker._reconfigure", MagicMock())
|
2019-04-30 17:32:03 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
|
2018-06-09 02:29:48 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
args = Arguments(
|
|
|
|
["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
|
|
|
|
).get_parsed_arg()
|
2019-04-30 17:32:03 +00:00
|
|
|
worker = Worker(args=args, config=default_conf)
|
2018-06-09 02:29:48 +00:00
|
|
|
with pytest.raises(SystemExit):
|
2024-05-12 13:29:14 +00:00
|
|
|
main(["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"])
|
2018-06-09 02:29:48 +00:00
|
|
|
|
2024-05-12 13:29:14 +00:00
|
|
|
assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog)
|
2019-04-30 17:32:03 +00:00
|
|
|
assert worker_mock.call_count == 4
|
|
|
|
assert reconfigure_mock.call_count == 1
|
|
|
|
assert isinstance(worker.freqtrade, FreqtradeBot)
|
2018-06-09 02:29:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_reconfigure(mocker, default_conf) -> None:
|
2018-06-17 20:38:31 +00:00
|
|
|
patch_exchange(mocker)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock())
|
2019-03-29 23:19:43 +00:00
|
|
|
mocker.patch(
|
2024-05-12 13:29:14 +00:00
|
|
|
"freqtrade.worker.Worker._worker", MagicMock(side_effect=OperationalException("Oh snap!"))
|
2019-03-29 23:19:43 +00:00
|
|
|
)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock())
|
|
|
|
mocker.patch("freqtrade.freqtradebot.init_db", MagicMock())
|
|
|
|
|
|
|
|
args = Arguments(
|
|
|
|
["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]
|
|
|
|
).get_parsed_arg()
|
2019-03-26 09:42:19 +00:00
|
|
|
worker = Worker(args=args, config=default_conf)
|
|
|
|
freqtrade = worker.freqtrade
|
2018-06-09 02:29:48 +00:00
|
|
|
|
|
|
|
# Renew mock to return modified data
|
|
|
|
conf = deepcopy(default_conf)
|
2024-05-12 13:29:14 +00:00
|
|
|
conf["stake_amount"] += 1
|
2019-07-11 21:39:42 +00:00
|
|
|
patched_configuration_load_config_file(mocker, conf)
|
2018-06-09 02:29:48 +00:00
|
|
|
|
2019-03-26 09:42:19 +00:00
|
|
|
worker._config = conf
|
2018-06-09 02:29:48 +00:00
|
|
|
# reconfigure should return a new instance
|
2019-03-26 09:42:19 +00:00
|
|
|
worker._reconfigure()
|
|
|
|
freqtrade2 = worker.freqtrade
|
2018-06-09 02:29:48 +00:00
|
|
|
|
|
|
|
# Verify we have a new instance with the new config
|
|
|
|
assert freqtrade is not freqtrade2
|
2024-05-12 13:29:14 +00:00
|
|
|
assert freqtrade.config["stake_amount"] + 1 == freqtrade2.config["stake_amount"]
|