Skip to content

Commit

Permalink
test exception retry handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mickjermsurawong-stripe committed Sep 7, 2018
1 parent f848656 commit 3a40824
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 15 deletions.
5 changes: 3 additions & 2 deletions stripe/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ def request(self, method, url, headers, post_data=None):

def _handle_request_error(self, e):

# Catch SSL error first as it belongs to ConnectionError
# Catch SSL error first as it belongs to ConnectionError,
# but we don't want to retry
if isinstance(e, requests.exceptions.SSLError):
msg = ("Could not verify Stripe's SSL certificate. Please make "
"sure that your network is not intercepting certificates. "
Expand All @@ -222,7 +223,7 @@ def _handle_request_error(self, e):
"support@stripe.com.")
err = "%s: %s" % (type(e).__name__, str(e))
should_retry = True
# Catch all request specific with descriptive class/messages
# Catch remaining request exceptions
elif isinstance(e, requests.exceptions.RequestException):
msg = ("Unexpected error communicating with Stripe. "
"If this problem persists, let us know at "
Expand Down
5 changes: 2 additions & 3 deletions tests/test_api_requestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def _user_agent_match(self, other):
def _idempotency_key_match(self, other):
if self.idempotency_key is not None:
return other['Idempotency-Key'] == self.idempotency_key

return True

def _x_stripe_ua_contains_app_info(self, other):
Expand Down Expand Up @@ -454,8 +453,8 @@ def test_uses_given_idempotency_key(self, requestor, mock_response,
)
check_call(meth, headers=header_matcher, post_data='')

def test_create_uuid4_idempotency_key(self, requestor, mock_response,
check_call):
def test_uuid4_idempotency_key_when_not_given(self, requestor,
mock_response, check_call):
mock_response('{}', 200)
meth = 'post'
requestor.request(meth, self.valid_path, {})
Expand Down
47 changes: 37 additions & 10 deletions tests/test_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def mock_response(mock, body, code):
@pytest.fixture
def mock_error(self, mocker, session):
def mock_error(mock):
# The first kind of request exceptions we catch
mock.exceptions.SSLError = Exception
session.request.side_effect = mock.exceptions.SSLError()
mock.Session = mocker.MagicMock(return_value=session)
Expand Down Expand Up @@ -231,7 +232,7 @@ def test_timeout(self, request_mock, mock_response, check_call):
check_call(None, 'POST', self.valid_url, data, headers, timeout=5)


class TestRetryRequestClient(TestRequestsClient):
class TestRequestClientRetryBehavior(TestRequestsClient):

@pytest.fixture
def response(self, mocker):
Expand All @@ -250,10 +251,8 @@ def mock_retry(retry_error_num=0,

# Mocking classes of exception we catch
# Any other exceptions with the same inheritance will work
# We consider request exceptions first, and differentiate which
# specific ones to retry.
root_error_class = Exception
request_mock.exceptions.RequestException = root_error_class
request_root_error_class = Exception
request_mock.exceptions.RequestException = request_root_error_class

no_retry_parent_class = LookupError
no_retry_child_class = KeyError
Expand All @@ -272,7 +271,7 @@ def mock_retry(retry_error_num=0,
responses

request_mock.Session = mocker.MagicMock(return_value=session)

return request_mock
return mock_retry

@pytest.fixture
Expand All @@ -287,6 +286,7 @@ def make_request(self):
client = self.REQUEST_CLIENT(verify_ssl_certs=True,
timeout=80,
proxy='http://slap/')
# Override sleep time to speed up tests
client._sleep_time = lambda _: 0.001
return client.request_with_retry('GET', self.valid_url, {}, None)

Expand All @@ -302,14 +302,14 @@ def test_retry_error_until_response(self, mock_retry, response,

def test_retry_error_until_exceeded(self, mock_retry, response,
check_call_numbers):
mock_retry(retry_error_num=3)
mock_retry(retry_error_num=self.max_retries())
with pytest.raises(stripe.error.APIConnectionError):
self.make_request()

check_call_numbers(self.max_retries())

def test_no_retry_error(self, mock_retry, response, check_call_numbers):
mock_retry(no_retry_error_num=3)
mock_retry(no_retry_error_num=self.max_retries())
with pytest.raises(stripe.error.APIConnectionError):
self.make_request()
check_call_numbers(1)
Expand All @@ -322,12 +322,39 @@ def test_retry_codes(self, mock_retry, response, check_call_numbers):

def test_retry_codes_until_exceeded(self, mock_retry, response,
check_call_numbers):
mock_retry(responses=[response(code=409)] * 3)
mock_retry(responses=[response(code=409)] * self.max_retries())
_, code, _ = self.make_request()
assert code == 409
check_call_numbers(self.max_retries())

# Basic requests client behavior tested in TestRequestsClient
def test_handle_request_error_retry(self, mock_retry, session):
client = self.REQUEST_CLIENT()
request_mock = mock_retry(retry_error_num=1, no_retry_error_num=1)

with pytest.raises(stripe.error.APIConnectionError) as error:
client._handle_request_error(request_mock.exceptions.SSLError())
assert error.value.should_retry is False

with pytest.raises(stripe.error.APIConnectionError) as error:
client._handle_request_error(request_mock.exceptions.Timeout())
assert error.value.should_retry

with pytest.raises(stripe.error.APIConnectionError) as error:
client._handle_request_error(
request_mock.exceptions.ConnectionError())
assert error.value.should_retry

with pytest.raises(stripe.error.APIConnectionError) as error:
client._handle_request_error(
request_mock.exceptions.RequestException())
assert error.value.should_retry is False

with pytest.raises(stripe.error.APIConnectionError) as error:
client._handle_request_error(KeyError(""))
assert error.value.should_retry is False

# Basic requests client behavior tested in TestRequestsClient

def test_request(self, request_mock, mock_response, check_call):
pass

Expand Down

0 comments on commit 3a40824

Please sign in to comment.