(self, method_name, exchange, args=[], is_public=False)
| 279 | return final_skips |
| 280 | |
| 281 | async def test_safe(self, method_name, exchange, args=[], is_public=False): |
| 282 | # `testSafe` method does not throw an exception, instead mutes it. The reason we |
| 283 | # mute the thrown exceptions here is because we don't want to stop the whole |
| 284 | # tests queue if any single test-method fails. Instead, they are echoed with |
| 285 | # formatted message "[TEST_FAILURE] ..." and that output is then regex-matched by |
| 286 | # run-tests.js, so the exceptions are still printed out to console from there. |
| 287 | max_retries = 3 |
| 288 | args_stringified = exchange.json(args) # args.join() breaks when we provide a list of symbols or multidimensional array; "args.toString()" breaks bcz of "array to string conversion" |
| 289 | for i in range(0, max_retries): |
| 290 | try: |
| 291 | await self.test_method(method_name, exchange, args, is_public) |
| 292 | return True |
| 293 | except Exception as ex: |
| 294 | e = get_root_exception(ex) |
| 295 | is_load_markets = (method_name == 'loadMarkets') |
| 296 | is_auth_error = (isinstance(e, AuthenticationError)) |
| 297 | is_not_supported = (isinstance(e, NotSupported)) |
| 298 | is_operation_failed = (isinstance(e, OperationFailed)) # includes "DDoSProtection", "RateLimitExceeded", "RequestTimeout", "ExchangeNotAvailable", "OperationFailed", "InvalidNonce", ... |
| 299 | last_url_msg = '' if self.ws_tests else ' (Last url: ' + exchange.last_request_url + ' )' |
| 300 | if is_operation_failed: |
| 301 | # if last retry was gone with same `tempFailure` error, then let's eventually return false |
| 302 | if i == max_retries - 1: |
| 303 | is_on_maintenance = (isinstance(e, OnMaintenance)) |
| 304 | is_exchange_not_available = (isinstance(e, ExchangeNotAvailable)) |
| 305 | should_fail = None |
| 306 | ret_success = None |
| 307 | if is_load_markets: |
| 308 | # if "loadMarkets" does not succeed, we must return "false" to caller method, to stop tests continual |
| 309 | ret_success = False |
| 310 | # we might not break exchange tests, if exchange is on maintenance at this moment |
| 311 | if is_on_maintenance: |
| 312 | should_fail = False |
| 313 | else: |
| 314 | should_fail = True |
| 315 | else: |
| 316 | # for any other method tests: |
| 317 | if is_exchange_not_available and not is_on_maintenance: |
| 318 | # break exchange tests if "ExchangeNotAvailable" exception is thrown, but it's not maintenance |
| 319 | should_fail = True |
| 320 | ret_success = False |
| 321 | else: |
| 322 | # in all other cases of OperationFailed, show Warning, but don't mark test as failed |
| 323 | should_fail = False |
| 324 | ret_success = True |
| 325 | # output the message |
| 326 | fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]' |
| 327 | dump(fail_type, exchange.id, method_name, args_stringified, last_url_msg, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exception_message(e)) |
| 328 | return ret_success |
| 329 | else: |
| 330 | # wait and retry again |
| 331 | # (increase wait time on every retry) |
| 332 | await exchange.sleep((i + 1) * 1000) |
| 333 | else: |
| 334 | # if it's loadMarkets, then fail test, because it's mandatory for tests |
| 335 | if is_load_markets: |
| 336 | dump('[TEST_FAILURE]', exchange.id, method_name, args_stringified, last_url_msg, 'Exchange can not load markets', exception_message(e)) |
| 337 | return False |
| 338 | # if the specific arguments to the test method throws "NotSupported" exception |
no test coverage detected