Skip to content

Commit 24b63d9

Browse files
authored
Merge pull request #586 from splitio/FME-4286-imp-prop-integration
Updated integration tests
2 parents a32a289 + 12ec9ae commit 24b63d9

16 files changed

+239
-212
lines changed

splitio/api/impressions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ def _build_bulk(impressions):
3737
'c': impression.change_number,
3838
'r': impression.label,
3939
'b': impression.bucketing_key,
40-
'pt': impression.previous_time
40+
'pt': impression.previous_time,
41+
'properties': impression.properties
4142
}
4243
for impression in imps
4344
]

splitio/client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def _build_impression(self, key, bucketing, feature, result, properties=None):
137137
bucketing_key=bucketing,
138138
time=utctime_ms(),
139139
previous_time=None,
140-
properties=json.dumps(properties)),
140+
properties=json.dumps(properties) if properties is not None else None),
141141
disabled=result['impressions_disabled'])
142142

143143
def _build_impressions(self, key, bucketing, results, properties=None):

splitio/storage/pluggable.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ def _wrap_impressions(self, impressions):
12311231
'r': impression.label,
12321232
'c': impression.change_number,
12331233
'm': impression.time,
1234+
'properties': impression.properties
12341235
}
12351236
}
12361237
bulk_impressions.append(json.dumps(to_store))

splitio/storage/redis.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ def _wrap_impressions(self, impressions):
11001100
'r': impression.label,
11011101
'c': impression.change_number,
11021102
'm': impression.time,
1103+
'properties': impression.properties
11031104
}
11041105
}
11051106
bulk_impressions.append(json.dumps(to_store))

tests/api/test_impressions_api.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@
1414
from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync
1515

1616
impressions_mock = [
17-
Impression('k1', 'f1', 'on', 'l1', 123456, 'b1', 321654, {}),
18-
Impression('k2', 'f2', 'off', 'l1', 123456, 'b1', 321654, {}),
19-
Impression('k3', 'f1', 'on', 'l1', 123456, 'b1', 321654, {})
17+
Impression('k1', 'f1', 'on', 'l1', 123456, 'b1', 321654, None, {'prop': 'val'}),
18+
Impression('k2', 'f2', 'off', 'l1', 123456, 'b1', 321654, None, None),
19+
Impression('k3', 'f1', 'on', 'l1', 123456, 'b1', 321654, None, None)
2020
]
2121
expectedImpressions = [{
2222
'f': 'f1',
2323
'i': [
24-
{'k': 'k1', 'b': 'b1', 't': 'on', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None},
25-
{'k': 'k3', 'b': 'b1', 't': 'on', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None},
24+
{'k': 'k1', 'b': 'b1', 't': 'on', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None, 'properties': {"prop": "val"}},
25+
{'k': 'k3', 'b': 'b1', 't': 'on', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None, 'properties': None},
2626
],
2727
}, {
2828
'f': 'f2',
2929
'i': [
30-
{'k': 'k2', 'b': 'b1', 't': 'off', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None},
30+
{'k': 'k2', 'b': 'b1', 't': 'off', 'r': 'l1', 'm': 321654, 'c': 123456, 'pt': None, 'properties': None},
3131
]
3232
}]
3333

tests/client/test_client.py

Lines changed: 43 additions & 43 deletions
Large diffs are not rendered by default.

tests/client/test_input_validator.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -766,14 +766,14 @@ def test_track(self, mocker):
766766
_logger.reset_mock()
767767
assert client.track("some_key", "traffic_type", "event_type", 1, []) is False
768768
assert _logger.error.mock_calls == [
769-
mocker.call("track: properties must be of type dictionary.")
769+
mocker.call("%s: properties must be of type dictionary.", "track")
770770
]
771771

772772
# Test track with invalid properties
773773
_logger.reset_mock()
774774
assert client.track("some_key", "traffic_type", "event_type", 1, True) is False
775775
assert _logger.error.mock_calls == [
776-
mocker.call("track: properties must be of type dictionary.")
776+
mocker.call("%s: properties must be of type dictionary.", "track")
777777
]
778778

779779
# Test track with properties
@@ -788,7 +788,7 @@ def test_track(self, mocker):
788788
_logger.reset_mock()
789789
assert client.track("some_key", "traffic_type", "event_type", 1, props1) is True
790790
assert _logger.warning.mock_calls == [
791-
mocker.call("Property %s is of invalid type. Setting value to None", [])
791+
mocker.call("%s: Property %s is of invalid type. Setting value to None", "track", [])
792792
]
793793

794794
# Test track with more than 300 properties
@@ -798,7 +798,7 @@ def test_track(self, mocker):
798798
_logger.reset_mock()
799799
assert client.track("some_key", "traffic_type", "event_type", 1, props2) is True
800800
assert _logger.warning.mock_calls == [
801-
mocker.call("Event has more than 300 properties. Some of them will be trimmed when processed")
801+
mocker.call("%s: Event has more than 300 properties. Some of them will be trimmed when processed", "track")
802802
]
803803

804804
# Test track with properties higher than 32kb
@@ -808,7 +808,7 @@ def test_track(self, mocker):
808808
props3["prop" + str(i)] = "a" * 300
809809
assert client.track("some_key", "traffic_type", "event_type", 1, props3) is False
810810
assert _logger.error.mock_calls == [
811-
mocker.call("The maximum size allowed for the properties is 32768 bytes. Current one is 32952 bytes. Event not queued")
811+
mocker.call("%s: The maximum size allowed for the properties is 32768 bytes. Current one is 32952 bytes. Event not queued", "track")
812812
]
813813
factory.destroy
814814

@@ -2378,14 +2378,14 @@ async def is_valid_traffic_type(*_):
23782378
_logger.reset_mock()
23792379
assert await client.track("some_key", "traffic_type", "event_type", 1, []) is False
23802380
assert _logger.error.mock_calls == [
2381-
mocker.call("track: properties must be of type dictionary.")
2381+
mocker.call("%s: properties must be of type dictionary.", "track")
23822382
]
23832383

23842384
# Test track with invalid properties
23852385
_logger.reset_mock()
23862386
assert await client.track("some_key", "traffic_type", "event_type", 1, True) is False
23872387
assert _logger.error.mock_calls == [
2388-
mocker.call("track: properties must be of type dictionary.")
2388+
mocker.call("%s: properties must be of type dictionary.", "track")
23892389
]
23902390

23912391
# Test track with properties
@@ -2400,7 +2400,7 @@ async def is_valid_traffic_type(*_):
24002400
_logger.reset_mock()
24012401
assert await client.track("some_key", "traffic_type", "event_type", 1, props1) is True
24022402
assert _logger.warning.mock_calls == [
2403-
mocker.call("Property %s is of invalid type. Setting value to None", [])
2403+
mocker.call("%s: Property %s is of invalid type. Setting value to None", "track", [])
24042404
]
24052405

24062406
# Test track with more than 300 properties
@@ -2410,7 +2410,7 @@ async def is_valid_traffic_type(*_):
24102410
_logger.reset_mock()
24112411
assert await client.track("some_key", "traffic_type", "event_type", 1, props2) is True
24122412
assert _logger.warning.mock_calls == [
2413-
mocker.call("Event has more than 300 properties. Some of them will be trimmed when processed")
2413+
mocker.call("%s: Event has more than 300 properties. Some of them will be trimmed when processed", "track")
24142414
]
24152415

24162416
# Test track with properties higher than 32kb
@@ -2420,7 +2420,7 @@ async def is_valid_traffic_type(*_):
24202420
props3["prop" + str(i)] = "a" * 300
24212421
assert await client.track("some_key", "traffic_type", "event_type", 1, props3) is False
24222422
assert _logger.error.mock_calls == [
2423-
mocker.call("The maximum size allowed for the properties is 32768 bytes. Current one is 32952 bytes. Event not queued")
2423+
mocker.call("%s: The maximum size allowed for the properties is 32768 bytes. Current one is 32952 bytes. Event not queued", "track")
24242424
]
24252425
await factory.destroy()
24262426

tests/integration/test_client_e2e.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
def _validate_last_impressions(client, *to_validate):
5050
"""Validate the last N impressions are present disregarding the order."""
5151
imp_storage = client._factory._get_storage('impressions')
52+
as_tup_set = set()
5253
if isinstance(client._factory._get_storage('splits'), RedisSplitStorage) or isinstance(client._factory._get_storage('splits'), PluggableSplitStorage):
5354
if isinstance(client._factory._get_storage('splits'), RedisSplitStorage):
5455
redis_client = imp_storage._redis
@@ -64,15 +65,28 @@ def _validate_last_impressions(client, *to_validate):
6465
json.loads(i)
6566
for i in results
6667
]
67-
as_tup_set = set(
68-
(i['i']['f'], i['i']['k'], i['i']['t'])
69-
for i in impressions_raw
70-
)
68+
if to_validate != ():
69+
if len(to_validate[0]) == 3:
70+
as_tup_set = set(
71+
(i['i']['f'], i['i']['k'], i['i']['t'])
72+
for i in impressions_raw
73+
)
74+
else:
75+
as_tup_set = set(
76+
(i['i']['f'], i['i']['k'], i['i']['t'], i['i']['properties'])
77+
for i in impressions_raw
78+
)
79+
7180
assert as_tup_set == set(to_validate)
7281
time.sleep(0.2) # delay for redis to sync
7382
else:
7483
impressions = imp_storage.pop_many(len(to_validate))
75-
as_tup_set = set((i.feature_name, i.matching_key, i.treatment) for i in impressions)
84+
if to_validate != ():
85+
if len(to_validate[0]) == 3:
86+
as_tup_set = set((i.feature_name, i.matching_key, i.treatment) for i in impressions)
87+
else:
88+
as_tup_set = set((i.feature_name, i.matching_key, i.treatment, i.properties) for i in impressions)
89+
7690
assert as_tup_set == set(to_validate)
7791

7892
def _validate_last_events(client, *to_validate):
@@ -108,9 +122,9 @@ def _get_treatment(factory, skip_rbs=False):
108122
except:
109123
pass
110124

111-
assert client.get_treatment('user1', 'sample_feature') == 'on'
125+
assert client.get_treatment('user1', 'sample_feature', impressions_properties={'prop':'value'}) == 'on'
112126
if not isinstance(factory._recorder._impressions_manager._strategy, StrategyNoneMode):
113-
_validate_last_impressions(client, ('sample_feature', 'user1', 'on'))
127+
_validate_last_impressions(client, ('sample_feature', 'user1', 'on', '{"prop": "value"}'))
114128

115129
assert client.get_treatment('invalidKey', 'sample_feature') == 'off'
116130
if not isinstance(factory._recorder._impressions_manager._strategy, StrategyNoneMode):
@@ -514,7 +528,7 @@ def setup_method(self):
514528
'events': InMemoryEventStorage(5000, telemetry_runtime_producer),
515529
}
516530
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
517-
recorder = StandardRecorder(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer)
531+
recorder = StandardRecorder(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer, imp_counter=ImpressionsCounter())
518532
# Since we are passing None as SDK_Ready event, the factory will use the Redis telemetry call, using try catch to ignore the exception.
519533
try:
520534
self.factory = SplitFactory('some_api_key',
@@ -674,7 +688,7 @@ def setup_method(self):
674688
'events': InMemoryEventStorage(5000, telemetry_runtime_producer),
675689
}
676690
impmanager = ImpressionsManager(StrategyOptimizedMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
677-
recorder = StandardRecorder(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer)
691+
recorder = StandardRecorder(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer, imp_counter=ImpressionsCounter())
678692
self.factory = SplitFactory('some_api_key',
679693
storages,
680694
True,
@@ -967,7 +981,7 @@ def setup_method(self):
967981
}
968982
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
969983
recorder = PipelinedRecorder(redis_client.pipeline, impmanager, storages['events'],
970-
storages['impressions'], telemetry_redis_storage)
984+
storages['impressions'], telemetry_redis_storage, imp_counter=ImpressionsCounter())
971985
self.factory = SplitFactory('some_api_key',
972986
storages,
973987
True,
@@ -1155,7 +1169,7 @@ def setup_method(self):
11551169
}
11561170
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
11571171
recorder = PipelinedRecorder(redis_client.pipeline, impmanager,
1158-
storages['events'], storages['impressions'], telemetry_redis_storage)
1172+
storages['events'], storages['impressions'], telemetry_redis_storage, imp_counter=ImpressionsCounter())
11591173
self.factory = SplitFactory('some_api_key',
11601174
storages,
11611175
True,
@@ -1375,7 +1389,7 @@ def setup_method(self):
13751389

13761390
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
13771391
recorder = StandardRecorder(impmanager, storages['events'],
1378-
storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer)
1392+
storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer, imp_counter=ImpressionsCounter())
13791393

13801394
self.factory = SplitFactory('some_api_key',
13811395
storages,
@@ -1570,7 +1584,7 @@ def setup_method(self):
15701584

15711585
impmanager = ImpressionsManager(StrategyOptimizedMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
15721586
recorder = StandardRecorder(impmanager, storages['events'],
1573-
storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer)
1587+
storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer, imp_counter=ImpressionsCounter())
15741588

15751589
self.factory = SplitFactory('some_api_key',
15761590
storages,
@@ -1617,7 +1631,7 @@ def test_get_treatment(self):
16171631
client.get_treatment('user1', 'sample_feature')
16181632
client.get_treatment('user1', 'sample_feature')
16191633
client.get_treatment('user1', 'sample_feature')
1620-
assert self.pluggable_storage_adapter._keys['SPLITIO.impressions'] == []
1634+
assert len(self.pluggable_storage_adapter._keys['SPLITIO.impressions']) == 1
16211635

16221636
def test_get_treatment_with_config(self):
16231637
"""Test client.get_treatment_with_config()."""
@@ -2317,7 +2331,7 @@ async def _setup_method(self):
23172331
'events': InMemoryEventStorageAsync(5000, telemetry_runtime_producer),
23182332
}
23192333
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
2320-
recorder = StandardRecorderAsync(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer)
2334+
recorder = StandardRecorderAsync(impmanager, storages['events'], storages['impressions'], telemetry_evaluation_producer, telemetry_runtime_producer, imp_counter=ImpressionsCounter())
23212335
# Since we are passing None as SDK_Ready event, the factory will use the Redis telemetry call, using try catch to ignore the exception.
23222336
try:
23232337
self.factory = SplitFactoryAsync('some_api_key',
@@ -2839,7 +2853,7 @@ async def _setup_method(self):
28392853
}
28402854
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
28412855
recorder = PipelinedRecorderAsync(redis_client.pipeline, impmanager, storages['events'],
2842-
storages['impressions'], telemetry_redis_storage)
2856+
storages['impressions'], telemetry_redis_storage, imp_counter=ImpressionsCounter())
28432857
self.factory = SplitFactoryAsync('some_api_key',
28442858
storages,
28452859
True,
@@ -3061,7 +3075,7 @@ async def _setup_method(self):
30613075
}
30623076
impmanager = ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer) # no listener
30633077
recorder = PipelinedRecorderAsync(redis_client.pipeline, impmanager, storages['events'],
3064-
storages['impressions'], telemetry_redis_storage)
3078+
storages['impressions'], telemetry_redis_storage, imp_counter=ImpressionsCounter())
30653079
self.factory = SplitFactoryAsync('some_api_key',
30663080
storages,
30673081
True,
@@ -3293,7 +3307,7 @@ async def _setup_method(self):
32933307
recorder = StandardRecorderAsync(impmanager, storages['events'],
32943308
storages['impressions'],
32953309
telemetry_producer.get_telemetry_evaluation_producer(),
3296-
telemetry_runtime_producer)
3310+
telemetry_runtime_producer, imp_counter=ImpressionsCounter())
32973311

32983312
self.factory = SplitFactoryAsync('some_api_key',
32993313
storages,

tests/integration/test_pluggable_integration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ class PluggableImpressionsStorageIntegrationTests(object):
158158
def _put_impressions(self, adapter, metadata):
159159
storage = PluggableImpressionsStorage(adapter, metadata)
160160
storage.put([
161-
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
162-
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
163-
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654)
161+
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
162+
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
163+
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None)
164164
])
165165

166166

tests/integration/test_redis_integration.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,9 @@ class RedisImpressionsStorageTests(object):
161161
def _put_impressions(self, adapter, metadata):
162162
storage = RedisImpressionsStorage(adapter, metadata)
163163
storage.put([
164-
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
165-
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
166-
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654)
164+
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
165+
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
166+
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None)
167167
])
168168

169169

@@ -394,9 +394,9 @@ class RedisImpressionsStorageAsyncTests(object):
394394
async def _put_impressions(self, adapter, metadata):
395395
storage = RedisImpressionsStorageAsync(adapter, metadata)
396396
await storage.put([
397-
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
398-
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654),
399-
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654)
397+
impressions.Impression('key1', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
398+
impressions.Impression('key2', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None),
399+
impressions.Impression('key3', 'feature1', 'on', 'l1', 123456, 'b1', 321654, None, None)
400400
])
401401

402402

0 commit comments

Comments
 (0)