Skip to content

Commit 6915880

Browse files
committed
add NOT READY label
1 parent bf54e84 commit 6915880

File tree

4 files changed

+79
-23
lines changed

4 files changed

+79
-23
lines changed

splitio/client/client.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,25 @@ def _send_impression_to_listener(self, impression, attributes):
8585
)
8686
self._logger.debug('Error', exc_info=True)
8787

88+
def _evaluate_if_ready(self, matching_key, bucketing_key, feature, attributes=None):
89+
if not self.ready:
90+
return {
91+
'treatment': CONTROL,
92+
'configurations': None,
93+
'impression': {
94+
'label': Label.NOT_READY,
95+
'change_number': None
96+
}
97+
}
98+
99+
return self._evaluator.evaluate_treatment(
100+
feature,
101+
matching_key,
102+
bucketing_key,
103+
attributes
104+
)
105+
106+
88107
def get_treatment_with_config(self, key, feature, attributes=None):
89108
"""
90109
Get the treatment and config for a feature and key, with optional dictionary of attributes.
@@ -120,12 +139,7 @@ def get_treatment_with_config(self, key, feature, attributes=None):
120139
or not input_validator.validate_attributes(attributes):
121140
return CONTROL, None
122141

123-
result = self._evaluator.evaluate_treatment(
124-
feature,
125-
matching_key,
126-
bucketing_key,
127-
attributes
128-
)
142+
result = self._evaluate_if_ready(matching_key, bucketing_key, feature, attributes)
129143

130144
impression = self._build_impression(
131145
matching_key,
@@ -221,23 +235,17 @@ def get_treatments_with_config(self, key, features, attributes=None):
221235

222236
for feature in features:
223237
try:
224-
treatment = self._evaluator.evaluate_treatment(
225-
feature,
226-
matching_key,
227-
bucketing_key,
228-
attributes
229-
)
230-
238+
result = self._evaluate_if_ready(matching_key, bucketing_key, feature, attributes)
231239
impression = self._build_impression(matching_key,
232240
feature,
233-
treatment['treatment'],
234-
treatment['impression']['label'],
235-
treatment['impression']['change_number'],
241+
result['treatment'],
242+
result['impression']['label'],
243+
result['impression']['change_number'],
236244
bucketing_key,
237245
start)
238246

239247
bulk_impressions.append(impression)
240-
treatments[feature] = (treatment['treatment'], treatment['configurations'])
248+
treatments[feature] = (result['treatment'], result['configurations'])
241249

242250
except Exception: #pylint: disable=broad-except
243251
self._logger.error('get_treatments: An exception occured when evaluating '

splitio/models/impressions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ class Label(object): #pylint: disable=too-few-public-methods
4646
# Treatment: control
4747
# Label: exception
4848
EXCEPTION = 'exception'
49+
50+
# Condition: Evaluation requested while client not ready
51+
# Treatment: control
52+
# Label: not ready
53+
NOT_READY = 'not ready'

tests/client/test_client.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from splitio.client.client import Client
77
from splitio.client.factory import SplitFactory
88
from splitio.engine.evaluator import Evaluator
9-
from splitio.models.impressions import Impression
9+
from splitio.models.impressions import Impression, Label
1010
from splitio.models.events import Event
1111
from splitio.storage import EventStorage, ImpressionStorage, SegmentStorage, SplitStorage, \
1212
TelemetryStorage
@@ -67,7 +67,18 @@ def _get_storage_mock(name):
6767
None
6868
) in client._send_impression_to_listener.mock_calls
6969

70+
# Test with client not ready
71+
ready_property = mocker.PropertyMock()
72+
ready_property.return_value = False
73+
type(factory).ready = ready_property
74+
impression_storage.put.reset_mock()
75+
assert client.get_treatment('some_key', 'some_feature', {'some_attribute': 1}) == 'control'
76+
assert mocker.call(
77+
[Impression('some_key', 'some_feature', 'control', Label.NOT_READY, mocker.ANY, mocker.ANY, mocker.ANY)]
78+
) in impression_storage.put.mock_calls
79+
7080
# Test with exception:
81+
ready_property.return_value = True
7182
split_storage.get_change_number.return_value = -1
7283
def _raise(*_):
7384
raise Exception('something')
@@ -76,7 +87,7 @@ def _raise(*_):
7687
assert mocker.call(
7788
[Impression('some_key', 'some_feature', 'control', 'exception', -1, None, 1000)]
7889
) in impression_storage.put.mock_calls
79-
assert len(telemetry_storage.inc_latency.mock_calls) == 2
90+
assert len(telemetry_storage.inc_latency.mock_calls) == 3
8091

8192
def test_get_treatment_with_config(self, mocker):
8293
"""Test get_treatment execution paths."""
@@ -131,7 +142,18 @@ def _get_storage_mock(name):
131142
None
132143
) in client._send_impression_to_listener.mock_calls
133144

145+
# Test with client not ready
146+
ready_property = mocker.PropertyMock()
147+
ready_property.return_value = False
148+
type(factory).ready = ready_property
149+
impression_storage.put.reset_mock()
150+
assert client.get_treatment_with_config('some_key', 'some_feature', {'some_attribute': 1}) == ('control', None)
151+
assert mocker.call(
152+
[Impression('some_key', 'some_feature', 'control', Label.NOT_READY, mocker.ANY, mocker.ANY, mocker.ANY)]
153+
) in impression_storage.put.mock_calls
154+
134155
# Test with exception:
156+
ready_property.return_value = True
135157
split_storage.get_change_number.return_value = -1
136158
def _raise(*_):
137159
raise Exception('something')
@@ -140,7 +162,7 @@ def _raise(*_):
140162
assert mocker.call(
141163
[Impression('some_key', 'some_feature', 'control', 'exception', -1, None, 1000)]
142164
) in impression_storage.put.mock_calls
143-
assert len(telemetry_storage.inc_latency.mock_calls) == 2
165+
assert len(telemetry_storage.inc_latency.mock_calls) == 3
144166

145167
def test_get_treatments(self, mocker):
146168
"""Test get_treatment execution paths."""
@@ -196,13 +218,24 @@ def _get_storage_mock(name):
196218
None
197219
) in client._send_impression_to_listener.mock_calls
198220

221+
# Test with client not ready
222+
ready_property = mocker.PropertyMock()
223+
ready_property.return_value = False
224+
type(factory).ready = ready_property
225+
impression_storage.put.reset_mock()
226+
assert client.get_treatments('some_key', ['some_feature'], {'some_attribute': 1}) == {'some_feature': 'control'}
227+
assert mocker.call(
228+
[Impression('some_key', 'some_feature', 'control', Label.NOT_READY, mocker.ANY, mocker.ANY, mocker.ANY)]
229+
) in impression_storage.put.mock_calls
230+
199231
# Test with exception:
232+
ready_property.return_value = True
200233
split_storage.get_change_number.return_value = -1
201234
def _raise(*_):
202235
raise Exception('something')
203236
client._evaluator.evaluate_treatment.side_effect = _raise
204237
assert client.get_treatments('key', ['f1', 'f2']) == {'f1': 'control', 'f2': 'control'}
205-
assert len(telemetry_storage.inc_latency.mock_calls) == 1
238+
assert len(telemetry_storage.inc_latency.mock_calls) == 2
206239

207240
def test_get_treatments_with_config(self, mocker):
208241
"""Test get_treatment execution paths."""
@@ -261,7 +294,18 @@ def _get_storage_mock(name):
261294
None
262295
) in client._send_impression_to_listener.mock_calls
263296

297+
# Test with client not ready
298+
ready_property = mocker.PropertyMock()
299+
ready_property.return_value = False
300+
type(factory).ready = ready_property
301+
impression_storage.put.reset_mock()
302+
assert client.get_treatments_with_config('some_key', ['some_feature'], {'some_attribute': 1}) == {'some_feature': ('control', None)}
303+
assert mocker.call(
304+
[Impression('some_key', 'some_feature', 'control', Label.NOT_READY, mocker.ANY, mocker.ANY, mocker.ANY)]
305+
) in impression_storage.put.mock_calls
306+
264307
# Test with exception:
308+
ready_property.return_value = True
265309
split_storage.get_change_number.return_value = -1
266310
def _raise(*_):
267311
raise Exception('something')
@@ -270,7 +314,7 @@ def _raise(*_):
270314
'f1': ('control', None),
271315
'f2': ('control', None)
272316
}
273-
assert len(telemetry_storage.inc_latency.mock_calls) == 1
317+
assert len(telemetry_storage.inc_latency.mock_calls) == 2
274318

275319
def test_destroy(self, mocker):
276320
"""Test that destroy/destroyed calls are forwarded to the factory."""

tests/client/test_input_validator.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,6 @@ def test_track(self, mocker):
671671
assert client._logger.error.mock_calls == []
672672
assert client._logger.warning.mock_calls == []
673673

674-
675674
def test_get_treatments(self, mocker):
676675
"""Test getTreatments() method."""
677676
split_mock = mocker.Mock(spec=Split)

0 commit comments

Comments
 (0)