5
5
import pytest
6
6
import copy
7
7
8
- from splitio .models .splits import Split , Status
8
+ from splitio .models .splits import Split , Status , from_raw , Prerequisites
9
9
from splitio .models import segments
10
10
from splitio .models .grammar .condition import Condition , ConditionType
11
11
from splitio .models .impressions import Label
@@ -127,6 +127,7 @@ def test_evaluate_treatment_killed_split(self, mocker):
127
127
mocked_split .killed = True
128
128
mocked_split .change_number = 123
129
129
mocked_split .get_configurations_for .return_value = '{"some_property": 123}'
130
+ mocked_split .prerequisites = []
130
131
131
132
ctx = EvaluationContext (flags = {'some' : mocked_split }, segment_memberships = set (), rbs_segments = {})
132
133
result = e .eval_with_context ('some_key' , 'some_bucketing_key' , 'some' , {}, ctx )
@@ -146,6 +147,8 @@ def test_evaluate_treatment_ok(self, mocker):
146
147
mocked_split .killed = False
147
148
mocked_split .change_number = 123
148
149
mocked_split .get_configurations_for .return_value = '{"some_property": 123}'
150
+ mocked_split .prerequisites = []
151
+
149
152
ctx = EvaluationContext (flags = {'some' : mocked_split }, segment_memberships = set (), rbs_segments = {})
150
153
result = e .eval_with_context ('some_key' , 'some_bucketing_key' , 'some' , {}, ctx )
151
154
assert result ['treatment' ] == 'on'
@@ -165,6 +168,8 @@ def test_evaluate_treatment_ok_no_config(self, mocker):
165
168
mocked_split .killed = False
166
169
mocked_split .change_number = 123
167
170
mocked_split .get_configurations_for .return_value = None
171
+ mocked_split .prerequisites = []
172
+
168
173
ctx = EvaluationContext (flags = {'some' : mocked_split }, segment_memberships = set (), rbs_segments = {})
169
174
result = e .eval_with_context ('some_key' , 'some_bucketing_key' , 'some' , {}, ctx )
170
175
assert result ['treatment' ] == 'on'
@@ -184,13 +189,15 @@ def test_evaluate_treatments(self, mocker):
184
189
mocked_split .killed = False
185
190
mocked_split .change_number = 123
186
191
mocked_split .get_configurations_for .return_value = '{"some_property": 123}'
192
+ mocked_split .prerequisites = []
187
193
188
194
mocked_split2 = mocker .Mock (spec = Split )
189
195
mocked_split2 .name = 'feature4'
190
196
mocked_split2 .default_treatment = 'on'
191
197
mocked_split2 .killed = False
192
198
mocked_split2 .change_number = 123
193
199
mocked_split2 .get_configurations_for .return_value = None
200
+ mocked_split2 .prerequisites = []
194
201
195
202
ctx = EvaluationContext (flags = {'feature2' : mocked_split , 'feature4' : mocked_split2 }, segment_memberships = set (), rbs_segments = {})
196
203
results = e .eval_many_with_context ('some_key' , 'some_bucketing_key' , ['feature2' , 'feature4' ], {}, ctx )
@@ -215,6 +222,8 @@ def test_get_gtreatment_for_split_no_condition_matches(self, mocker):
215
222
mocked_split .change_number = '123'
216
223
mocked_split .conditions = []
217
224
mocked_split .get_configurations_for = None
225
+ mocked_split .prerequisites = []
226
+
218
227
ctx = EvaluationContext (flags = {'some' : mocked_split }, segment_memberships = set (), rbs_segments = {})
219
228
assert e ._treatment_for_flag (mocked_split , 'some_key' , 'some_bucketing' , {}, ctx ) == (
220
229
'off' ,
@@ -232,6 +241,8 @@ def test_get_gtreatment_for_split_non_rollout(self, mocker):
232
241
mocked_split = mocker .Mock (spec = Split )
233
242
mocked_split .killed = False
234
243
mocked_split .conditions = [mocked_condition_1 ]
244
+ mocked_split .prerequisites = []
245
+
235
246
treatment , label = e ._treatment_for_flag (mocked_split , 'some_key' , 'some_bucketing' , {}, EvaluationContext (None , None , None ))
236
247
assert treatment == 'on'
237
248
assert label == 'some_label'
@@ -240,7 +251,7 @@ def test_evaluate_treatment_with_rule_based_segment(self, mocker):
240
251
"""Test that a non-killed split returns the appropriate treatment."""
241
252
e = evaluator .Evaluator (splitters .Splitter ())
242
253
243
- mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
254
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [] )
244
255
245
256
ctx = EvaluationContext (flags = {'some' : mocked_split }, segment_memberships = set (), rbs_segments = {'sample_rule_based_segment' : rule_based_segments .from_raw (rbs_raw )})
246
257
result = e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'some' , {'email' : 'bilal@split.io' }, ctx )
@@ -257,7 +268,7 @@ def test_evaluate_treatment_with_rbs_in_condition(self):
257
268
with open (rbs_segments , 'r' ) as flo :
258
269
data = json .loads (flo .read ())
259
270
260
- mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
271
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [] )
261
272
rbs = rule_based_segments .from_raw (data ["rbs" ]["d" ][0 ])
262
273
rbs2 = rule_based_segments .from_raw (data ["rbs" ]["d" ][1 ])
263
274
rbs_storage .update ([rbs , rbs2 ], [], 12 )
@@ -279,7 +290,7 @@ def test_using_segment_in_excluded(self):
279
290
segment_storage = InMemorySegmentStorage ()
280
291
evaluation_facctory = EvaluationDataFactory (splits_storage , segment_storage , rbs_storage )
281
292
282
- mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
293
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [] )
283
294
rbs = rule_based_segments .from_raw (data ["rbs" ]["d" ][0 ])
284
295
rbs_storage .update ([rbs ], [], 12 )
285
296
splits_storage .update ([mocked_split ], [], 12 )
@@ -303,7 +314,7 @@ def test_using_rbs_in_excluded(self):
303
314
segment_storage = InMemorySegmentStorage ()
304
315
evaluation_facctory = EvaluationDataFactory (splits_storage , segment_storage , rbs_storage )
305
316
306
- mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
317
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [] )
307
318
rbs = rule_based_segments .from_raw (data ["rbs" ]["d" ][0 ])
308
319
rbs2 = rule_based_segments .from_raw (data ["rbs" ]["d" ][1 ])
309
320
rbs_storage .update ([rbs , rbs2 ], [], 12 )
@@ -315,7 +326,52 @@ def test_using_rbs_in_excluded(self):
315
326
assert e .eval_with_context ('bilal' , 'bilal' , 'some' , {'email' : 'bilal' }, ctx )['treatment' ] == "on"
316
327
ctx = evaluation_facctory .context_for ('bilal2@split.io' , ['some' ])
317
328
assert e .eval_with_context ('bilal2@split.io' , 'bilal2@split.io' , 'some' , {'email' : 'bilal2@split.io' }, ctx )['treatment' ] == "off"
318
-
329
+
330
+ def test_prerequisites (self ):
331
+ splits_load = os .path .join (os .path .dirname (__file__ ), '../models/grammar/files' , 'splits_prereq.json' )
332
+ with open (splits_load , 'r' ) as flo :
333
+ data = json .loads (flo .read ())
334
+ e = evaluator .Evaluator (splitters .Splitter ())
335
+ splits_storage = InMemorySplitStorage ()
336
+ rbs_storage = InMemoryRuleBasedSegmentStorage ()
337
+ segment_storage = InMemorySegmentStorage ()
338
+ evaluation_facctory = EvaluationDataFactory (splits_storage , segment_storage , rbs_storage )
339
+
340
+ rbs = rule_based_segments .from_raw (data ["rbs" ]["d" ][0 ])
341
+ split1 = from_raw (data ["ff" ]["d" ][0 ])
342
+ split2 = from_raw (data ["ff" ]["d" ][1 ])
343
+ split3 = from_raw (data ["ff" ]["d" ][2 ])
344
+ split4 = from_raw (data ["ff" ]["d" ][3 ])
345
+ rbs_storage .update ([rbs ], [], 12 )
346
+ splits_storage .update ([split1 , split2 , split3 , split4 ], [], 12 )
347
+ segment = segments .from_raw ({'name' : 'segment-test' , 'added' : ['pato@split.io' ], 'removed' : [], 'till' : 123 })
348
+ segment_storage .put (segment )
349
+
350
+ ctx = evaluation_facctory .context_for ('bilal@split.io' , ['test_prereq' ])
351
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'test_prereq' , {'email' : 'bilal@split.io' }, ctx )['treatment' ] == "on"
352
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'test_prereq' , {}, ctx )['treatment' ] == "def_treatment"
353
+
354
+ ctx = evaluation_facctory .context_for ('mauro@split.io' , ['test_prereq' ])
355
+ assert e .eval_with_context ('mauro@split.io' , 'mauro@split.io' , 'test_prereq' , {'email' : 'mauro@split.io' }, ctx )['treatment' ] == "def_treatment"
356
+
357
+ ctx = evaluation_facctory .context_for ('pato@split.io' , ['test_prereq' ])
358
+ assert e .eval_with_context ('pato@split.io' , 'pato@split.io' , 'test_prereq' , {'email' : 'pato@split.io' }, ctx )['treatment' ] == "def_treatment"
359
+
360
+ ctx = evaluation_facctory .context_for ('nico@split.io' , ['test_prereq' ])
361
+ assert e .eval_with_context ('nico@split.io' , 'nico@split.io' , 'test_prereq' , {'email' : 'nico@split.io' }, ctx )['treatment' ] == "on"
362
+
363
+ ctx = evaluation_facctory .context_for ('bilal@split.io' , ['prereq_chain' ])
364
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'prereq_chain' , {'email' : 'bilal@split.io' }, ctx )['treatment' ] == "on_whitelist"
365
+
366
+ ctx = evaluation_facctory .context_for ('nico@split.io' , ['prereq_chain' ])
367
+ assert e .eval_with_context ('nico@split.io' , 'nico@split.io' , 'test_prereq' , {'email' : 'nico@split.io' }, ctx )['treatment' ] == "on"
368
+
369
+ ctx = evaluation_facctory .context_for ('pato@split.io' , ['prereq_chain' ])
370
+ assert e .eval_with_context ('pato@split.io' , 'pato@split.io' , 'prereq_chain' , {'email' : 'pato@split.io' }, ctx )['treatment' ] == "on_default"
371
+
372
+ ctx = evaluation_facctory .context_for ('mauro@split.io' , ['prereq_chain' ])
373
+ assert e .eval_with_context ('mauro@split.io' , 'mauro@split.io' , 'prereq_chain' , {'email' : 'mauro@split.io' }, ctx )['treatment' ] == "on_default"
374
+
319
375
@pytest .mark .asyncio
320
376
async def test_evaluate_treatment_with_rbs_in_condition_async (self ):
321
377
e = evaluator .Evaluator (splitters .Splitter ())
@@ -388,16 +444,63 @@ async def test_using_rbs_in_excluded_async(self):
388
444
ctx = await evaluation_facctory .context_for ('bilal2@split.io' , ['some' ])
389
445
assert e .eval_with_context ('bilal2@split.io' , 'bilal2@split.io' , 'some' , {'email' : 'bilal2@split.io' }, ctx )['treatment' ] == "off"
390
446
447
+ @pytest .mark .asyncio
448
+ async def test_prerequisites (self ):
449
+ splits_load = os .path .join (os .path .dirname (__file__ ), '../models/grammar/files' , 'splits_prereq.json' )
450
+ with open (splits_load , 'r' ) as flo :
451
+ data = json .loads (flo .read ())
452
+ e = evaluator .Evaluator (splitters .Splitter ())
453
+ splits_storage = InMemorySplitStorageAsync ()
454
+ rbs_storage = InMemoryRuleBasedSegmentStorageAsync ()
455
+ segment_storage = InMemorySegmentStorageAsync ()
456
+ evaluation_facctory = AsyncEvaluationDataFactory (splits_storage , segment_storage , rbs_storage )
457
+
458
+ rbs = rule_based_segments .from_raw (data ["rbs" ]["d" ][0 ])
459
+ split1 = from_raw (data ["ff" ]["d" ][0 ])
460
+ split2 = from_raw (data ["ff" ]["d" ][1 ])
461
+ split3 = from_raw (data ["ff" ]["d" ][2 ])
462
+ split4 = from_raw (data ["ff" ]["d" ][3 ])
463
+ await rbs_storage .update ([rbs ], [], 12 )
464
+ await splits_storage .update ([split1 , split2 , split3 , split4 ], [], 12 )
465
+ segment = segments .from_raw ({'name' : 'segment-test' , 'added' : ['pato@split.io' ], 'removed' : [], 'till' : 123 })
466
+ await segment_storage .put (segment )
467
+
468
+ ctx = await evaluation_facctory .context_for ('bilal@split.io' , ['test_prereq' ])
469
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'test_prereq' , {'email' : 'bilal@split.io' }, ctx )['treatment' ] == "on"
470
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'test_prereq' , {}, ctx )['treatment' ] == "def_treatment"
471
+
472
+ ctx = await evaluation_facctory .context_for ('mauro@split.io' , ['test_prereq' ])
473
+ assert e .eval_with_context ('mauro@split.io' , 'mauro@split.io' , 'test_prereq' , {'email' : 'mauro@split.io' }, ctx )['treatment' ] == "def_treatment"
474
+
475
+ ctx = await evaluation_facctory .context_for ('pato@split.io' , ['test_prereq' ])
476
+ assert e .eval_with_context ('pato@split.io' , 'pato@split.io' , 'test_prereq' , {'email' : 'pato@split.io' }, ctx )['treatment' ] == "def_treatment"
477
+
478
+ ctx = await evaluation_facctory .context_for ('nico@split.io' , ['test_prereq' ])
479
+ assert e .eval_with_context ('nico@split.io' , 'nico@split.io' , 'test_prereq' , {'email' : 'nico@split.io' }, ctx )['treatment' ] == "on"
480
+
481
+ ctx = await evaluation_facctory .context_for ('bilal@split.io' , ['prereq_chain' ])
482
+ assert e .eval_with_context ('bilal@split.io' , 'bilal@split.io' , 'prereq_chain' , {'email' : 'bilal@split.io' }, ctx )['treatment' ] == "on_whitelist"
483
+
484
+ ctx = await evaluation_facctory .context_for ('nico@split.io' , ['prereq_chain' ])
485
+ assert e .eval_with_context ('nico@split.io' , 'nico@split.io' , 'test_prereq' , {'email' : 'nico@split.io' }, ctx )['treatment' ] == "on"
486
+
487
+ ctx = await evaluation_facctory .context_for ('pato@split.io' , ['prereq_chain' ])
488
+ assert e .eval_with_context ('pato@split.io' , 'pato@split.io' , 'prereq_chain' , {'email' : 'pato@split.io' }, ctx )['treatment' ] == "on_default"
489
+
490
+ ctx = await evaluation_facctory .context_for ('mauro@split.io' , ['prereq_chain' ])
491
+ assert e .eval_with_context ('mauro@split.io' , 'mauro@split.io' , 'prereq_chain' , {'email' : 'mauro@split.io' }, ctx )['treatment' ] == "on_default"
492
+
391
493
class EvaluationDataFactoryTests (object ):
392
494
"""Test evaluation factory class."""
393
495
394
496
def test_get_context (self ):
395
497
"""Test context."""
396
- mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
498
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [Prerequisites ('split2' , ['on' ])])
499
+ split2 = Split ('split2' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [])
397
500
flag_storage = InMemorySplitStorage ([])
398
501
segment_storage = InMemorySegmentStorage ()
399
502
rbs_segment_storage = InMemoryRuleBasedSegmentStorage ()
400
- flag_storage .update ([mocked_split ], [], - 1 )
503
+ flag_storage .update ([mocked_split , split2 ], [], - 1 )
401
504
rbs = copy .deepcopy (rbs_raw )
402
505
rbs ['conditions' ].append (
403
506
{"matcherGroup" : {
@@ -421,6 +524,7 @@ def test_get_context(self):
421
524
ec = eval_factory .context_for ('bilal@split.io' , ['some' ])
422
525
assert ec .rbs_segments == {'sample_rule_based_segment' : rbs }
423
526
assert ec .segment_memberships == {"employees" : False }
527
+ assert ec .flags .get ("split2" ).name == "split2"
424
528
425
529
segment_storage .update ("employees" , {"mauro@split.io" }, {}, 1234 )
426
530
ec = eval_factory .context_for ('mauro@split.io' , ['some' ])
@@ -433,11 +537,12 @@ class EvaluationDataFactoryAsyncTests(object):
433
537
@pytest .mark .asyncio
434
538
async def test_get_context (self ):
435
539
"""Test context."""
436
- mocked_split = Split ('some' , 123 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False )
540
+ mocked_split = Split ('some' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [Prerequisites ('split2' , ['on' ])])
541
+ split2 = Split ('split2' , 12345 , False , 'off' , 'user' , Status .ACTIVE , 12 , split_conditions , 1.2 , 100 , 1234 , {}, None , False , [])
437
542
flag_storage = InMemorySplitStorageAsync ([])
438
543
segment_storage = InMemorySegmentStorageAsync ()
439
544
rbs_segment_storage = InMemoryRuleBasedSegmentStorageAsync ()
440
- await flag_storage .update ([mocked_split ], [], - 1 )
545
+ await flag_storage .update ([mocked_split , split2 ], [], - 1 )
441
546
rbs = copy .deepcopy (rbs_raw )
442
547
rbs ['conditions' ].append (
443
548
{"matcherGroup" : {
@@ -461,6 +566,7 @@ async def test_get_context(self):
461
566
ec = await eval_factory .context_for ('bilal@split.io' , ['some' ])
462
567
assert ec .rbs_segments == {'sample_rule_based_segment' : rbs }
463
568
assert ec .segment_memberships == {"employees" : False }
569
+ assert ec .flags .get ("split2" ).name == "split2"
464
570
465
571
await segment_storage .update ("employees" , {"mauro@split.io" }, {}, 1234 )
466
572
ec = await eval_factory .context_for ('mauro@split.io' , ['some' ])
0 commit comments