Example #1
0
class ConnectCommandTest(unittest.TestCase):
    @patch("hummingbot.core.utils.trading_pair_fetcher.TradingPairFetcher")
    def setUp(self, _: MagicMock) -> None:
        super().setUp()
        self.ev_loop = asyncio.get_event_loop()

        self.async_run_with_timeout(read_system_configs_from_yml())

        self.app = HummingbotApplication()
        self.cli_mock_assistant = CLIMockingAssistant(self.app.app)
        self.cli_mock_assistant.start()
        self.global_config_backup = deepcopy(global_config_map)

    def tearDown(self) -> None:
        self.cli_mock_assistant.stop()
        self.reset_global_config()
        Security._decryption_done.clear()
        super().tearDown()

    def reset_global_config(self):
        for key, value in self.global_config_backup.items():
            global_config_map[key] = value

    @staticmethod
    def get_async_sleep_fn(delay: float):
        async def async_sleep(*_, **__):
            await asyncio.sleep(delay)

        return async_sleep

    def async_run_with_timeout(self, coroutine: Awaitable, timeout: float = 1):
        ret = self.ev_loop.run_until_complete(
            asyncio.wait_for(coroutine, timeout))
        return ret

    def async_run_with_timeout_coroutine_must_raise_timeout(
            self, coroutine: Awaitable, timeout: float = 1):
        class DesiredError(Exception):
            pass

        async def run_coro_that_raises(coro: Awaitable):
            try:
                await coro
            except asyncio.TimeoutError:
                raise DesiredError

        try:
            self.async_run_with_timeout(run_coro_that_raises(coroutine),
                                        timeout)
        except DesiredError:  # the coroutine raised an asyncio.TimeoutError as expected
            raise asyncio.TimeoutError
        except asyncio.TimeoutError:  # the coroutine did not finish on time
            raise RuntimeError

    @patch("hummingbot.client.config.security.Security.update_secure_config")
    @patch("hummingbot.client.config.security.Security.encrypted_file_exists")
    @patch("hummingbot.client.config.security.Security.api_keys")
    @patch("hummingbot.user.user_balances.UserBalances.add_exchange")
    def test_connect_exchange_success(
        self,
        add_exchange_mock: AsyncMock,
        api_keys_mock: AsyncMock,
        encrypted_file_exists_mock: MagicMock,
        _: MagicMock,
    ):
        add_exchange_mock.return_value = None
        exchange = "binance"
        api_key = "someKey"
        api_secret = "someSecret"
        api_keys_mock.return_value = {
            "binance_api_key": api_key,
            "binance_api_secret": api_secret
        }
        encrypted_file_exists_mock.return_value = False
        global_config_map["other_commands_timeout"].value = 30
        self.cli_mock_assistant.queue_prompt_reply(api_key)  # binance API key
        self.cli_mock_assistant.queue_prompt_reply(
            api_secret)  # binance API secret

        self.async_run_with_timeout(self.app.connect_exchange(exchange))
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=f"\nYou are now connected to {exchange}."))
        self.assertFalse(self.app.placeholder_mode)
        self.assertFalse(self.app.app.hide_input)

    @patch("hummingbot.client.config.security.Security.update_secure_config")
    @patch("hummingbot.client.config.security.Security.encrypted_file_exists")
    @patch("hummingbot.client.config.security.Security.api_keys")
    @patch("hummingbot.user.user_balances.UserBalances.add_exchange")
    def test_connect_exchange_handles_network_timeouts(
        self,
        add_exchange_mock: AsyncMock,
        api_keys_mock: AsyncMock,
        encrypted_file_exists_mock: MagicMock,
        _: MagicMock,
    ):
        add_exchange_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01
        api_key = "someKey"
        api_secret = "someSecret"
        api_keys_mock.return_value = {
            "binance_api_key": api_key,
            "binance_api_secret": api_secret
        }
        encrypted_file_exists_mock.return_value = False
        self.cli_mock_assistant.queue_prompt_reply(api_key)  # binance API key
        self.cli_mock_assistant.queue_prompt_reply(
            api_secret)  # binance API secret

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connect_exchange("binance"))
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection to complete. See logs for more details."
            ))
        self.assertFalse(self.app.placeholder_mode)
        self.assertFalse(self.app.app.hide_input)

    @patch("hummingbot.user.user_balances.UserBalances.update_exchanges")
    def test_connection_df_handles_network_timeouts(
            self, update_exchanges_mock: AsyncMock):
        update_exchanges_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connection_df())
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection table to populate. See logs for more details."
            ))

    @patch("hummingbot.user.user_balances.UserBalances.update_exchanges")
    def test_connection_df_handles_network_timeouts_logs_hidden(
            self, update_exchanges_mock: AsyncMock):
        self.cli_mock_assistant.toggle_logs()

        update_exchanges_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connection_df())
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection table to populate. See logs for more details."
            ))

    @patch(
        "hummingbot.client.hummingbot_application.HummingbotApplication._notify"
    )
    @patch(
        "hummingbot.client.hummingbot_application.HummingbotApplication.connection_df"
    )
    def test_show_connections(self, connection_df_mock, notify_mock):
        global_config_map["tables_format"].value = "psql"

        Security._decryption_done.set()

        captures = []
        notify_mock.side_effect = lambda s: captures.append(s)

        connections_df = pd.DataFrame(
            columns=pd.Index(
                ['Exchange', '  Keys Added', '  Keys Confirmed', '  Status'],
                dtype='object'),
            data=[["ascend_ex", "Yes", "Yes", "&cYELLOW"],
                  ["beaxy", "Yes", "Yes", "&cGREEN"]])
        connection_df_mock.return_value = (connections_df, [])

        self.async_run_with_timeout(self.app.show_connections())

        self.assertEqual(2, len(captures))
        self.assertEqual("\nTesting connections, please wait...", captures[0])

        df_str_expected = (
            "    +------------+----------------+--------------------+------------+"
            "\n    | Exchange   |   Keys Added   |   Keys Confirmed   |   Status   |"
            "\n    |------------+----------------+--------------------+------------|"
            "\n    | ascend_ex  | Yes            | Yes                | &cYELLOW   |"
            "\n    | beaxy      | Yes            | Yes                | &cGREEN    |"
            "\n    +------------+----------------+--------------------+------------+"
        )

        self.assertEqual(df_str_expected, captures[1])
Example #2
0
class ConnectCommandTest(unittest.TestCase):
    @patch("hummingbot.core.utils.trading_pair_fetcher.TradingPairFetcher")
    def setUp(self, _: MagicMock) -> None:
        super().setUp()
        self.ev_loop = asyncio.get_event_loop()

        self.async_run_with_timeout(read_system_configs_from_yml())

        self.app = HummingbotApplication()
        self.cli_mock_assistant = CLIMockingAssistant(self.app.app)
        self.cli_mock_assistant.start()
        self.global_config_backup = deepcopy(global_config_map)

    def tearDown(self) -> None:
        self.cli_mock_assistant.stop()
        self.reset_global_config()
        super().tearDown()

    def reset_global_config(self):
        for key, value in self.global_config_backup.items():
            global_config_map[key] = value

    @staticmethod
    def get_async_sleep_fn(delay: float):
        async def async_sleep(*_, **__):
            await asyncio.sleep(delay)

        return async_sleep

    def async_run_with_timeout(self, coroutine: Awaitable, timeout: float = 1):
        ret = self.ev_loop.run_until_complete(
            asyncio.wait_for(coroutine, timeout))
        return ret

    def async_run_with_timeout_coroutine_must_raise_timeout(
            self, coroutine: Awaitable, timeout: float = 1):
        class DesiredError(Exception):
            pass

        async def run_coro_that_raises(coro: Awaitable):
            try:
                await coro
            except asyncio.TimeoutError:
                raise DesiredError

        try:
            self.async_run_with_timeout(run_coro_that_raises(coroutine),
                                        timeout)
        except DesiredError:  # the coroutine raised an asyncio.TimeoutError as expected
            raise asyncio.TimeoutError
        except asyncio.TimeoutError:  # the coroutine did not finish on time
            raise RuntimeError

    @patch("hummingbot.client.config.security.Security.update_secure_config")
    @patch("hummingbot.client.config.security.Security.encrypted_file_exists")
    @patch("hummingbot.client.config.security.Security.api_keys")
    @patch("hummingbot.user.user_balances.UserBalances.add_exchange")
    def test_connect_exchange_success(
        self,
        add_exchange_mock: AsyncMock,
        api_keys_mock: AsyncMock,
        encrypted_file_exists_mock: MagicMock,
        _: MagicMock,
    ):
        add_exchange_mock.return_value = None
        exchange = "binance"
        api_key = "someKey"
        api_secret = "someSecret"
        api_keys_mock.return_value = {
            "binance_api_key": api_key,
            "binance_api_secret": api_secret
        }
        encrypted_file_exists_mock.return_value = False
        global_config_map["other_commands_timeout"].value = 30
        self.cli_mock_assistant.queue_prompt_reply(api_key)  # binance API key
        self.cli_mock_assistant.queue_prompt_reply(
            api_secret)  # binance API secret

        self.async_run_with_timeout(self.app.connect_exchange(exchange))
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=f"\nYou are now connected to {exchange}."))
        self.assertFalse(self.app.placeholder_mode)
        self.assertFalse(self.app.app.hide_input)

    @patch("hummingbot.client.config.security.Security.update_secure_config")
    @patch("hummingbot.client.config.security.Security.encrypted_file_exists")
    @patch("hummingbot.client.config.security.Security.api_keys")
    @patch("hummingbot.user.user_balances.UserBalances.add_exchange")
    def test_connect_exchange_handles_network_timeouts(
        self,
        add_exchange_mock: AsyncMock,
        api_keys_mock: AsyncMock,
        encrypted_file_exists_mock: MagicMock,
        _: MagicMock,
    ):
        add_exchange_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01
        api_key = "someKey"
        api_secret = "someSecret"
        api_keys_mock.return_value = {
            "binance_api_key": api_key,
            "binance_api_secret": api_secret
        }
        encrypted_file_exists_mock.return_value = False
        self.cli_mock_assistant.queue_prompt_reply(api_key)  # binance API key
        self.cli_mock_assistant.queue_prompt_reply(
            api_secret)  # binance API secret

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connect_exchange("binance"))
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection to complete. See logs for more details."
            ))
        self.assertFalse(self.app.placeholder_mode)
        self.assertFalse(self.app.app.hide_input)

    @patch("hummingbot.user.user_balances.UserBalances.update_exchanges")
    def test_connection_df_handles_network_timeouts(
            self, update_exchanges_mock: AsyncMock):
        update_exchanges_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connection_df())
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection table to populate. See logs for more details."
            ))

    @patch("hummingbot.user.user_balances.UserBalances.update_exchanges")
    def test_connection_df_handles_network_timeouts_logs_hidden(
            self, update_exchanges_mock: AsyncMock):
        self.cli_mock_assistant.toggle_logs()

        update_exchanges_mock.side_effect = self.get_async_sleep_fn(delay=0.02)
        global_config_map["other_commands_timeout"].value = 0.01

        with self.assertRaises(asyncio.TimeoutError):
            self.async_run_with_timeout_coroutine_must_raise_timeout(
                self.app.connection_df())
        self.assertTrue(
            self.cli_mock_assistant.check_log_called_with(
                msg=
                "\nA network error prevented the connection table to populate. See logs for more details."
            ))