Skip to content

Commit a9270a1

Browse files
authored
Merge branch 'development' into Feature/FlagSets
2 parents 2e00bff + e880f37 commit a9270a1

File tree

8 files changed

+71
-29
lines changed

8 files changed

+71
-29
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
9.5.1 (Sep 5, 2023)
2+
- Exclude tests from when building the package
3+
- Fixed exception when fetching telemetry stats if no SSE Feature flags update events are stored
4+
15
9.5.0 (Jul 18, 2023)
26
- Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system.
37

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@
5353
'Programming Language :: Python :: 3',
5454
'Topic :: Software Development :: Libraries'
5555
],
56-
packages=find_packages()
56+
packages=find_packages(exclude=('tests', 'tests.*'))
5757
)

splitio/models/token.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,25 +58,6 @@ def iat(self):
5858
return self._iat
5959

6060

61-
def decode_token(raw_token):
62-
"""Decode token"""
63-
if not 'pushEnabled' in raw_token or not 'token' in raw_token:
64-
return None, None, None
65-
66-
token = raw_token['token']
67-
push_enabled = raw_token['pushEnabled']
68-
if not push_enabled or len(token.strip()) == 0:
69-
return None, None, None
70-
71-
token_parts = token.split('.')
72-
if len(token_parts) < 2:
73-
return None, None, None
74-
75-
to_decode = token_parts[1]
76-
decoded_payload = base64.b64decode(to_decode + '='*(-len(to_decode) % 4))
77-
return push_enabled, token, json.loads(decoded_payload)
78-
79-
8061
def from_raw(raw_token):
8162
"""
8263
Parse a new token from a raw token response.
@@ -87,5 +68,15 @@ def from_raw(raw_token):
8768
:return: New token model object
8869
:rtype: splitio.models.token.Token
8970
"""
90-
push_enabled, token, decoded_token = decode_token(raw_token)
91-
return None if push_enabled is None else Token(push_enabled, token, json.loads(decoded_token['x-ably-capability']), decoded_token['exp'], decoded_token['iat'])
71+
if not 'pushEnabled' in raw_token or not 'token' in raw_token:
72+
return Token(False, None, None, None, None)
73+
token = raw_token['token']
74+
push_enabled = raw_token['pushEnabled']
75+
token_parts = token.strip().split('.')
76+
77+
if not push_enabled or len(token_parts) < 2:
78+
return Token(False, None, None, None, None)
79+
80+
to_decode = token_parts[1]
81+
decoded_token = json.loads(base64.b64decode(to_decode + '='*(-len(to_decode) % 4)))
82+
return Token(push_enabled, token, json.loads(decoded_token['x-ably-capability']), decoded_token['exp'], decoded_token['iat'])

splitio/tasks/util/asynctask.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def _execution_wrapper(self):
9494
_LOGGER.debug("Force execution signal received. Running now")
9595
if not _safe_run(self._main):
9696
_LOGGER.error("An error occurred when executing the task. "
97-
"Retrying after perio expires")
97+
"Retrying after period expires")
9898
continue
9999
except queue.Empty:
100100
# If no message was received, the timeout has expired
@@ -104,7 +104,7 @@ def _execution_wrapper(self):
104104
if not _safe_run(self._main):
105105
_LOGGER.error(
106106
"An error occurred when executing the task. "
107-
"Retrying after perio expires"
107+
"Retrying after period expires"
108108
)
109109
finally:
110110
self._cleanup()

splitio/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '9.6.1-rc1'
1+
__version__ = '9.6.1'

tests/models/test_telemetry_model.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def test_storage_type_and_operation_mode(self, mocker):
4444
def test_method_latencies(self, mocker):
4545
method_latencies = MethodLatencies()
4646

47+
method_latencies.pop_all() # should not raise exception
4748
for method in ModelTelemetry.MethodExceptionsAndLatencies:
4849
method_latencies.add_latency(method, 50)
4950
if method.value == 'treatment':
@@ -116,6 +117,7 @@ def test_method_latencies(self, mocker):
116117
def test_http_latencies(self, mocker):
117118
http_latencies = HTTPLatencies()
118119

120+
http_latencies.pop_all() # should not raise exception
119121
for resource in ModelTelemetry.HTTPExceptionsAndLatencies:
120122
if self._get_http_latency(resource, http_latencies) == None:
121123
continue
@@ -169,6 +171,7 @@ def _get_http_latency(self, resource, storage):
169171
def test_method_exceptions(self, mocker):
170172
method_exception = MethodExceptions()
171173

174+
exceptions = method_exception.pop_all() # should not raise exception
172175
[method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT) for i in range(2)]
173176
method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS)
174177
method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG)
@@ -200,6 +203,7 @@ def test_method_exceptions(self, mocker):
200203

201204
def test_http_errors(self, mocker):
202205
http_error = HTTPErrors()
206+
errors = http_error.pop_all() # should not raise exception
203207
[http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, str(i)) for i in [500, 501, 502]]
204208
[http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, str(i)) for i in [400, 401, 402]]
205209
http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, '502')
@@ -220,6 +224,7 @@ def test_http_errors(self, mocker):
220224

221225
def test_last_synchronization(self, mocker):
222226
last_synchronization = LastSynchronization()
227+
last_synchronization.get_all() # should not raise exception
223228
last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, 10)
224229
last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, 20)
225230
last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, 40)
@@ -240,19 +245,27 @@ def test_telemetry_counters(self):
240245
assert(telemetry_counter._token_refreshes == 0)
241246
assert(telemetry_counter._update_from_sse == {})
242247

248+
assert(telemetry_counter.get_session_length() == 0)
243249
telemetry_counter.record_session_length(20)
244250
assert(telemetry_counter.get_session_length() == 20)
245251

252+
assert(telemetry_counter.pop_auth_rejections() == 0)
246253
[telemetry_counter.record_auth_rejections() for i in range(5)]
247254
auth_rejections = telemetry_counter.pop_auth_rejections()
248255
assert(telemetry_counter._auth_rejections == 0)
249256
assert(auth_rejections == 5)
250257

258+
assert(telemetry_counter.pop_token_refreshes() == 0)
251259
[telemetry_counter.record_token_refreshes() for i in range(3)]
252260
token_refreshes = telemetry_counter.pop_token_refreshes()
253261
assert(telemetry_counter._token_refreshes == 0)
254262
assert(token_refreshes == 3)
255263

264+
assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_QUEUED) == 0)
265+
assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_DEDUPED) == 0)
266+
assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_DROPPED) == 0)
267+
assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.EVENTS_QUEUED) == 0)
268+
assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.EVENTS_DROPPED) == 0)
256269
telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_QUEUED, 10)
257270
assert(telemetry_counter._impressions_queued == 10)
258271
telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_DEDUPED, 14)
@@ -263,6 +276,7 @@ def test_telemetry_counters(self):
263276
assert(telemetry_counter._events_queued == 30)
264277
telemetry_counter.record_events_value(ModelTelemetry.CounterConstants.EVENTS_DROPPED, 1)
265278
assert(telemetry_counter._events_dropped == 1)
279+
assert(telemetry_counter.pop_update_from_sse(UpdateFromSSE.SPLIT_UPDATE) == 0)
266280
telemetry_counter.record_update_from_sse(UpdateFromSSE.SPLIT_UPDATE)
267281
assert(telemetry_counter._update_from_sse[UpdateFromSSE.SPLIT_UPDATE.value] == 1)
268282
updates = telemetry_counter.pop_update_from_sse(UpdateFromSSE.SPLIT_UPDATE)
@@ -277,6 +291,7 @@ def test_streaming_event(self, mocker):
277291

278292
def test_streaming_events(self, mocker):
279293
streaming_events = StreamingEvents()
294+
events = streaming_events.pop_streaming_events() # should not raise exception
280295
streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED, 'split', 1234))
281296
streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.STREAMING_STATUS, 'split', 1234))
282297
events = streaming_events.pop_streaming_events()
@@ -286,6 +301,7 @@ def test_streaming_events(self, mocker):
286301

287302
def test_telemetry_config(self):
288303
telemetry_config = TelemetryConfig()
304+
stats = telemetry_config.get_stats() # should not raise exception
289305
config = {'operationMode': 'standalone',
290306
'streamingEnabled': True,
291307
'impressionsQueueSize': 100,
@@ -326,9 +342,11 @@ def test_telemetry_config(self):
326342
telemetry_config.record_flag_sets(5)
327343
assert(telemetry_config._flag_sets == 5)
328344

345+
assert(telemetry_config.get_bur_time_outs() == 0)
329346
[telemetry_config.record_bur_time_out() for i in range(2)]
330347
assert(telemetry_config.get_bur_time_outs() == 2)
331348

349+
assert(telemetry_config.get_non_ready_usage() == 0)
332350
[telemetry_config.record_not_ready_usage() for i in range(5)]
333351
assert(telemetry_config.get_non_ready_usage() == 5)
334352

tests/models/test_token.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ class TokenTests(object):
1111
def test_from_raw_false(self):
1212
"""Test token model parsing."""
1313
parsed = token.from_raw(self.raw_false)
14-
assert parsed == None
15-
14+
assert parsed.push_enabled == False
15+
assert parsed.iat == None
16+
assert parsed.channels == None
17+
assert parsed.exp == None
18+
assert parsed.token == None
19+
1620
raw_empty = {
1721
'pushEnabled': True,
1822
'token': '',
@@ -21,7 +25,11 @@ def test_from_raw_false(self):
2125
def test_from_raw_empty(self):
2226
"""Test token model parsing."""
2327
parsed = token.from_raw(self.raw_empty)
24-
assert parsed == None
28+
assert parsed.push_enabled == False
29+
assert parsed.iat == None
30+
assert parsed.channels == None
31+
assert parsed.exp == None
32+
assert parsed.token == None
2533

2634
raw_ok = {
2735
'pushEnabled': True,
@@ -39,4 +47,3 @@ def test_from_raw(self):
3947
assert parsed.channels['NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits'] == ['subscribe']
4048
assert parsed.channels['control_pri'] == ['subscribe', 'channel-metadata:publishers']
4149
assert parsed.channels['control_sec'] == ['subscribe', 'channel-metadata:publishers']
42-

tests/push/test_manager.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,28 @@ def new_start(*args, **kwargs): # pylint: disable=unused-argument
9090
assert feedback_loop.get() == Status.PUSH_RETRYABLE_ERROR
9191
assert timer_mock.mock_calls == [mocker.call(0, Any())]
9292

93+
def test_empty_auth_respnse(self, mocker):
94+
"""Test the initial status is ok and reset() works as expected."""
95+
api_mock = mocker.Mock()
96+
api_mock.authenticate.return_value = Token(False, None, None, None, None)
97+
98+
sse_mock = mocker.Mock(spec=SplitSSEClient)
99+
sse_constructor_mock = mocker.Mock()
100+
sse_constructor_mock.return_value = sse_mock
101+
timer_mock = mocker.Mock()
102+
mocker.patch('splitio.push.manager.Timer', new=timer_mock)
103+
mocker.patch('splitio.push.manager.SplitSSEClient', new=sse_constructor_mock)
104+
feedback_loop = Queue()
105+
telemetry_storage = InMemoryTelemetryStorage()
106+
telemetry_producer = TelemetryStorageProducer(telemetry_storage)
107+
telemetry_runtime_producer = telemetry_producer.get_telemetry_runtime_producer()
108+
manager = PushManager(api_mock, mocker.Mock(), feedback_loop, mocker.Mock(), telemetry_runtime_producer)
109+
manager.start()
110+
assert feedback_loop.get() == Status.PUSH_NONRETRYABLE_ERROR
111+
assert timer_mock.mock_calls == [mocker.call(0, Any())]
112+
assert sse_mock.mock_calls == []
113+
114+
93115
def test_push_disabled(self, mocker):
94116
"""Test the initial status is ok and reset() works as expected."""
95117
api_mock = mocker.Mock()

0 commit comments

Comments
 (0)