|
5 | 5 |
|
6 | 6 | from __future__ import annotations |
7 | 7 |
|
| 8 | +import logging |
| 9 | +import unittest.mock |
8 | 10 | from logging import getLogger |
9 | 11 |
|
| 12 | +import pytest |
| 13 | + |
| 14 | +import snowflake.connector |
10 | 15 | from snowflake.connector import errorcode, errors |
11 | | -from snowflake.connector.network import SnowflakeRestful |
| 16 | +from snowflake.connector.network import ( |
| 17 | + QUERY_IN_PROGRESS_ASYNC_CODE, |
| 18 | + QUERY_IN_PROGRESS_CODE, |
| 19 | + SnowflakeRestful, |
| 20 | +) |
12 | 21 |
|
13 | 22 | logger = getLogger(__name__) |
14 | 23 |
|
@@ -36,3 +45,56 @@ def test_no_auth(db_parameters): |
36 | 45 | assert e.errno == errorcode.ER_CONNECTION_IS_CLOSED |
37 | 46 | finally: |
38 | 47 | rest.close() |
| 48 | + |
| 49 | + |
| 50 | +@pytest.mark.skipolddriver |
| 51 | +@pytest.mark.parametrize( |
| 52 | + "query_return_code", [QUERY_IN_PROGRESS_CODE, QUERY_IN_PROGRESS_ASYNC_CODE] |
| 53 | +) |
| 54 | +def test_none_object_when_querying_result(db_parameters, caplog, query_return_code): |
| 55 | + # this test simulate the case where the response from the server is None |
| 56 | + # the following events happen in sequence: |
| 57 | + # 1. we send a simple query to the server which is a post request |
| 58 | + # 2. we record the query result in a global variable |
| 59 | + # 3. we mock return a query in progress code and an url to fetch the query result |
| 60 | + # 4. we return None for the fetching query result request for the first time |
| 61 | + # 5. for the second time, we return the code for the query result |
| 62 | + # 6. in the end, we assert the result, and retry has taken place when result is None by checking logging |
| 63 | + |
| 64 | + original_request_exec = SnowflakeRestful._request_exec |
| 65 | + expected_ret = None |
| 66 | + get_executed_time = 0 |
| 67 | + |
| 68 | + def side_effect_request_exec(self, *args, **kwargs): |
| 69 | + nonlocal expected_ret, get_executed_time |
| 70 | + # 1. we send a simple query to the server which is a post request |
| 71 | + if "queries/v1/query-request" in kwargs["full_url"]: |
| 72 | + ret = original_request_exec(self, *args, **kwargs) |
| 73 | + expected_ret = ret # 2. we record the query result in a global variable |
| 74 | + # 3. we mock return a query in progress code and an url to fetch the query result |
| 75 | + return { |
| 76 | + "code": query_return_code, |
| 77 | + "data": {"getResultUrl": "/queries/123/result"}, |
| 78 | + } |
| 79 | + |
| 80 | + if "/queries/123/result" in kwargs["full_url"]: |
| 81 | + if get_executed_time == 0: |
| 82 | + # 4. we return None for the 1st time fetching query result request, this should trigger retry |
| 83 | + get_executed_time += 1 |
| 84 | + return None |
| 85 | + else: |
| 86 | + # 5. for the second time, we return the code for the query result, this indicates retry success |
| 87 | + return expected_ret |
| 88 | + |
| 89 | + with snowflake.connector.connect( |
| 90 | + **db_parameters |
| 91 | + ) as conn, conn.cursor() as cursor, caplog.at_level(logging.INFO): |
| 92 | + with unittest.mock.patch.object( |
| 93 | + SnowflakeRestful, "_request_exec", new=side_effect_request_exec |
| 94 | + ): |
| 95 | + # 6. in the end, we assert the result, and retry has taken place when result is None by checking logging |
| 96 | + assert cursor.execute("select 1").fetchone() == (1,) |
| 97 | + assert ( |
| 98 | + "fetch query status failed and http request returned None, this is usually caused by transient network failures, retrying" |
| 99 | + in caplog.text |
| 100 | + ) |
0 commit comments