Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ldclient/polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def run(self):
if not is_http_error_recoverable(e.status):
self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited
self.stop()
break
except Exception as e:
log.exception(
'Error: Exception encountered when updating flags. %s' % e)
Expand Down
2 changes: 2 additions & 0 deletions testing/stub_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ class MockFeatureRequester(FeatureRequester):
def __init__(self):
self.all_data = {}
self.exception = None
self.request_count = 0

def get_all_data(self):
self.request_count += 1
if self.exception is not None:
raise self.exception
return self.all_data
Expand Down
33 changes: 21 additions & 12 deletions testing/test_polling_processor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import threading
import time
import mock

from ldclient.config import Config
from ldclient.feature_store import InMemoryFeatureStore
Expand All @@ -10,7 +11,6 @@
from ldclient.versioned_data_kind import FEATURES, SEGMENTS
from testing.stub_util import MockFeatureRequester, MockResponse

config = Config()
pp = None
mock_requester = None
store = None
Expand Down Expand Up @@ -47,21 +47,23 @@ def test_successful_request_puts_feature_data_in_store():
"segkey": segment
}
}
setup_processor(config)
setup_processor(Config())
ready.wait()
assert store.get(FEATURES, "flagkey", lambda x: x) == flag
assert store.get(SEGMENTS, "segkey", lambda x: x) == segment
assert store.initialized
assert pp.initialized()

def test_general_connection_error_does_not_cause_immediate_failure():
# Note that we have to mock Config.poll_interval because Config won't let you set a value less than 30 seconds

@mock.patch('ldclient.config.Config.poll_interval', new_callable=mock.PropertyMock, return_value=0.1)
def test_general_connection_error_does_not_cause_immediate_failure(ignore_mock):
mock_requester.exception = Exception("bad")
start_time = time.time()
setup_processor(config)
setup_processor(Config())
ready.wait(0.3)
elapsed_time = time.time() - start_time
assert elapsed_time >= 0.2
assert not pp.initialized()
assert mock_requester.request_count >= 2

def test_http_401_error_causes_immediate_failure():
verify_unrecoverable_http_error(401)
Expand All @@ -78,16 +80,23 @@ def test_http_429_error_does_not_cause_immediate_failure():
def test_http_500_error_does_not_cause_immediate_failure():
verify_recoverable_http_error(500)

def verify_unrecoverable_http_error(status):
def test_http_503_error_does_not_cause_immediate_failure():
verify_recoverable_http_error(503)

@mock.patch('ldclient.config.Config.poll_interval', new_callable=mock.PropertyMock, return_value=0.1)
def verify_unrecoverable_http_error(status, ignore_mock):
mock_requester.exception = UnsuccessfulResponseException(status)
setup_processor(config)
finished = ready.wait(5.0)
setup_processor(Config())
finished = ready.wait(0.5)
assert finished
assert not pp.initialized()
assert mock_requester.request_count == 1

def verify_recoverable_http_error(status):
@mock.patch('ldclient.config.Config.poll_interval', new_callable=mock.PropertyMock, return_value=0.1)
def verify_recoverable_http_error(status, ignore_mock):
mock_requester.exception = UnsuccessfulResponseException(status)
setup_processor(config)
finished = ready.wait(0.2)
setup_processor(Config())
finished = ready.wait(0.5)
assert not finished
assert not pp.initialized()
assert mock_requester.request_count >= 2