From bb119810cca72cafb7e21df453fd683154c3e059 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Sun, 1 Sep 2024 17:07:06 +0200 Subject: [PATCH 1/2] [Tests] add test_invalid_api_key_error exchange test --- additional_tests/exchanges_tests/__init__.py | 38 +++++++++++++++---- .../abstract_authenticated_exchange_tester.py | 16 +++++++- .../exchanges_tests/test_ascendex.py | 3 ++ .../exchanges_tests/test_binance.py | 3 ++ .../exchanges_tests/test_binance_futures.py | 3 ++ .../exchanges_tests/test_bingx.py | 3 ++ .../exchanges_tests/test_bitget.py | 3 ++ .../exchanges_tests/test_bitmart.py | 3 ++ .../exchanges_tests/test_bybit.py | 3 ++ .../exchanges_tests/test_bybit_futures.py | 3 ++ .../exchanges_tests/test_coinbase.py | 3 ++ .../exchanges_tests/test_coinex.py | 3 ++ .../exchanges_tests/test_cryptocom.py | 3 ++ .../exchanges_tests/test_gateio.py | 4 ++ .../exchanges_tests/test_hollaex.py | 3 ++ additional_tests/exchanges_tests/test_htx.py | 3 ++ .../exchanges_tests/test_kucoin.py | 3 ++ .../exchanges_tests/test_kucoin_futures.py | 3 ++ additional_tests/exchanges_tests/test_mexc.py | 3 ++ additional_tests/exchanges_tests/test_okx.py | 3 ++ .../exchanges_tests/test_okx_futures.py | 3 ++ .../exchanges_tests/test_phemex.py | 3 ++ 22 files changed, 106 insertions(+), 9 deletions(-) diff --git a/additional_tests/exchanges_tests/__init__.py b/additional_tests/exchanges_tests/__init__.py index 65ad3e142..f351a2809 100644 --- a/additional_tests/exchanges_tests/__init__.py +++ b/additional_tests/exchanges_tests/__init__.py @@ -22,6 +22,7 @@ import octobot_commons.constants as commons_constants import octobot_commons.asyncio_tools as asyncio_tools import octobot_commons.os_util as os_util +import octobot_commons.configuration as configuration import octobot_commons.tests.test_config as test_config import octobot_trading.api as trading_api import octobot_trading.exchanges as exchanges @@ -61,15 +62,18 @@ def get_name(self): @contextlib.asynccontextmanager -async def get_authenticated_exchange_manager(exchange_name, exchange_tentacle_name, config=None, - credentials_exchange_name=None, market_filter=None): +async def get_authenticated_exchange_manager( + exchange_name, exchange_tentacle_name, config=None, + credentials_exchange_name=None, market_filter=None, + use_invalid_creds=False +): credentials_exchange_name = credentials_exchange_name or exchange_name _load_exchange_creds_env_variables_if_necessary() config = {**test_config.load_test_config(), **config} if config else test_config.load_test_config() if exchange_name not in config[commons_constants.CONFIG_EXCHANGES]: config[commons_constants.CONFIG_EXCHANGES][exchange_name] = {} config[commons_constants.CONFIG_EXCHANGES][exchange_name].update(_get_exchange_auth_details( - credentials_exchange_name + credentials_exchange_name, use_invalid_creds )) exchange_type = config[commons_constants.CONFIG_EXCHANGES][exchange_name].get( commons_constants.CONFIG_EXCHANGE_TYPE, exchanges.get_default_exchange_type(exchange_name)) @@ -129,15 +133,35 @@ def _load_exchange_creds_env_variables_if_necessary(): LOADED_EXCHANGE_CREDS_ENV_VARIABLES = True -def _get_exchange_auth_details(exchange_name): - return { +def _get_exchange_auth_details(exchange_name, use_invalid_creds): + config = { commons_constants.CONFIG_EXCHANGE_KEY: _get_exchange_credential_from_env(exchange_name, commons_constants.CONFIG_EXCHANGE_KEY), - commons_constants.CONFIG_EXCHANGE_SECRET: - _get_exchange_credential_from_env(exchange_name, commons_constants.CONFIG_EXCHANGE_SECRET), + commons_constants.CONFIG_EXCHANGE_SECRET: _get_exchange_credential_from_env( + exchange_name, commons_constants.CONFIG_EXCHANGE_SECRET + ), commons_constants.CONFIG_EXCHANGE_PASSWORD: _get_exchange_credential_from_env(exchange_name, commons_constants.CONFIG_EXCHANGE_PASSWORD), } + if use_invalid_creds: + _invalidate(config) + return config + + +def _invalidate(exchange_config: dict): + # try to invalidate key while returning a "plausible" value to avoid signature / parsing issues + decoded = configuration.decrypt_element_if_possible( + commons_constants.CONFIG_EXCHANGE_KEY, exchange_config, None + ) + updated_decoded = decoded + for numb in range(9): + if str(numb) in decoded: + number_index = decoded.index(str(numb)) + updated_decoded = f"{decoded[:number_index]}{numb + 1}{decoded[number_index+1:]}" + break + if updated_decoded == decoded: + raise ValueError("No number to invalid api key") + exchange_config[commons_constants.CONFIG_EXCHANGE_KEY] = configuration.encrypt(updated_decoded).decode() def _get_exchange_credential_from_env(exchange_name, cred_suffix): diff --git a/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py b/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py index fb8131397..666d93c17 100644 --- a/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py +++ b/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py @@ -143,6 +143,17 @@ async def inner_test_get_account_id(self): with pytest.raises(NotImplementedError): await self.exchange_manager.exchange.get_account_id() + async def test_invalid_api_key_error(self): + with pytest.raises(trading_errors.AuthenticationError): + created_exchange = mock.Mock() + async with self.local_exchange_manager(use_invalid_creds=True): + created_exchange() + # should fail + await self.get_portfolio() + raise AssertionError("Did not raise") + # ensure self.local_exchange_manager did not raise + created_exchange.assert_called_once() + async def test_get_api_key_permissions(self): async with self.local_exchange_manager(): await self.inner_test_get_api_key_permissions() @@ -735,7 +746,7 @@ async def get_price(self, symbol=None): )) @contextlib.asynccontextmanager - async def local_exchange_manager(self, market_filter=None, identifiers_suffix=None): + async def local_exchange_manager(self, market_filter=None, identifiers_suffix=None, use_invalid_creds=False): try: exchange_tentacle_name = self.EXCHANGE_TENTACLE_NAME or self.EXCHANGE_NAME.capitalize() credentials_exchange_name = self.CREDENTIALS_EXCHANGE_NAME or self.EXCHANGE_NAME @@ -746,7 +757,8 @@ async def local_exchange_manager(self, market_filter=None, identifiers_suffix=No exchange_tentacle_name, self.get_config(), credentials_exchange_name=credentials_exchange_name, - market_filter=market_filter + market_filter=market_filter, + use_invalid_creds=use_invalid_creds, ) as exchange_manager: self.exchange_manager = exchange_manager yield diff --git a/additional_tests/exchanges_tests/test_ascendex.py b/additional_tests/exchanges_tests/test_ascendex.py index 4fbb5311b..847477df1 100644 --- a/additional_tests/exchanges_tests/test_ascendex.py +++ b/additional_tests/exchanges_tests/test_ascendex.py @@ -44,6 +44,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_binance.py b/additional_tests/exchanges_tests/test_binance.py index bb12ba7d6..2e4363c40 100644 --- a/additional_tests/exchanges_tests/test_binance.py +++ b/additional_tests/exchanges_tests/test_binance.py @@ -47,6 +47,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_binance_futures.py b/additional_tests/exchanges_tests/test_binance_futures.py index 5cedfdd9c..4cc7e4a72 100644 --- a/additional_tests/exchanges_tests/test_binance_futures.py +++ b/additional_tests/exchanges_tests/test_binance_futures.py @@ -51,6 +51,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_bingx.py b/additional_tests/exchanges_tests/test_bingx.py index 6d87a2054..257479205 100644 --- a/additional_tests/exchanges_tests/test_bingx.py +++ b/additional_tests/exchanges_tests/test_bingx.py @@ -46,6 +46,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_bitget.py b/additional_tests/exchanges_tests/test_bitget.py index 7e90c1360..628be48cf 100644 --- a/additional_tests/exchanges_tests/test_bitget.py +++ b/additional_tests/exchanges_tests/test_bitget.py @@ -46,6 +46,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_bitmart.py b/additional_tests/exchanges_tests/test_bitmart.py index f42ba8684..82ae6d604 100644 --- a/additional_tests/exchanges_tests/test_bitmart.py +++ b/additional_tests/exchanges_tests/test_bitmart.py @@ -44,6 +44,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_bybit.py b/additional_tests/exchanges_tests/test_bybit.py index f2aceae70..0a5782030 100644 --- a/additional_tests/exchanges_tests/test_bybit.py +++ b/additional_tests/exchanges_tests/test_bybit.py @@ -48,6 +48,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_bybit_futures.py b/additional_tests/exchanges_tests/test_bybit_futures.py index d447609f8..e30453855 100644 --- a/additional_tests/exchanges_tests/test_bybit_futures.py +++ b/additional_tests/exchanges_tests/test_bybit_futures.py @@ -51,6 +51,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_coinbase.py b/additional_tests/exchanges_tests/test_coinbase.py index fb9cee9df..01532a1ab 100644 --- a/additional_tests/exchanges_tests/test_coinbase.py +++ b/additional_tests/exchanges_tests/test_coinbase.py @@ -44,6 +44,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_coinex.py b/additional_tests/exchanges_tests/test_coinex.py index 5fe7a67c9..536697e85 100644 --- a/additional_tests/exchanges_tests/test_coinex.py +++ b/additional_tests/exchanges_tests/test_coinex.py @@ -43,6 +43,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_cryptocom.py b/additional_tests/exchanges_tests/test_cryptocom.py index 6f0e9bbe1..43c0e7795 100644 --- a/additional_tests/exchanges_tests/test_cryptocom.py +++ b/additional_tests/exchanges_tests/test_cryptocom.py @@ -44,6 +44,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_gateio.py b/additional_tests/exchanges_tests/test_gateio.py index 94899e31c..bf466f5f5 100644 --- a/additional_tests/exchanges_tests/test_gateio.py +++ b/additional_tests/exchanges_tests/test_gateio.py @@ -45,6 +45,10 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + # await super().test_invalid_api_key_error() # raises Request timeout + pass + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_hollaex.py b/additional_tests/exchanges_tests/test_hollaex.py index 56311f7b0..c445ed702 100644 --- a/additional_tests/exchanges_tests/test_hollaex.py +++ b/additional_tests/exchanges_tests/test_hollaex.py @@ -46,6 +46,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_htx.py b/additional_tests/exchanges_tests/test_htx.py index 77a7f5cf8..ac9b56106 100644 --- a/additional_tests/exchanges_tests/test_htx.py +++ b/additional_tests/exchanges_tests/test_htx.py @@ -45,6 +45,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_kucoin.py b/additional_tests/exchanges_tests/test_kucoin.py index da70b469f..8aa92be46 100644 --- a/additional_tests/exchanges_tests/test_kucoin.py +++ b/additional_tests/exchanges_tests/test_kucoin.py @@ -47,6 +47,9 @@ async def test_create_and_cancel_limit_orders(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_kucoin_futures.py b/additional_tests/exchanges_tests/test_kucoin_futures.py index ef4eddb15..943b35a30 100644 --- a/additional_tests/exchanges_tests/test_kucoin_futures.py +++ b/additional_tests/exchanges_tests/test_kucoin_futures.py @@ -48,6 +48,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_mexc.py b/additional_tests/exchanges_tests/test_mexc.py index d2c09ccbb..ad95718c7 100644 --- a/additional_tests/exchanges_tests/test_mexc.py +++ b/additional_tests/exchanges_tests/test_mexc.py @@ -45,6 +45,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass diff --git a/additional_tests/exchanges_tests/test_okx.py b/additional_tests/exchanges_tests/test_okx.py index 9facd1215..16831d6f3 100644 --- a/additional_tests/exchanges_tests/test_okx.py +++ b/additional_tests/exchanges_tests/test_okx.py @@ -47,6 +47,9 @@ async def test_create_and_cancel_limit_orders(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_okx_futures.py b/additional_tests/exchanges_tests/test_okx_futures.py index ddc336412..9897bb189 100644 --- a/additional_tests/exchanges_tests/test_okx_futures.py +++ b/additional_tests/exchanges_tests/test_okx_futures.py @@ -46,6 +46,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_get_account_id(self): await super().test_get_account_id() + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): await super().test_get_api_key_permissions() diff --git a/additional_tests/exchanges_tests/test_phemex.py b/additional_tests/exchanges_tests/test_phemex.py index 052280d03..44df6e6ee 100644 --- a/additional_tests/exchanges_tests/test_phemex.py +++ b/additional_tests/exchanges_tests/test_phemex.py @@ -43,6 +43,9 @@ async def test_get_account_id(self): # pass if not implemented pass + async def test_invalid_api_key_error(self): + await super().test_invalid_api_key_error() + async def test_get_api_key_permissions(self): # pass if not implemented pass From 8c181caad5867cc8fa2e008f111eb298b773901a Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Sun, 1 Sep 2024 17:07:23 +0200 Subject: [PATCH 2/2] [Requirements] bump --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f95dda72e..e31a79b37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # Drakkar-Software requirements OctoBot-Commons==1.9.58 -OctoBot-Trading==2.4.108 +OctoBot-Trading==2.4.109 OctoBot-Evaluators==1.9.6 OctoBot-Tentacles-Manager==2.9.16 OctoBot-Services==1.6.17