From dcaa7261263ecf0b6ffdab9fa9c48eab5cf2e4e0 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 22 May 2025 10:08:23 -0700 Subject: [PATCH 01/12] Added and updated DTOs --- .../io/split/client/CacheUpdaterService.java | 2 +- .../java/io/split/client/api/SplitView.java | 3 + .../io/split/client/dtos/Prerequisites.java | 12 +++ .../main/java/io/split/client/dtos/Split.java | 1 + .../split/engine/experiments/ParsedSplit.java | 25 +++-- .../split/engine/experiments/SplitParser.java | 3 +- .../storages/memory/InMemoryCacheImp.java | 3 +- .../io/split/client/SplitClientImplTest.java | 94 +++++++++---------- .../io/split/client/SplitManagerImplTest.java | 16 +++- .../evaluator/EvaluatorIntegrationTest.java | 10 +- .../split/engine/evaluator/EvaluatorTest.java | 14 +-- .../engine/experiments/SplitFetcherTest.java | 2 +- .../engine/experiments/SplitParserTest.java | 18 ++-- .../storages/memory/InMemoryCacheTest.java | 36 +++---- 14 files changed, 138 insertions(+), 101 deletions(-) create mode 100644 client/src/main/java/io/split/client/dtos/Prerequisites.java diff --git a/client/src/main/java/io/split/client/CacheUpdaterService.java b/client/src/main/java/io/split/client/CacheUpdaterService.java index 6d5f8a06..63b42663 100644 --- a/client/src/main/java/io/split/client/CacheUpdaterService.java +++ b/client/src/main/java/io/split/client/CacheUpdaterService.java @@ -51,7 +51,7 @@ public void updateCache(Map map) { String treatment = conditions.size() > 0 ? Treatments.CONTROL : localhostSplit.treatment; configurations.put(localhostSplit.treatment, localhostSplit.config); - split = new ParsedSplit(splitName, 0, false, treatment,conditions, LOCALHOST, 0, 100, 0, 0, configurations, new HashSet<>(), true); + split = new ParsedSplit(splitName, 0, false, treatment,conditions, LOCALHOST, 0, 100, 0, 0, configurations, new HashSet<>(), true, null); parsedSplits.removeIf(parsedSplit -> parsedSplit.feature().equals(splitName)); parsedSplits.add(split); } diff --git a/client/src/main/java/io/split/client/api/SplitView.java b/client/src/main/java/io/split/client/api/SplitView.java index a7701327..25a09e25 100644 --- a/client/src/main/java/io/split/client/api/SplitView.java +++ b/client/src/main/java/io/split/client/api/SplitView.java @@ -1,6 +1,7 @@ package io.split.client.api; import io.split.client.dtos.Partition; +import io.split.client.dtos.Prerequisites; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; @@ -27,6 +28,7 @@ public class SplitView { public List sets; public String defaultTreatment; public boolean impressionsDisabled; + public List prerequisites; public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { SplitView splitView = new SplitView(); @@ -48,6 +50,7 @@ public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { splitView.treatments = new ArrayList(treatments); splitView.configs = parsedSplit.configurations() == null? Collections.emptyMap() : parsedSplit.configurations() ; splitView.impressionsDisabled = parsedSplit.impressionsDisabled(); + splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites(): new ArrayList<>(); return splitView; } diff --git a/client/src/main/java/io/split/client/dtos/Prerequisites.java b/client/src/main/java/io/split/client/dtos/Prerequisites.java new file mode 100644 index 00000000..644cb5fc --- /dev/null +++ b/client/src/main/java/io/split/client/dtos/Prerequisites.java @@ -0,0 +1,12 @@ +package io.split.client.dtos; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class Prerequisites { + @SerializedName("n") + public String featureFlagName; + @SerializedName("ts") + public List treatments; +} diff --git a/client/src/main/java/io/split/client/dtos/Split.java b/client/src/main/java/io/split/client/dtos/Split.java index 866300d3..1b9a01e3 100644 --- a/client/src/main/java/io/split/client/dtos/Split.java +++ b/client/src/main/java/io/split/client/dtos/Split.java @@ -19,6 +19,7 @@ public class Split { public Map configurations; public HashSet sets; public Boolean impressionsDisabled = null; + public List prerequisites; @Override public String toString() { diff --git a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java index a4d52d6a..9d1bff1f 100644 --- a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java +++ b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java @@ -1,6 +1,7 @@ package io.split.engine.experiments; import com.google.common.collect.ImmutableList; +import io.split.client.dtos.Prerequisites; import io.split.engine.matchers.AttributeMatcher; import io.split.engine.matchers.RuleBasedSegmentMatcher; import io.split.engine.matchers.UserDefinedSegmentMatcher; @@ -34,6 +35,7 @@ public class ParsedSplit { private final Map _configurations; private final HashSet _flagSets; private final boolean _impressionsDisabled; + private List _prerequisites; public static ParsedSplit createParsedSplitForTests( String feature, @@ -45,7 +47,8 @@ public static ParsedSplit createParsedSplitForTests( long changeNumber, int algo, HashSet flagSets, - boolean impressionsDisabled + boolean impressionsDisabled, + List prerequisites ) { return new ParsedSplit( feature, @@ -60,7 +63,8 @@ public static ParsedSplit createParsedSplitForTests( algo, null, flagSets, - impressionsDisabled + impressionsDisabled, + prerequisites ); } @@ -75,7 +79,8 @@ public static ParsedSplit createParsedSplitForTests( int algo, Map configurations, HashSet flagSets, - boolean impressionsDisabled + boolean impressionsDisabled, + List prerequisites ) { return new ParsedSplit( feature, @@ -90,7 +95,8 @@ public static ParsedSplit createParsedSplitForTests( algo, configurations, flagSets, - impressionsDisabled + impressionsDisabled, + prerequisites ); } @@ -107,7 +113,8 @@ public ParsedSplit( int algo, Map configurations, HashSet flagSets, - boolean impressionsDisabled + boolean impressionsDisabled, + List prerequisites ) { _split = feature; _seed = seed; @@ -125,6 +132,7 @@ public ParsedSplit( _configurations = configurations; _flagSets = flagSets; _impressionsDisabled = impressionsDisabled; + _prerequisites = prerequisites; } public String feature() { @@ -171,6 +179,7 @@ public Map configurations() { public boolean impressionsDisabled() { return _impressionsDisabled; } + public List prerequisites() { return _prerequisites; } @Override public int hashCode() { @@ -205,7 +214,8 @@ public boolean equals(Object obj) { && _changeNumber == other._changeNumber && _algo == other._algo && _configurations == null ? other._configurations == null : _configurations.equals(other._configurations) - && _impressionsDisabled == other._impressionsDisabled; + && _impressionsDisabled == other._impressionsDisabled + && _prerequisites == other._prerequisites; } @Override @@ -231,6 +241,9 @@ public String toString() { bldr.append(_configurations); bldr.append(", impressionsDisabled:"); bldr.append(_impressionsDisabled); + bldr.append(", prerequisites:"); + bldr.append(_prerequisites); + return bldr.toString(); } diff --git a/client/src/main/java/io/split/engine/experiments/SplitParser.java b/client/src/main/java/io/split/engine/experiments/SplitParser.java index 414e5dfe..1d2bcc60 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitParser.java +++ b/client/src/main/java/io/split/engine/experiments/SplitParser.java @@ -68,6 +68,7 @@ private ParsedSplit parseWithoutExceptionHandling(Split split) { split.algo, split.configurations, split.sets, - split.impressionsDisabled); + split.impressionsDisabled, + split.prerequisites); } } \ No newline at end of file diff --git a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java index 57c63b99..b6544dc8 100644 --- a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java +++ b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java @@ -131,7 +131,8 @@ public void kill(String splitName, String defaultTreatment, long changeNumber) { parsedSplit.algo(), parsedSplit.configurations(), parsedSplit.flagSets(), - parsedSplit.impressionsDisabled() + parsedSplit.impressionsDisabled(), + parsedSplit.prerequisites() ); _concurrentMap.put(splitName, updatedSplit); diff --git a/client/src/test/java/io/split/client/SplitClientImplTest.java b/client/src/test/java/io/split/client/SplitClientImplTest.java index 8af9e1fb..bec502e7 100644 --- a/client/src/test/java/io/split/client/SplitClientImplTest.java +++ b/client/src/test/java/io/split/client/SplitClientImplTest.java @@ -83,7 +83,7 @@ public void nullKeyResultsInControl() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -113,7 +113,7 @@ public void nullTestResultsInControl() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -167,7 +167,7 @@ public void works() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -206,7 +206,7 @@ public void worksNullConfig() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -242,7 +242,7 @@ public void worksAndHasConfig() { Map configurations = new HashMap<>(); configurations.put(Treatments.ON, "{\"size\" : 30}"); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, configurations, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, configurations, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -279,7 +279,7 @@ public void lastConditionIsAlwaysDefault() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("adil@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -319,7 +319,7 @@ public void lastConditionIsAlwaysDefaultButWithTreatment() { configurations.put(Treatments.OFF, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - "user", 1, 1, configurations, new HashSet<>(), true); + "user", 1, 1, configurations, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -354,7 +354,7 @@ public void multipleConditionsWork() { ParsedCondition trevor_is_always_shown = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("trevor@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(adil_is_always_on, pato_is_never_shown, trevor_is_always_shown); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -389,7 +389,7 @@ public void killedTestAlwaysGoesToDefault() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("adil@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, true, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, true, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -429,7 +429,7 @@ public void killedTestAlwaysGoesToDefaultHasConfig() { configurations.put(Treatments.OFF, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, true, Treatments.OFF, conditions, - "user", 1, 1, configurations, new HashSet<>(), true); + "user", 1, 1, configurations, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -462,11 +462,11 @@ public void dependencyMatcherOn() { ParsedCondition parent_is_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition(Treatments.ON, 100))); List parent_conditions = Lists.newArrayList(parent_is_on); - ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, null); ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher(parent, Lists.newArrayList(Treatments.ON))), Lists.newArrayList(partition(Treatments.ON, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -497,11 +497,11 @@ public void dependencyMatcherOff() { ParsedCondition parent_is_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition(Treatments.ON, 100))); List parent_conditions = Lists.newArrayList(parent_is_on); - ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, null); ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher(parent, Lists.newArrayList(Treatments.OFF))), Lists.newArrayList(partition(Treatments.ON, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -531,7 +531,7 @@ public void dependencyMatcherControl() { ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher("not-exists", Lists.newArrayList(Treatments.OFF))), Lists.newArrayList(partition(Treatments.OFF, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.ON, dependent_conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.ON, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -561,7 +561,7 @@ public void attributesWork() { ParsedCondition users_with_age_greater_than_10_are_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new GreaterThanOrEqualToMatcher(10, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(adil_is_always_on, users_with_age_greater_than_10_are_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -596,7 +596,7 @@ public void attributesWork2() { ParsedCondition age_equal_to_0_should_be_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new EqualToMatcher(0, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -632,7 +632,7 @@ public void attributesGreaterThanNegativeNumber() { ParsedCondition age_equal_to_0_should_be_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new EqualToMatcher(-20, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -670,7 +670,7 @@ public void attributesForSets() { ParsedCondition any_of_set = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("products", new ContainsAnyOfSetMatcher(Lists.newArrayList("sms", "video"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(any_of_set); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -714,7 +714,7 @@ public void labelsArePopulated() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); SegmentCacheConsumer segmentCacheConsumer = mock(SegmentCacheConsumer.class); @@ -817,7 +817,7 @@ private void trafficAllocation(String key, int trafficAllocation, int trafficAll List conditions = Lists.newArrayList(whitelistCondition, rollOutToEveryone); ParsedSplit parsedSplit = new ParsedSplit(test, 123, false, Treatments.OFF, conditions, null, 1, - trafficAllocation, trafficAllocationSeed, 1, null, new HashSet<>(), true); + trafficAllocation, trafficAllocationSeed, 1, null, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -869,7 +869,7 @@ public void notInTrafficAllocationDefaultConfig() { List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = new ParsedSplit(test, 123, false, Treatments.OFF, conditions, null, - 1, trafficAllocation, trafficAllocationSeed, 1, configurations, new HashSet<>(), true); + 1, trafficAllocation, trafficAllocationSeed, 1, configurations, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -916,7 +916,7 @@ public void matchingBucketingKeysWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -953,7 +953,7 @@ public void matchingBucketingKeysByFlagSetWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -994,7 +994,7 @@ public void matchingBucketingKeysByFlagSetsWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1037,7 +1037,7 @@ public void impressionMetadataIsPropagated() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1229,7 +1229,7 @@ public void getTreatmentWithInvalidKeys() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1388,7 +1388,7 @@ public void clientCannotPerformActionsWhenDestroyed() throws InterruptedExceptio ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1445,7 +1445,7 @@ public void worksAndHasConfigTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(), true); + null, 1, 1, configurations, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1490,7 +1490,7 @@ public void worksAndHasConfigByFlagSetTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1538,7 +1538,7 @@ public void worksAndHasConfigByFlagSetsTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1581,7 +1581,7 @@ public void blockUntilReadyException() throws TimeoutException, InterruptedExcep Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1611,7 +1611,7 @@ public void nullKeyResultsInControlGetTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1642,7 +1642,7 @@ public void nullSplitsResultsInEmptyGetTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1698,7 +1698,7 @@ public void getTreatmentsWorks() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1730,7 +1730,7 @@ public void emptySplitsResultsInNullGetTreatments() { String test = "test1"; ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1788,9 +1788,9 @@ public void worksTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); ParsedSplit parsedSplit2 = ParsedSplit.createParsedSplitForTests(test2, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true); + null, 1, 1, new HashSet<>(), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); parsedSplits.put(test2, parsedSplit2); @@ -1829,7 +1829,7 @@ public void worksOneControlTreatments() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -1876,7 +1876,7 @@ public void treatmentsWorksAndHasConfig() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(), true); + null, 1, 1, configurations, new HashSet<>(), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1913,7 +1913,7 @@ public void testTreatmentsByFlagSet() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1958,7 +1958,7 @@ public void testTreatmentsByFlagSetInvalid() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1989,9 +1989,9 @@ public void testTreatmentsByFlagSets() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); ParsedSplit parsedSplit2 = ParsedSplit.createParsedSplitForTests(test2, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set3", "set4")), true); + null, 1, 1, new HashSet<>(Arrays.asList("set3", "set4")), true, null); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -2048,7 +2048,7 @@ public void treatmentsWorksAndHasConfigFlagSet() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -2099,7 +2099,7 @@ public void treatmentsWorksAndHasConfigFlagSets() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -2146,7 +2146,7 @@ public void impressionPropertiesTest() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(Arrays.asList("set")), true); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(Arrays.asList("set")), true, null); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); diff --git a/client/src/test/java/io/split/client/SplitManagerImplTest.java b/client/src/test/java/io/split/client/SplitManagerImplTest.java index 4843bd81..354ebcf5 100644 --- a/client/src/test/java/io/split/client/SplitManagerImplTest.java +++ b/client/src/test/java/io/split/client/SplitManagerImplTest.java @@ -3,6 +3,7 @@ import com.google.common.collect.Lists; import io.split.client.api.SplitView; +import io.split.client.dtos.Prerequisites; import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; import io.split.client.utils.Json; @@ -66,7 +67,11 @@ public void splitCallWithExistentSplit() { String existent = "existent"; SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); - ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(), false); + Prerequisites prereq = new Prerequisites(); + prereq.featureFlagName = "feature1"; + prereq.treatments = Lists.newArrayList("on"); + ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(), false, + Lists.newArrayList(prereq)); when(splitCacheConsumer.get(existent)).thenReturn(response); SplitManagerImpl splitManager = new SplitManagerImpl(splitCacheConsumer, @@ -81,6 +86,7 @@ public void splitCallWithExistentSplit() { Assert.assertEquals("off", theOne.treatments.get(0)); Assert.assertEquals(0, theOne.configs.size()); Assert.assertEquals("off", theOne.defaultTreatment); + Assert.assertEquals(prereq, theOne.prerequisites.get(0)); } @Test @@ -92,7 +98,7 @@ public void splitCallWithExistentSplitAndConfigs() { Map configurations = new HashMap<>(); configurations.put(Treatments.OFF, "{\"size\" : 30}"); - ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, configurations, new HashSet<>(), false); + ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, configurations, new HashSet<>(), false, null); when(splitCacheConsumer.get(existent)).thenReturn(response); SplitManagerImpl splitManager = new SplitManagerImpl(splitCacheConsumer, @@ -128,7 +134,7 @@ public void splitsCallWithSplit() { List parsedSplits = Lists.newArrayList(); SDKReadinessGates gates = mock(SDKReadinessGates.class); when(gates.isSDKReady()).thenReturn(false); - ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(), false); + ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(), false, null); parsedSplits.add(response); when(splitCacheConsumer.getAll()).thenReturn(parsedSplits); @@ -203,7 +209,7 @@ public void splitCallWithExistentSets() { String existent = "existent"; SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", - Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(Arrays.asList("set1", "set2", "set3")), false); + Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(Arrays.asList("set1", "set2", "set3")), false, null); when(splitCacheConsumer.get(existent)).thenReturn(response); SplitManagerImpl splitManager = new SplitManagerImpl(splitCacheConsumer, @@ -218,7 +224,7 @@ public void splitCallWithEmptySets() { String existent = "existent"; SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", - Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, null, false); + Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, null, false, null); when(splitCacheConsumer.get(existent)).thenReturn(response); SplitManagerImpl splitManager = new SplitManagerImpl(splitCacheConsumer, diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java index cb92bc17..c12fdbd7 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java @@ -200,11 +200,11 @@ private Evaluator buildEvaluatorAndLoadCache(boolean killed, int trafficAllocati List conditions = Lists.newArrayList(whitelistCondition, rollOutCondition); List conditionsForRBS = Lists.newArrayList(ruleBasedSegmentCondition, rollOutCondition); - ParsedSplit parsedSplit1 = new ParsedSplit("split_1", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366551, 100, 0, 2, null, new HashSet<>(), true); - ParsedSplit parsedSplit2 = new ParsedSplit("split_2", 0, true, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366552, 100, 0, 2, null, new HashSet<>(), true); - ParsedSplit parsedSplit3 = new ParsedSplit("split_3", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true); - ParsedSplit parsedSplit4 = new ParsedSplit("split_test", 0, killed, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366555, trafficAllocation, 0, 2, null, new HashSet<>(), true); - ParsedSplit parsedSplit5 = new ParsedSplit("split_5", 0, false, DEFAULT_TREATMENT_VALUE, conditionsForRBS, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true); + ParsedSplit parsedSplit1 = new ParsedSplit("split_1", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366551, 100, 0, 2, null, new HashSet<>(), true, null); + ParsedSplit parsedSplit2 = new ParsedSplit("split_2", 0, true, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366552, 100, 0, 2, null, new HashSet<>(), true, null); + ParsedSplit parsedSplit3 = new ParsedSplit("split_3", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, null); + ParsedSplit parsedSplit4 = new ParsedSplit("split_test", 0, killed, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366555, trafficAllocation, 0, 2, null, new HashSet<>(), true, null); + ParsedSplit parsedSplit5 = new ParsedSplit("split_5", 0, false, DEFAULT_TREATMENT_VALUE, conditionsForRBS, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, null); splitCache.putMany(Stream.of(parsedSplit1, parsedSplit2, parsedSplit3, parsedSplit4, parsedSplit5).collect(Collectors.toList())); ParsedRuleBasedSegment parsedRuleBasedSegment = new ParsedRuleBasedSegment("sample_rule_based_segment", diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java index 2fe2d83e..3b7a8435 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java @@ -68,7 +68,7 @@ public void evaluateWhenSplitNameDoesNotExistReturnControl() { @Test public void evaluateWhenSplitIsKilledReturnDefaultTreatment() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, null); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); @@ -80,7 +80,7 @@ public void evaluateWhenSplitIsKilledReturnDefaultTreatment() { @Test public void evaluateWithoutConditionsReturnDefaultTreatment() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, null); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); @@ -99,7 +99,7 @@ public void evaluateWithRollOutConditionBucketIsBiggerTrafficAllocationReturnDef ParsedCondition condition = new ParsedCondition(ConditionType.ROLLOUT, _matcher,_partitions, TEST_LABEL_VALUE); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 10, 12, 2, _configurations, new HashSet<>(), true); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 10, 12, 2, _configurations, new HashSet<>(), true, null); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(MATCHING_KEY, BUCKETING_KEY, null, _evaluationContext)).thenReturn(true); @@ -120,7 +120,7 @@ public void evaluateWithRollOutConditionTrafficAllocationIsBiggerBucketReturnTre ParsedCondition condition = new ParsedCondition(ConditionType.ROLLOUT, _matcher, _partitions, TEST_LABEL_VALUE); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true); @@ -141,7 +141,7 @@ public void evaluateWithWhitelistConditionReturnTreatment() { ParsedCondition condition = new ParsedCondition(ConditionType.WHITELIST, _matcher, _partitions, "test whitelist label"); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true); @@ -155,7 +155,7 @@ public void evaluateWithWhitelistConditionReturnTreatment() { @Test public void evaluateWithSets() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, null); List sets = new ArrayList<>(Arrays.asList("set1", "empty_set")); Map> flagSets = new HashMap<>(); flagSets.put("set1", new HashSet<>(Arrays.asList(SPLIT_NAME))); @@ -176,7 +176,7 @@ public void evaluateWithSets() { @Test public void evaluateWithSetsNotHaveFlags() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, null); List sets = new ArrayList<>(Arrays.asList("set2")); Map> flagSets = new HashMap<>(); Mockito.when(_splitCacheConsumer.getNamesByFlagSets(sets)).thenReturn(flagSets); diff --git a/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java b/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java index f2cc0cc4..a6c2468a 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java @@ -88,7 +88,7 @@ private void works(long startingChangeNumber) throws InterruptedException { ParsedCondition expectedParsedCondition = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(ConditionsTestUtil.partition("on", 10))); List expectedListOfMatcherAndSplits = Lists.newArrayList(expectedParsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("" + cache.getChangeNumber(), (int) cache.getChangeNumber(), false, Treatments.OFF, expectedListOfMatcherAndSplits, null, cache.getChangeNumber(), 1, new HashSet<>(), true); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("" + cache.getChangeNumber(), (int) cache.getChangeNumber(), false, Treatments.OFF, expectedListOfMatcherAndSplits, null, cache.getChangeNumber(), 1, new HashSet<>(), true, null); ParsedSplit actual = cache.get("" + cache.getChangeNumber()); Thread.sleep(1000); diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java index 5b581983..9b41bf96 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java @@ -94,7 +94,7 @@ public void works() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); assertTrue(expected.hashCode() != 0); @@ -137,7 +137,7 @@ public void worksWithConfig() { List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, - listOfMatcherAndSplits, "user", 1, 1, configurations, new HashSet<>(), false); + listOfMatcherAndSplits, "user", 1, 1, configurations, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); Assert.assertEquals(actual.configurations().get("on"), configurations.get("on")); @@ -176,7 +176,7 @@ public void worksForTwoConditions() { ParsedCondition parsedCondition2 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(EMPLOYEES)), turnOff); List listOfParsedConditions = Lists.newArrayList(parsedCondition1, parsedCondition2); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfParsedConditions, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfParsedConditions, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -245,7 +245,7 @@ public void worksWithAttributes() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -278,7 +278,7 @@ public void lessThanOrEqualTo() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -310,7 +310,7 @@ public void equalTo() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -341,7 +341,7 @@ public void equalToNegativeNumber() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -377,7 +377,7 @@ public void between() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } @@ -693,7 +693,7 @@ public void setMatcherTest(Condition c, io.split.engine.matchers.Matcher m) { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("splitName", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("splitName", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); Assert.assertEquals(actual, expected); } diff --git a/client/src/test/java/io/split/storages/memory/InMemoryCacheTest.java b/client/src/test/java/io/split/storages/memory/InMemoryCacheTest.java index d2079f1b..5589d71d 100644 --- a/client/src/test/java/io/split/storages/memory/InMemoryCacheTest.java +++ b/client/src/test/java/io/split/storages/memory/InMemoryCacheTest.java @@ -139,10 +139,10 @@ public void getMany() { @Test public void trafficTypesExist() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true); - ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true); - ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", new ArrayList<>(), "tt_2", 123, 2, null, true); - ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", new ArrayList<>(), "tt_3", 123, 2, null, true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true, null); + ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true, null); + ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", new ArrayList<>(), "tt_2", 123, 2, null, true, null); + ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", new ArrayList<>(), "tt_3", 123, 2, null, true, null); _cache.putMany(Stream.of(split, split2, split3, split4).collect(Collectors.toList())); assertTrue(_cache.trafficTypeExists("tt_2")); @@ -163,10 +163,10 @@ public void testSegmentNames() { ParsedCondition parsedCondition1 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(EMPLOYEES)), fullyRollout); ParsedCondition parsedCondition2 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(EMPLOYEES+"2")), turnOff); - ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", Stream.of(parsedCondition1).collect(Collectors.toList()), "tt", 123, 2, null, true); - ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", Stream.of(parsedCondition2).collect(Collectors.toList()), "tt", 123, 2, null, true); - ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", Stream.of(parsedCondition1).collect(Collectors.toList()), "tt_2", 123, 2, null, true); - ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", Stream.of(parsedCondition2).collect(Collectors.toList()), "tt_3", 123, 2, null, true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", Stream.of(parsedCondition1).collect(Collectors.toList()), "tt", 123, 2, null, true, null); + ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", Stream.of(parsedCondition2).collect(Collectors.toList()), "tt", 123, 2, null, true, null); + ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", Stream.of(parsedCondition1).collect(Collectors.toList()), "tt_2", 123, 2, null, true, null); + ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", Stream.of(parsedCondition2).collect(Collectors.toList()), "tt_3", 123, 2, null, true, null); _cache.putMany(Stream.of(split, split2, split3, split4).collect(Collectors.toList())); @@ -178,19 +178,19 @@ public void testSegmentNames() { } private ParsedSplit getParsedSplitWithFlagSetsSameStorage(String splitName) { - return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1", "set2")), true); + return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, null); } private ParsedSplit getParsedSplitWithFlagSetsNotSameStorage(String splitName) { - return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set3")), true); + return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set3")), true, null); } private ParsedSplit getParsedSplitFlagSetsNull(String splitName) { - return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true); + return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, null, true, null); } private ParsedSplit getParsedSplitFlagSetsEmpty(String splitName) { - return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true); + return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true, null); } @Test @@ -204,7 +204,7 @@ public void testPutMany() { @Test public void testIncreaseTrafficType() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true, null); _cache.putMany(Stream.of(split).collect(Collectors.toList())); _cache.increaseTrafficType("tt_2"); assertTrue(_cache.trafficTypeExists("tt_2")); @@ -212,7 +212,7 @@ public void testIncreaseTrafficType() { @Test public void testDecreaseTrafficType() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(), true, null); _cache.putMany(Stream.of(split).collect(Collectors.toList())); _cache.decreaseTrafficType("tt"); assertFalse(_cache.trafficTypeExists("tt_2")); @@ -220,10 +220,10 @@ public void testDecreaseTrafficType() { @Test public void testGetNamesByFlagSets() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1", "set2", "set3")), true); - ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1")), true); - ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", new ArrayList<>(), "tt_2", 123, 2, new HashSet<>(Arrays.asList("set4")), true); - ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", new ArrayList<>(), "tt_3", 123, 2, new HashSet<>(Arrays.asList("set2")), true); + ParsedSplit split = ParsedSplit.createParsedSplitForTests("splitName_1", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1", "set2", "set3")), true, null); + ParsedSplit split2 = ParsedSplit.createParsedSplitForTests("splitName_2", 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2, new HashSet<>(Arrays.asList("set1")), true, null); + ParsedSplit split3 = ParsedSplit.createParsedSplitForTests("splitName_3", 0, false, "default_treatment", new ArrayList<>(), "tt_2", 123, 2, new HashSet<>(Arrays.asList("set4")), true, null); + ParsedSplit split4 = ParsedSplit.createParsedSplitForTests("splitName_4", 0, false, "default_treatment", new ArrayList<>(), "tt_3", 123, 2, new HashSet<>(Arrays.asList("set2")), true, null); _cache.putMany(Stream.of(split, split2, split3, split4).collect(Collectors.toList())); Map> namesByFlagSets = _cache.getNamesByFlagSets(new ArrayList<>(Arrays.asList("set1", "set2", "set3"))); From 6e5286b0ee51297e2fb330af7ca9b7aa78e32648 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 23 May 2025 10:17:10 -0700 Subject: [PATCH 02/12] Added prereq matcher --- .../engine/matchers/PrerequisitesMatcher.java | 60 +++++++++++++++++++ .../matchers/PrerequisitesMatcherTest.java | 52 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java create mode 100644 client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java new file mode 100644 index 00000000..2fabf58f --- /dev/null +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -0,0 +1,60 @@ +package io.split.engine.matchers; + +import io.split.client.dtos.Prerequisites; +import io.split.engine.evaluator.EvaluationContext; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class PrerequisitesMatcher implements Matcher { + private List _prerequisites; + + public PrerequisitesMatcher(List prerequisites) { + _prerequisites = prerequisites; + } + + @Override + public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) { + if (matchValue == null) { + return false; + } + + if (!(matchValue instanceof String)) { + return false; + } + for (Prerequisites prerequisites : _prerequisites) { + String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, prerequisites.featureFlagName, attributes). treatment; + if (!prerequisites.treatments.contains(treatment)) { + return false; + } + } + return true; + } + + @Override + public String toString() { + StringBuilder bldr = new StringBuilder(); + bldr.append("prerequisites: "); + bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + return bldr.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PrerequisitesMatcher that = (PrerequisitesMatcher) o; + + return Objects.equals(_prerequisites, that._prerequisites); + } + + @Override + public int hashCode() { + int result = _prerequisites != null ? _prerequisites.hashCode() : 0; + result = 31 * result + (_prerequisites != null ? _prerequisites.hashCode() : 0); + return result; + } +} diff --git a/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java new file mode 100644 index 00000000..23c2af1c --- /dev/null +++ b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java @@ -0,0 +1,52 @@ +package io.split.engine.matchers; + +import io.split.client.dtos.Prerequisites; +import io.split.client.utils.Json; +import io.split.engine.evaluator.EvaluationContext; +import io.split.engine.evaluator.Evaluator; +import io.split.engine.evaluator.EvaluatorImp; +import io.split.storages.RuleBasedSegmentCache; +import io.split.storages.SegmentCache; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for Prerequisites matcher + */ +public class PrerequisitesMatcherTest { + + @Test + public void works() { + Evaluator evaluator = Mockito.mock(Evaluator.class); + EvaluationContext evaluationContext = new EvaluationContext(evaluator, Mockito.mock(SegmentCache.class), Mockito.mock(RuleBasedSegmentCache.class)); + List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"on\"]}", Prerequisites.class), Json.fromJson("{\"n\": \"split2\", \"ts\": [\"off\"]}", Prerequisites.class)); + PrerequisitesMatcher matcher = new PrerequisitesMatcher(prerequisites); + Assert.assertEquals("prerequisites: split1 [on], split2 [off]", matcher.toString()); + PrerequisitesMatcher matcher2 = new PrerequisitesMatcher(prerequisites); + Assert.assertTrue(matcher.equals(matcher2)); + Assert.assertTrue(matcher.hashCode() != 0); + + Mockito.when(evaluator.evaluateFeature("user", "user", "split1", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Mockito.when(evaluator.evaluateFeature("user", "user", "split2", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("off", "")); + Assert.assertTrue(matcher.match("user", "user", null, evaluationContext)); + + Mockito.when(evaluator.evaluateFeature("user", "user", "split2", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Assert.assertFalse(matcher.match("user", "user", null, evaluationContext)); + } + + @Test + public void invalidParams() { + Evaluator evaluator = Mockito.mock(Evaluator.class); + EvaluationContext evaluationContext = new EvaluationContext(evaluator, Mockito.mock(SegmentCache.class), Mockito.mock(RuleBasedSegmentCache.class)); + + List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"on\"]}", Prerequisites.class), Json.fromJson("{\"n\": \"split2\", \"ts\": [\"off\"]}", Prerequisites.class)); + PrerequisitesMatcher matcher = new PrerequisitesMatcher(prerequisites); + Mockito.when(evaluator.evaluateFeature("user", "user", "split1", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Assert.assertFalse(matcher.match(null, null, null, evaluationContext)); + Assert.assertFalse(matcher.match(123, null, null, evaluationContext)); + } +} \ No newline at end of file From d84392052e9ee637bda9e701782ef27f7b1b0f56 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 23 May 2025 10:48:22 -0700 Subject: [PATCH 03/12] polish --- .../java/io/split/engine/matchers/PrerequisitesMatcher.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java index 2fabf58f..b6e11180 100644 --- a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -25,7 +25,8 @@ public boolean match(Object matchValue, String bucketingKey, Map return false; } for (Prerequisites prerequisites : _prerequisites) { - String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, prerequisites.featureFlagName, attributes). treatment; + String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, + prerequisites.featureFlagName, attributes). treatment; if (!prerequisites.treatments.contains(treatment)) { return false; } @@ -37,7 +38,8 @@ public boolean match(Object matchValue, String bucketingKey, Map public String toString() { StringBuilder bldr = new StringBuilder(); bldr.append("prerequisites: "); - bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); return bldr.toString(); } From 29a673b75dc472814712d48c934dc443e453abef Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 27 May 2025 09:35:16 -0700 Subject: [PATCH 04/12] Update Evaluator --- .../split/engine/evaluator/EvaluatorImp.java | 25 ++++++++---- .../io/split/engine/evaluator/Labels.java | 1 + .../engine/matchers/PrerequisitesMatcher.java | 5 +++ .../split/engine/evaluator/EvaluatorTest.java | 39 +++++++++++++++++++ 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java index 32b4a8df..8a3fbc29 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -4,6 +4,7 @@ import io.split.client.exceptions.ChangeNumberExceptionWrapper; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.engine.splitter.Splitter; import io.split.grammar.Treatments; import io.split.storages.RuleBasedSegmentCacheConsumer; @@ -27,6 +28,7 @@ public class EvaluatorImp implements Evaluator { private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer; private final EvaluationContext _evaluationContext; private final SplitCacheConsumer _splitCacheConsumer; + private PrerequisitesMatcher _prerequisitesMatcher; public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache, RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) { @@ -88,8 +90,8 @@ private List getFeatureFlagNamesByFlagSets(List flagSets) { private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bucketingKey, ParsedSplit parsedSplit, Map attributes) throws ChangeNumberExceptionWrapper { try { + String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; if (parsedSplit.killed()) { - String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), Labels.KILLED, @@ -98,6 +100,17 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu parsedSplit.impressionsDisabled()); } + String bk = (bucketingKey == null) ? matchingKey : bucketingKey; + + if (!_prerequisitesMatcher.match(matchingKey, bk, attributes, _evaluationContext)) { + return new TreatmentLabelAndChangeNumber( + parsedSplit.defaultTreatment(), + Labels.PREREQUISITES_NOT_MET, + parsedSplit.changeNumber(), + config, + parsedSplit.impressionsDisabled()); + } + /* * There are three parts to a single Feature flag: 1) Whitelists 2) Traffic Allocation * 3) Rollout. The flag inRollout is there to understand when we move into the Rollout @@ -106,8 +119,6 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu */ boolean inRollout = false; - String bk = (bucketingKey == null) ? matchingKey : bucketingKey; - for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) { if (!inRollout && parsedCondition.conditionType() == ConditionType.ROLLOUT) { @@ -118,7 +129,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu if (bucket > parsedSplit.trafficAllocation()) { // out of split - String config = parsedSplit.configurations() != null ? + config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; return new TreatmentLabelAndChangeNumber(parsedSplit.defaultTreatment(), Labels.NOT_IN_SPLIT, parsedSplit.changeNumber(), config, parsedSplit.impressionsDisabled()); @@ -130,7 +141,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu if (parsedCondition.matcher().match(matchingKey, bucketingKey, attributes, _evaluationContext)) { String treatment = Splitter.getTreatment(bk, parsedSplit.seed(), parsedCondition.partitions(), parsedSplit.algo()); - String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(treatment) : null; + config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(treatment) : null; return new TreatmentLabelAndChangeNumber( treatment, parsedCondition.label(), @@ -140,7 +151,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu } } - String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; + config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), Labels.DEFAULT_RULE, @@ -158,7 +169,7 @@ private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, St if (parsedSplit == null) { return new TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.DEFINITION_NOT_FOUND); } - + _prerequisitesMatcher = new PrerequisitesMatcher(parsedSplit.prerequisites()); return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes); } catch (ChangeNumberExceptionWrapper e) { _log.error("Evaluator Exception", e.wrappedException()); diff --git a/client/src/main/java/io/split/engine/evaluator/Labels.java b/client/src/main/java/io/split/engine/evaluator/Labels.java index ac5a465a..9bda16a8 100644 --- a/client/src/main/java/io/split/engine/evaluator/Labels.java +++ b/client/src/main/java/io/split/engine/evaluator/Labels.java @@ -7,4 +7,5 @@ public class Labels { public static final String DEFINITION_NOT_FOUND = "definition not found"; public static final String EXCEPTION = "exception"; public static final String UNSUPPORTED_MATCHER = "targeting rule type unsupported by sdk"; + public static final String PREREQUISITES_NOT_MET = "prerequisites not met"; } \ No newline at end of file diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java index b6e11180..557fe113 100644 --- a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -24,6 +24,11 @@ public boolean match(Object matchValue, String bucketingKey, Map if (!(matchValue instanceof String)) { return false; } + + if (_prerequisites == null) { + return true; + } + for (Prerequisites prerequisites : _prerequisites) { String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, prerequisites.featureFlagName, attributes). treatment; diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java index 3b7a8435..fe7b12f1 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java @@ -2,6 +2,8 @@ import io.split.client.dtos.ConditionType; import io.split.client.dtos.Partition; +import io.split.client.dtos.Prerequisites; +import io.split.client.utils.Json; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; import io.split.engine.matchers.CombiningMatcher; @@ -186,4 +188,41 @@ public void evaluateWithSetsNotHaveFlags() { Map result = _evaluator.evaluateFeaturesByFlagSets(MATCHING_KEY, BUCKETING_KEY, sets, null); Assert.assertTrue(result.isEmpty()); } + + @Test + public void evaluateWithPrerequisites() { + Partition partition = new Partition(); + partition.treatment = TREATMENT_VALUE; + partition.size = 100; + _partitions.add(partition); + ParsedCondition condition = new ParsedCondition(ConditionType.WHITELIST, _matcher, _partitions, "test whitelist label"); + _conditions.add(condition); + List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"" + TREATMENT_VALUE + "\"]}", Prerequisites.class)); + + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites); + ParsedSplit split1 = new ParsedSplit("split1", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); + + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); + Mockito.when(_splitCacheConsumer.get("split1")).thenReturn(split1); + Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true); + + EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals(TREATMENT_VALUE, result.treatment); + assertEquals("test whitelist label", result.label); + assertEquals(CHANGE_NUMBER, result.changeNumber); + + Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(false); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals(DEFAULT_TREATMENT_VALUE, result.treatment); + assertEquals(Labels.PREREQUISITES_NOT_MET, result.label); + assertEquals(CHANGE_NUMBER, result.changeNumber); + + // if split is killed, label should be killed. + split = new ParsedSplit(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites); + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals(DEFAULT_TREATMENT_VALUE, result.treatment); + assertEquals(Labels.KILLED, result.label); + assertEquals(CHANGE_NUMBER, result.changeNumber); + } } \ No newline at end of file From c280a3eb8aaa590be18436ee23152c871c20e97d Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 27 May 2025 09:39:04 -0700 Subject: [PATCH 05/12] updated test --- .../io/split/engine/matchers/PrerequisitesMatcherTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java index 23c2af1c..4fe92d04 100644 --- a/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java +++ b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java @@ -48,5 +48,8 @@ public void invalidParams() { Mockito.when(evaluator.evaluateFeature("user", "user", "split1", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); Assert.assertFalse(matcher.match(null, null, null, evaluationContext)); Assert.assertFalse(matcher.match(123, null, null, evaluationContext)); + + matcher = new PrerequisitesMatcher(null); + Assert.assertFalse(matcher.match(123, null, null, evaluationContext)); } } \ No newline at end of file From 32ea00d2479b61b5df89804afe37f6cdf3c81c7b Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 27 May 2025 11:50:03 -0700 Subject: [PATCH 06/12] polish --- .../java/io/split/client/api/SplitView.java | 4 +- .../split/engine/evaluator/EvaluatorImp.java | 5 +- .../split/engine/experiments/ParsedSplit.java | 12 +-- .../split/engine/experiments/SplitParser.java | 3 +- .../engine/matchers/PrerequisitesMatcher.java | 6 +- .../io/split/client/SplitClientImplTest.java | 101 +++++++++--------- .../io/split/client/SplitManagerImplTest.java | 5 +- .../evaluator/EvaluatorIntegrationTest.java | 11 +- .../split/engine/evaluator/EvaluatorTest.java | 21 ++-- .../engine/experiments/SplitParserTest.java | 30 ++++-- 10 files changed, 109 insertions(+), 89 deletions(-) diff --git a/client/src/main/java/io/split/client/api/SplitView.java b/client/src/main/java/io/split/client/api/SplitView.java index 25a09e25..9a70f08e 100644 --- a/client/src/main/java/io/split/client/api/SplitView.java +++ b/client/src/main/java/io/split/client/api/SplitView.java @@ -28,7 +28,7 @@ public class SplitView { public List sets; public String defaultTreatment; public boolean impressionsDisabled; - public List prerequisites; + public String prerequisites; public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { SplitView splitView = new SplitView(); @@ -50,7 +50,7 @@ public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { splitView.treatments = new ArrayList(treatments); splitView.configs = parsedSplit.configurations() == null? Collections.emptyMap() : parsedSplit.configurations() ; splitView.impressionsDisabled = parsedSplit.impressionsDisabled(); - splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites(): new ArrayList<>(); + splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites().toString(): ""; return splitView; } diff --git a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java index 8a3fbc29..c56a7cb4 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -4,7 +4,6 @@ import io.split.client.exceptions.ChangeNumberExceptionWrapper; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; -import io.split.engine.matchers.PrerequisitesMatcher; import io.split.engine.splitter.Splitter; import io.split.grammar.Treatments; import io.split.storages.RuleBasedSegmentCacheConsumer; @@ -28,7 +27,6 @@ public class EvaluatorImp implements Evaluator { private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer; private final EvaluationContext _evaluationContext; private final SplitCacheConsumer _splitCacheConsumer; - private PrerequisitesMatcher _prerequisitesMatcher; public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache, RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) { @@ -102,7 +100,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu String bk = (bucketingKey == null) ? matchingKey : bucketingKey; - if (!_prerequisitesMatcher.match(matchingKey, bk, attributes, _evaluationContext)) { + if (!parsedSplit.prerequisites().match(matchingKey, bk, attributes, _evaluationContext)) { return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), Labels.PREREQUISITES_NOT_MET, @@ -169,7 +167,6 @@ private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, St if (parsedSplit == null) { return new TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.DEFINITION_NOT_FOUND); } - _prerequisitesMatcher = new PrerequisitesMatcher(parsedSplit.prerequisites()); return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes); } catch (ChangeNumberExceptionWrapper e) { _log.error("Evaluator Exception", e.wrappedException()); diff --git a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java index 9d1bff1f..8cb8fd6a 100644 --- a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java +++ b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java @@ -1,8 +1,8 @@ package io.split.engine.experiments; import com.google.common.collect.ImmutableList; -import io.split.client.dtos.Prerequisites; import io.split.engine.matchers.AttributeMatcher; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.engine.matchers.RuleBasedSegmentMatcher; import io.split.engine.matchers.UserDefinedSegmentMatcher; @@ -35,7 +35,7 @@ public class ParsedSplit { private final Map _configurations; private final HashSet _flagSets; private final boolean _impressionsDisabled; - private List _prerequisites; + private PrerequisitesMatcher _prerequisites; public static ParsedSplit createParsedSplitForTests( String feature, @@ -48,7 +48,7 @@ public static ParsedSplit createParsedSplitForTests( int algo, HashSet flagSets, boolean impressionsDisabled, - List prerequisites + PrerequisitesMatcher prerequisites ) { return new ParsedSplit( feature, @@ -80,7 +80,7 @@ public static ParsedSplit createParsedSplitForTests( Map configurations, HashSet flagSets, boolean impressionsDisabled, - List prerequisites + PrerequisitesMatcher prerequisites ) { return new ParsedSplit( feature, @@ -114,7 +114,7 @@ public ParsedSplit( Map configurations, HashSet flagSets, boolean impressionsDisabled, - List prerequisites + PrerequisitesMatcher prerequisites ) { _split = feature; _seed = seed; @@ -179,7 +179,7 @@ public Map configurations() { public boolean impressionsDisabled() { return _impressionsDisabled; } - public List prerequisites() { return _prerequisites; } + public PrerequisitesMatcher prerequisites() { return _prerequisites; } @Override public int hashCode() { diff --git a/client/src/main/java/io/split/engine/experiments/SplitParser.java b/client/src/main/java/io/split/engine/experiments/SplitParser.java index 1d2bcc60..5771c9ae 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitParser.java +++ b/client/src/main/java/io/split/engine/experiments/SplitParser.java @@ -6,6 +6,7 @@ import io.split.client.dtos.Partition; import io.split.client.dtos.Split; import io.split.engine.matchers.CombiningMatcher; +import io.split.engine.matchers.PrerequisitesMatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +70,6 @@ private ParsedSplit parseWithoutExceptionHandling(Split split) { split.configurations, split.sets, split.impressionsDisabled, - split.prerequisites); + new PrerequisitesMatcher(split.prerequisites)); } } \ No newline at end of file diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java index 557fe113..cbcff178 100644 --- a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -43,8 +43,10 @@ public boolean match(Object matchValue, String bucketingKey, Map public String toString() { StringBuilder bldr = new StringBuilder(); bldr.append("prerequisites: "); - bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + - pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + if (this._prerequisites != null) { + bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + } return bldr.toString(); } diff --git a/client/src/test/java/io/split/client/SplitClientImplTest.java b/client/src/test/java/io/split/client/SplitClientImplTest.java index bec502e7..08e4b1c0 100644 --- a/client/src/test/java/io/split/client/SplitClientImplTest.java +++ b/client/src/test/java/io/split/client/SplitClientImplTest.java @@ -11,6 +11,12 @@ import io.split.client.impressions.ImpressionsManager; import io.split.client.interceptors.FlagSetsFilter; import io.split.client.interceptors.FlagSetsFilterImpl; +import io.split.engine.matchers.PrerequisitesMatcher; +import io.split.engine.matchers.CombiningMatcher; +import io.split.engine.matchers.EqualToMatcher; +import io.split.engine.matchers.GreaterThanOrEqualToMatcher; +import io.split.engine.matchers.AllKeysMatcher; +import io.split.engine.matchers.DependencyMatcher; import io.split.storages.RuleBasedSegmentCacheConsumer; import io.split.storages.SegmentCacheConsumer; import io.split.storages.SplitCacheConsumer; @@ -18,11 +24,6 @@ import io.split.engine.SDKReadinessGates; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; -import io.split.engine.matchers.AllKeysMatcher; -import io.split.engine.matchers.CombiningMatcher; -import io.split.engine.matchers.DependencyMatcher; -import io.split.engine.matchers.EqualToMatcher; -import io.split.engine.matchers.GreaterThanOrEqualToMatcher; import io.split.engine.matchers.collections.ContainsAnyOfSetMatcher; import io.split.engine.matchers.strings.WhitelistMatcher; import io.split.grammar.Treatments; @@ -83,7 +84,7 @@ public void nullKeyResultsInControl() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -167,7 +168,7 @@ public void works() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -206,7 +207,7 @@ public void worksNullConfig() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -242,7 +243,7 @@ public void worksAndHasConfig() { Map configurations = new HashMap<>(); configurations.put(Treatments.ON, "{\"size\" : 30}"); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, configurations, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -279,7 +280,7 @@ public void lastConditionIsAlwaysDefault() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("adil@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -319,7 +320,7 @@ public void lastConditionIsAlwaysDefaultButWithTreatment() { configurations.put(Treatments.OFF, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - "user", 1, 1, configurations, new HashSet<>(), true, null); + "user", 1, 1, configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -354,7 +355,7 @@ public void multipleConditionsWork() { ParsedCondition trevor_is_always_shown = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("trevor@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(adil_is_always_on, pato_is_never_shown, trevor_is_always_shown); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -389,7 +390,7 @@ public void killedTestAlwaysGoesToDefault() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(Lists.newArrayList("adil@codigo.com"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, true, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, true, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -462,11 +463,11 @@ public void dependencyMatcherOn() { ParsedCondition parent_is_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition(Treatments.ON, 100))); List parent_conditions = Lists.newArrayList(parent_is_on); - ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher(parent, Lists.newArrayList(Treatments.ON))), Lists.newArrayList(partition(Treatments.ON, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -497,11 +498,11 @@ public void dependencyMatcherOff() { ParsedCondition parent_is_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition(Treatments.ON, 100))); List parent_conditions = Lists.newArrayList(parent_is_on); - ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parentSplit = ParsedSplit.createParsedSplitForTests(parent, 123, false, Treatments.OFF, parent_conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher(parent, Lists.newArrayList(Treatments.OFF))), Lists.newArrayList(partition(Treatments.ON, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.OFF, dependent_conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -531,7 +532,7 @@ public void dependencyMatcherControl() { ParsedCondition dependent_needs_parent = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new DependencyMatcher("not-exists", Lists.newArrayList(Treatments.OFF))), Lists.newArrayList(partition(Treatments.OFF, 100))); List dependent_conditions = Lists.newArrayList(dependent_needs_parent); - ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.ON, dependent_conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit dependentSplit = ParsedSplit.createParsedSplitForTests(dependent, 123, false, Treatments.ON, dependent_conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -561,7 +562,7 @@ public void attributesWork() { ParsedCondition users_with_age_greater_than_10_are_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new GreaterThanOrEqualToMatcher(10, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(adil_is_always_on, users_with_age_greater_than_10_are_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -596,7 +597,7 @@ public void attributesWork2() { ParsedCondition age_equal_to_0_should_be_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new EqualToMatcher(0, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -632,7 +633,7 @@ public void attributesGreaterThanNegativeNumber() { ParsedCondition age_equal_to_0_should_be_on = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("age", new EqualToMatcher(-20, DataType.NUMBER)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -670,7 +671,7 @@ public void attributesForSets() { ParsedCondition any_of_set = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of("products", new ContainsAnyOfSetMatcher(Lists.newArrayList("sms", "video"))), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(any_of_set); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -714,7 +715,7 @@ public void labelsArePopulated() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); SegmentCacheConsumer segmentCacheConsumer = mock(SegmentCacheConsumer.class); @@ -817,7 +818,7 @@ private void trafficAllocation(String key, int trafficAllocation, int trafficAll List conditions = Lists.newArrayList(whitelistCondition, rollOutToEveryone); ParsedSplit parsedSplit = new ParsedSplit(test, 123, false, Treatments.OFF, conditions, null, 1, - trafficAllocation, trafficAllocationSeed, 1, null, new HashSet<>(), true, null); + trafficAllocation, trafficAllocationSeed, 1, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -869,7 +870,7 @@ public void notInTrafficAllocationDefaultConfig() { List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = new ParsedSplit(test, 123, false, Treatments.OFF, conditions, null, - 1, trafficAllocation, trafficAllocationSeed, 1, configurations, new HashSet<>(), true, null); + 1, trafficAllocation, trafficAllocationSeed, 1, configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -916,7 +917,7 @@ public void matchingBucketingKeysWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -953,7 +954,7 @@ public void matchingBucketingKeysByFlagSetWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -994,7 +995,7 @@ public void matchingBucketingKeysByFlagSetsWork() { ParsedCondition aijaz_should_match = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new WhitelistMatcher(whitelist)), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(aijaz_should_match); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, "user", 1, 1, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1037,7 +1038,7 @@ public void impressionMetadataIsPropagated() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1229,7 +1230,7 @@ public void getTreatmentWithInvalidKeys() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1388,7 +1389,7 @@ public void clientCannotPerformActionsWhenDestroyed() throws InterruptedExceptio ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1445,7 +1446,7 @@ public void worksAndHasConfigTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(), true, null); + null, 1, 1, configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1490,7 +1491,7 @@ public void worksAndHasConfigByFlagSetTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1538,7 +1539,7 @@ public void worksAndHasConfigByFlagSetsTryKetTreatmentWithKey() { configurations.put(Treatments.ON, "{\"size\" : 30}"); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1581,7 +1582,7 @@ public void blockUntilReadyException() throws TimeoutException, InterruptedExcep Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1611,7 +1612,7 @@ public void nullKeyResultsInControlGetTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1642,7 +1643,7 @@ public void nullSplitsResultsInEmptyGetTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1698,7 +1699,7 @@ public void getTreatmentsWorks() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1730,7 +1731,7 @@ public void emptySplitsResultsInNullGetTreatments() { String test = "test1"; ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map splits = new HashMap<>(); splits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1788,9 +1789,9 @@ public void worksTreatments() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); ParsedSplit parsedSplit2 = ParsedSplit.createParsedSplitForTests(test2, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(), true, null); + null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); parsedSplits.put(test2, parsedSplit2); @@ -1829,7 +1830,7 @@ public void worksOneControlTreatments() { ParsedCondition rollOutToEveryone = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new AllKeysMatcher()), Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -1876,7 +1877,7 @@ public void treatmentsWorksAndHasConfig() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(), true, null); + null, 1, 1, configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); SDKReadinessGates gates = mock(SDKReadinessGates.class); @@ -1913,7 +1914,7 @@ public void testTreatmentsByFlagSet() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1958,7 +1959,7 @@ public void testTreatmentsByFlagSetInvalid() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -1989,9 +1990,9 @@ public void testTreatmentsByFlagSets() { Lists.newArrayList(partition("on", 100))); List conditions = Lists.newArrayList(rollOutToEveryone); ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, null); + null, 1, 1, new HashSet<>(Arrays.asList("set1", "set2")), true, new PrerequisitesMatcher(null)); ParsedSplit parsedSplit2 = ParsedSplit.createParsedSplitForTests(test2, 123, false, Treatments.OFF, conditions, - null, 1, 1, new HashSet<>(Arrays.asList("set3", "set4")), true, null); + null, 1, 1, new HashSet<>(Arrays.asList("set3", "set4")), true, new PrerequisitesMatcher(null)); SDKReadinessGates gates = mock(SDKReadinessGates.class); SplitCacheConsumer splitCacheConsumer = mock(SplitCacheConsumer.class); @@ -2048,7 +2049,7 @@ public void treatmentsWorksAndHasConfigFlagSet() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -2099,7 +2100,7 @@ public void treatmentsWorksAndHasConfigFlagSets() { ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, - null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, null); + null, 1, 1, configurations, new HashSet<>(Arrays.asList("set1")), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); @@ -2146,7 +2147,7 @@ public void impressionPropertiesTest() { ); List conditions = Lists.newArrayList(age_equal_to_0_should_be_on); - ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(Arrays.asList("set")), true, null); + ParsedSplit parsedSplit = ParsedSplit.createParsedSplitForTests(test, 123, false, Treatments.OFF, conditions, null, 1, 1, new HashSet<>(Arrays.asList("set")), true, new PrerequisitesMatcher(null)); Map parsedSplits = new HashMap<>(); parsedSplits.put(test, parsedSplit); diff --git a/client/src/test/java/io/split/client/SplitManagerImplTest.java b/client/src/test/java/io/split/client/SplitManagerImplTest.java index 354ebcf5..5a5b70e4 100644 --- a/client/src/test/java/io/split/client/SplitManagerImplTest.java +++ b/client/src/test/java/io/split/client/SplitManagerImplTest.java @@ -14,6 +14,7 @@ import io.split.engine.experiments.SplitParser; import io.split.engine.matchers.AllKeysMatcher; import io.split.engine.matchers.CombiningMatcher; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.grammar.Treatments; import io.split.storages.SplitCacheConsumer; import io.split.telemetry.storage.InMemoryTelemetryStorage; @@ -71,7 +72,7 @@ public void splitCallWithExistentSplit() { prereq.featureFlagName = "feature1"; prereq.treatments = Lists.newArrayList("on"); ParsedSplit response = ParsedSplit.createParsedSplitForTests("FeatureName", 123, true, "off", Lists.newArrayList(getTestCondition("off")), "traffic", 456L, 1, new HashSet<>(), false, - Lists.newArrayList(prereq)); + new PrerequisitesMatcher(Lists.newArrayList(prereq))); when(splitCacheConsumer.get(existent)).thenReturn(response); SplitManagerImpl splitManager = new SplitManagerImpl(splitCacheConsumer, @@ -86,7 +87,7 @@ public void splitCallWithExistentSplit() { Assert.assertEquals("off", theOne.treatments.get(0)); Assert.assertEquals(0, theOne.configs.size()); Assert.assertEquals("off", theOne.defaultTreatment); - Assert.assertEquals(prereq, theOne.prerequisites.get(0)); + Assert.assertEquals(new PrerequisitesMatcher(Lists.newArrayList(prereq)).toString(), theOne.prerequisites); } @Test diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java index c12fdbd7..5cc6d01d 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java @@ -11,6 +11,7 @@ import io.split.engine.experiments.ParsedSplit; import io.split.engine.matchers.AttributeMatcher; import io.split.engine.matchers.CombiningMatcher; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.engine.matchers.RuleBasedSegmentMatcher; import io.split.engine.matchers.strings.EndsWithAnyOfMatcher; import io.split.engine.matchers.strings.WhitelistMatcher; @@ -200,11 +201,11 @@ private Evaluator buildEvaluatorAndLoadCache(boolean killed, int trafficAllocati List conditions = Lists.newArrayList(whitelistCondition, rollOutCondition); List conditionsForRBS = Lists.newArrayList(ruleBasedSegmentCondition, rollOutCondition); - ParsedSplit parsedSplit1 = new ParsedSplit("split_1", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366551, 100, 0, 2, null, new HashSet<>(), true, null); - ParsedSplit parsedSplit2 = new ParsedSplit("split_2", 0, true, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366552, 100, 0, 2, null, new HashSet<>(), true, null); - ParsedSplit parsedSplit3 = new ParsedSplit("split_3", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, null); - ParsedSplit parsedSplit4 = new ParsedSplit("split_test", 0, killed, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366555, trafficAllocation, 0, 2, null, new HashSet<>(), true, null); - ParsedSplit parsedSplit5 = new ParsedSplit("split_5", 0, false, DEFAULT_TREATMENT_VALUE, conditionsForRBS, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, null); + ParsedSplit parsedSplit1 = new ParsedSplit("split_1", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366551, 100, 0, 2, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); + ParsedSplit parsedSplit2 = new ParsedSplit("split_2", 0, true, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366552, 100, 0, 2, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); + ParsedSplit parsedSplit3 = new ParsedSplit("split_3", 0, false, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); + ParsedSplit parsedSplit4 = new ParsedSplit("split_test", 0, killed, DEFAULT_TREATMENT_VALUE, conditions, TRAFFIC_TYPE_VALUE, 223366555, trafficAllocation, 0, 2, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); + ParsedSplit parsedSplit5 = new ParsedSplit("split_5", 0, false, DEFAULT_TREATMENT_VALUE, conditionsForRBS, TRAFFIC_TYPE_VALUE, 223366554, 100, 0, 2, null, new HashSet<>(), true, new PrerequisitesMatcher(null)); splitCache.putMany(Stream.of(parsedSplit1, parsedSplit2, parsedSplit3, parsedSplit4, parsedSplit5).collect(Collectors.toList())); ParsedRuleBasedSegment parsedRuleBasedSegment = new ParsedRuleBasedSegment("sample_rule_based_segment", diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java index fe7b12f1..cf166bd2 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java @@ -7,6 +7,7 @@ import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; import io.split.engine.matchers.CombiningMatcher; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.storages.RuleBasedSegmentCacheConsumer; import io.split.storages.SegmentCacheConsumer; import io.split.storages.SplitCacheConsumer; @@ -70,7 +71,7 @@ public void evaluateWhenSplitNameDoesNotExistReturnControl() { @Test public void evaluateWhenSplitIsKilledReturnDefaultTreatment() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, null); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); @@ -82,7 +83,7 @@ public void evaluateWhenSplitIsKilledReturnDefaultTreatment() { @Test public void evaluateWithoutConditionsReturnDefaultTreatment() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, null); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); @@ -101,7 +102,7 @@ public void evaluateWithRollOutConditionBucketIsBiggerTrafficAllocationReturnDef ParsedCondition condition = new ParsedCondition(ConditionType.ROLLOUT, _matcher,_partitions, TEST_LABEL_VALUE); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 10, 12, 2, _configurations, new HashSet<>(), true, null); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 10, 12, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(MATCHING_KEY, BUCKETING_KEY, null, _evaluationContext)).thenReturn(true); @@ -122,7 +123,7 @@ public void evaluateWithRollOutConditionTrafficAllocationIsBiggerBucketReturnTre ParsedCondition condition = new ParsedCondition(ConditionType.ROLLOUT, _matcher, _partitions, TEST_LABEL_VALUE); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true); @@ -143,7 +144,7 @@ public void evaluateWithWhitelistConditionReturnTreatment() { ParsedCondition condition = new ParsedCondition(ConditionType.WHITELIST, _matcher, _partitions, "test whitelist label"); _conditions.add(condition); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true); @@ -157,7 +158,7 @@ public void evaluateWithWhitelistConditionReturnTreatment() { @Test public void evaluateWithSets() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, null); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, new PrerequisitesMatcher(null)); List sets = new ArrayList<>(Arrays.asList("set1", "empty_set")); Map> flagSets = new HashMap<>(); flagSets.put("set1", new HashSet<>(Arrays.asList(SPLIT_NAME))); @@ -178,7 +179,7 @@ public void evaluateWithSets() { @Test public void evaluateWithSetsNotHaveFlags() { - ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, null); + ParsedSplit split = ParsedSplit.createParsedSplitForTests(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 2, new HashSet<>(Arrays.asList("set1", "set2")), true, new PrerequisitesMatcher(null)); List sets = new ArrayList<>(Arrays.asList("set2")); Map> flagSets = new HashMap<>(); Mockito.when(_splitCacheConsumer.getNamesByFlagSets(sets)).thenReturn(flagSets); @@ -199,8 +200,8 @@ public void evaluateWithPrerequisites() { _conditions.add(condition); List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"" + TREATMENT_VALUE + "\"]}", Prerequisites.class)); - ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites); - ParsedSplit split1 = new ParsedSplit("split1", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null); + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(prerequisites)); + ParsedSplit split1 = new ParsedSplit("split1", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(null)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); Mockito.when(_splitCacheConsumer.get("split1")).thenReturn(split1); @@ -218,7 +219,7 @@ public void evaluateWithPrerequisites() { assertEquals(CHANGE_NUMBER, result.changeNumber); // if split is killed, label should be killed. - split = new ParsedSplit(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites); + split = new ParsedSplit(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, new PrerequisitesMatcher(prerequisites)); Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); assertEquals(DEFAULT_TREATMENT_VALUE, result.treatment); diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java index 9b41bf96..fb8db81f 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java @@ -11,11 +11,7 @@ import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; import io.split.client.dtos.Status; -import io.split.storages.SegmentCache; -import io.split.storages.memory.SegmentCacheInMemoryImpl; -import io.split.client.utils.Json; -import io.split.engine.evaluator.Labels; -import io.split.engine.ConditionsTestUtil; +import io.split.engine.matchers.PrerequisitesMatcher; import io.split.engine.matchers.AttributeMatcher; import io.split.engine.matchers.BetweenMatcher; import io.split.engine.matchers.CombiningMatcher; @@ -23,6 +19,11 @@ import io.split.engine.matchers.GreaterThanOrEqualToMatcher; import io.split.engine.matchers.LessThanOrEqualToMatcher; import io.split.engine.matchers.UserDefinedSegmentMatcher; +import io.split.storages.SegmentCache; +import io.split.storages.memory.SegmentCacheInMemoryImpl; +import io.split.client.utils.Json; +import io.split.engine.evaluator.Labels; +import io.split.engine.ConditionsTestUtil; import io.split.engine.matchers.collections.ContainsAllOfSetMatcher; import io.split.engine.matchers.collections.ContainsAnyOfSetMatcher; import io.split.engine.matchers.collections.EqualToSetMatcher; @@ -137,9 +138,23 @@ public void worksWithConfig() { List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, - listOfMatcherAndSplits, "user", 1, 1, configurations, new HashSet<>(), false, null); + listOfMatcherAndSplits, "user", 1, 1, configurations, new HashSet<>(), false, new PrerequisitesMatcher(null)); + + Assert.assertEquals(actual.parsedConditions(), expected.parsedConditions()); + Assert.assertEquals(actual.feature(), expected.feature()); + Assert.assertEquals(actual.changeNumber(), expected.changeNumber()); + Assert.assertEquals(actual.defaultTreatment(), expected.defaultTreatment()); + Assert.assertEquals(actual.killed(), expected.killed()); + Assert.assertEquals(actual.impressionsDisabled(), expected.impressionsDisabled()); + Assert.assertEquals(actual.flagSets(), null); + Assert.assertEquals(actual.algo(), expected.algo()); + Assert.assertEquals(actual.seed(), expected.seed()); + Assert.assertEquals(actual.trafficAllocation(), expected.trafficAllocation()); + Assert.assertEquals(actual.trafficAllocationSeed(), expected.trafficAllocationSeed()); + Assert.assertEquals(actual.getSegmentsNames(), expected.getSegmentsNames()); + Assert.assertEquals(actual.getRuleBasedSegmentsNames(), expected.getRuleBasedSegmentsNames()); + Assert.assertEquals(actual.prerequisites().toString(), expected.prerequisites().toString()); - Assert.assertEquals(actual, expected); Assert.assertEquals(actual.configurations().get("on"), configurations.get("on")); } @@ -715,6 +730,7 @@ private Split makeSplit(String name, int seed, List conditions, long split.changeNumber = changeNumber; split.algo = 1; split.configurations = configurations; + split.prerequisites = new ArrayList<>(); return split; } From f99c52752c5356da77f04e61fde1313020a6556e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 27 May 2025 14:55:47 -0700 Subject: [PATCH 07/12] Fixed split view --- client/src/main/java/io/split/client/api/SplitView.java | 4 ++-- .../java/io/split/engine/matchers/PrerequisitesMatcher.java | 2 ++ .../src/test/java/io/split/client/SplitManagerImplTest.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/io/split/client/api/SplitView.java b/client/src/main/java/io/split/client/api/SplitView.java index 9a70f08e..fb1e3f67 100644 --- a/client/src/main/java/io/split/client/api/SplitView.java +++ b/client/src/main/java/io/split/client/api/SplitView.java @@ -28,7 +28,7 @@ public class SplitView { public List sets; public String defaultTreatment; public boolean impressionsDisabled; - public String prerequisites; + public List prerequisites; public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { SplitView splitView = new SplitView(); @@ -50,7 +50,7 @@ public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { splitView.treatments = new ArrayList(treatments); splitView.configs = parsedSplit.configurations() == null? Collections.emptyMap() : parsedSplit.configurations() ; splitView.impressionsDisabled = parsedSplit.impressionsDisabled(); - splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites().toString(): ""; + splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites().getPrerequisites(): new ArrayList<>(); return splitView; } diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java index cbcff178..12278449 100644 --- a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -15,6 +15,8 @@ public PrerequisitesMatcher(List prerequisites) { _prerequisites = prerequisites; } + public List getPrerequisites() { return _prerequisites; } + @Override public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) { if (matchValue == null) { diff --git a/client/src/test/java/io/split/client/SplitManagerImplTest.java b/client/src/test/java/io/split/client/SplitManagerImplTest.java index 5a5b70e4..f3c04454 100644 --- a/client/src/test/java/io/split/client/SplitManagerImplTest.java +++ b/client/src/test/java/io/split/client/SplitManagerImplTest.java @@ -87,7 +87,7 @@ public void splitCallWithExistentSplit() { Assert.assertEquals("off", theOne.treatments.get(0)); Assert.assertEquals(0, theOne.configs.size()); Assert.assertEquals("off", theOne.defaultTreatment); - Assert.assertEquals(new PrerequisitesMatcher(Lists.newArrayList(prereq)).toString(), theOne.prerequisites); + Assert.assertEquals(Lists.newArrayList(prereq), theOne.prerequisites); } @Test From 6218697472aa98b3ee96528b9d965732ee4fac24 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 27 May 2025 16:55:43 -0700 Subject: [PATCH 08/12] polish --- .../java/io/split/client/api/SplitView.java | 3 ++- .../split/engine/evaluator/EvaluatorImp.java | 2 +- .../split/engine/experiments/ParsedSplit.java | 20 +++++++++---------- .../storages/memory/InMemoryCacheImp.java | 2 +- .../engine/experiments/SplitParserTest.java | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/client/src/main/java/io/split/client/api/SplitView.java b/client/src/main/java/io/split/client/api/SplitView.java index fb1e3f67..cc217fe1 100644 --- a/client/src/main/java/io/split/client/api/SplitView.java +++ b/client/src/main/java/io/split/client/api/SplitView.java @@ -50,7 +50,8 @@ public static SplitView fromParsedSplit(ParsedSplit parsedSplit) { splitView.treatments = new ArrayList(treatments); splitView.configs = parsedSplit.configurations() == null? Collections.emptyMap() : parsedSplit.configurations() ; splitView.impressionsDisabled = parsedSplit.impressionsDisabled(); - splitView.prerequisites = parsedSplit.prerequisites() != null ? parsedSplit.prerequisites().getPrerequisites(): new ArrayList<>(); + splitView.prerequisites = parsedSplit.prerequisitesMatcher() != null ? + parsedSplit.prerequisitesMatcher().getPrerequisites(): new ArrayList<>(); return splitView; } diff --git a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java index c56a7cb4..0b39148e 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -100,7 +100,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu String bk = (bucketingKey == null) ? matchingKey : bucketingKey; - if (!parsedSplit.prerequisites().match(matchingKey, bk, attributes, _evaluationContext)) { + if (!parsedSplit.prerequisitesMatcher().match(matchingKey, bk, attributes, _evaluationContext)) { return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), Labels.PREREQUISITES_NOT_MET, diff --git a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java index 8cb8fd6a..f5999b50 100644 --- a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java +++ b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java @@ -35,7 +35,7 @@ public class ParsedSplit { private final Map _configurations; private final HashSet _flagSets; private final boolean _impressionsDisabled; - private PrerequisitesMatcher _prerequisites; + private PrerequisitesMatcher _prerequisitesMatcher; public static ParsedSplit createParsedSplitForTests( String feature, @@ -48,7 +48,7 @@ public static ParsedSplit createParsedSplitForTests( int algo, HashSet flagSets, boolean impressionsDisabled, - PrerequisitesMatcher prerequisites + PrerequisitesMatcher prerequisitesMatcher ) { return new ParsedSplit( feature, @@ -64,7 +64,7 @@ public static ParsedSplit createParsedSplitForTests( null, flagSets, impressionsDisabled, - prerequisites + prerequisitesMatcher ); } @@ -80,7 +80,7 @@ public static ParsedSplit createParsedSplitForTests( Map configurations, HashSet flagSets, boolean impressionsDisabled, - PrerequisitesMatcher prerequisites + PrerequisitesMatcher prerequisitesMatcher ) { return new ParsedSplit( feature, @@ -96,7 +96,7 @@ public static ParsedSplit createParsedSplitForTests( configurations, flagSets, impressionsDisabled, - prerequisites + prerequisitesMatcher ); } @@ -114,7 +114,7 @@ public ParsedSplit( Map configurations, HashSet flagSets, boolean impressionsDisabled, - PrerequisitesMatcher prerequisites + PrerequisitesMatcher prerequisitesMatcher ) { _split = feature; _seed = seed; @@ -132,7 +132,7 @@ public ParsedSplit( _configurations = configurations; _flagSets = flagSets; _impressionsDisabled = impressionsDisabled; - _prerequisites = prerequisites; + _prerequisitesMatcher = prerequisitesMatcher; } public String feature() { @@ -179,7 +179,7 @@ public Map configurations() { public boolean impressionsDisabled() { return _impressionsDisabled; } - public PrerequisitesMatcher prerequisites() { return _prerequisites; } + public PrerequisitesMatcher prerequisitesMatcher() { return _prerequisitesMatcher; } @Override public int hashCode() { @@ -215,7 +215,7 @@ public boolean equals(Object obj) { && _algo == other._algo && _configurations == null ? other._configurations == null : _configurations.equals(other._configurations) && _impressionsDisabled == other._impressionsDisabled - && _prerequisites == other._prerequisites; + && _prerequisitesMatcher == other._prerequisitesMatcher; } @Override @@ -242,7 +242,7 @@ public String toString() { bldr.append(", impressionsDisabled:"); bldr.append(_impressionsDisabled); bldr.append(", prerequisites:"); - bldr.append(_prerequisites); + bldr.append(_prerequisitesMatcher); return bldr.toString(); diff --git a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java index b6544dc8..83e9f3b7 100644 --- a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java +++ b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java @@ -132,7 +132,7 @@ public void kill(String splitName, String defaultTreatment, long changeNumber) { parsedSplit.configurations(), parsedSplit.flagSets(), parsedSplit.impressionsDisabled(), - parsedSplit.prerequisites() + parsedSplit.prerequisitesMatcher() ); _concurrentMap.put(splitName, updatedSplit); diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java index fb8db81f..a60d71c0 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java @@ -153,7 +153,7 @@ public void worksWithConfig() { Assert.assertEquals(actual.trafficAllocationSeed(), expected.trafficAllocationSeed()); Assert.assertEquals(actual.getSegmentsNames(), expected.getSegmentsNames()); Assert.assertEquals(actual.getRuleBasedSegmentsNames(), expected.getRuleBasedSegmentsNames()); - Assert.assertEquals(actual.prerequisites().toString(), expected.prerequisites().toString()); + Assert.assertEquals(actual.prerequisitesMatcher().toString(), expected.prerequisitesMatcher().toString()); Assert.assertEquals(actual.configurations().get("on"), configurations.get("on")); } From 3fe5be3b988233d9dfc99ffd6c45c5b9c582ebe9 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 28 May 2025 09:43:59 -0700 Subject: [PATCH 09/12] Added integration test --- .../client/SplitClientIntegrationTest.java | 74 +++++ client/src/test/resources/splits_prereq.json | 293 ++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 client/src/test/resources/splits_prereq.json diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index 82f65602..f034e996 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -1101,6 +1101,80 @@ public MockResponse dispatch(RecordedRequest request) { Assert.assertTrue(check2); } + @Test + public void getTreatmentWithPrerequisites() throws Exception { + String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/splits_prereq.json")), StandardCharsets.UTF_8); + List allRequests = new ArrayList<>(); + Dispatcher dispatcher = new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + allRequests.add(request); + switch (request.getPath()) { + case "/api/splitChanges?s=1.3&since=-1&rbSince=-1": + return new MockResponse().setResponseCode(200).setBody(splits); + case "/api/splitChanges?s=1.3&since=1585948850109&rbSince=1585948850109": + return new MockResponse().setResponseCode(200).setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109},\"rbs\":{\"d\":[],\"t\":1585948850109,\"s\":1585948850109}}"); + case "/api/segmentChanges/segment-test?since=-1": + return new MockResponse().setResponseCode(200).setBody("{\"name\":\"segment-test\",\"added\":[\"user-1\"],\"removed\":[],\"since\":-1,\"till\":-1}"); + case "/api/testImpressions/bulk": + return new MockResponse().setResponseCode(200); + case "/api/testImpressions/count": + return new MockResponse().setResponseCode(200); + case "/v1/keys/ss": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/usage": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/config": + return new MockResponse().setResponseCode(200); + } + return new MockResponse().setResponseCode(404); + } + }; + + MockWebServer splitServer = new MockWebServer(); + splitServer.setDispatcher(dispatcher); + splitServer.start(); + String serverURL = String.format("http://%s:%s", splitServer.getHostName(), splitServer.getPort()); + + SplitClientConfig config = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .endpoint(serverURL, serverURL) + .telemetryURL(serverURL + "/v1") + .authServiceURL(String.format("%s/api/auth/enabled", serverURL)) + .streamingEnabled(false) + .featuresRefreshRate(5) + .impressionsMode(ImpressionsManager.Mode.DEBUG) + .build(); + + SplitFactory factory = SplitFactoryBuilder.build("fake-api-token", config); + SplitClient client = factory.client(); + client.blockUntilReady(); + + Assert.assertEquals("on", client.getTreatment("bilal@split.io", "test_prereq", new HashMap() {{ + put("email", "bilal@@split.io"); + }})); + Assert.assertEquals("def_treatment", client.getTreatment("bilal@split.io", "test_prereq")); + Assert.assertEquals("def_treatment", client.getTreatment("mauro@split.io", "test_prereq", new HashMap() {{ + put("email", "mauro@@split.io"); + }})); + Assert.assertEquals("on", client.getTreatment("pato@split.io", "test_prereq", new HashMap() {{ + put("email", "pato@@split.io"); + }})); + + Assert.assertEquals("on_whitelist", client.getTreatment("bilal@split.io", "prereq_chain", new HashMap() {{ + put("email", "bilal@@split.io"); + }})); + Assert.assertEquals("on", client.getTreatment("pato@split.io", "prereq_chain", new HashMap() {{ + put("email", "pato@@split.io"); + }})); + Assert.assertEquals("on_default", client.getTreatment("mauro@split.io", "prereq_chain", new HashMap() {{ + put("email", "mauro@@split.io"); + }})); + + client.destroy(); + splitServer.shutdown(); + } + private SSEMockServer buildSSEMockServer(SSEMockServer.SseEventQueue eventQueue) { return new SSEMockServer(eventQueue, (token, version, channel) -> { if (!"1.1".equals(version)) { diff --git a/client/src/test/resources/splits_prereq.json b/client/src/test/resources/splits_prereq.json new file mode 100644 index 00000000..5efa7fed --- /dev/null +++ b/client/src/test/resources/splits_prereq.json @@ -0,0 +1,293 @@ +{"ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "test_prereq", + "prerequisites": [ + { "n": "feature_segment", "ts": ["off", "def_test"] }, + { "n": "rbs_flag", "ts": ["on"] } + ], + "trafficAllocation": 100, + "trafficAllocationSeed": 1582960494, + "seed": 1842944006, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "def_treatment", + "changeNumber": 1582741588594, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + }, + { + "name":"feature_segment", + "trafficTypeId":"u", + "trafficTypeName":"User", + "trafficAllocation": 100, + "trafficAllocationSeed": 1582960494, + "seed":-1177551240, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"def_test", + "changeNumber": 1582741588594, + "algo": 2, + "configurations": {}, + "conditions":[ + { + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "matcherType":"IN_SEGMENT", + "negate":false, + "userDefinedSegmentMatcherData":{ + "segmentName":"segment-test" + }, + "whitelistMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + }, + { + "treatment":"off", + "size":0 + } + ], + "label": "default label" + } + ] + }, + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_flag", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in rule based segment sample_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + }, + { + "trafficTypeName": "user", + "name": "prereq_chain", + "prerequisites": [ + { "n": "test_prereq", "ts": ["on"] } + ], + "trafficAllocation": 100, + "trafficAllocationSeed": -2092979940, + "seed": 105482719, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on_default", + "changeNumber": 1585948850109, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "bilal@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on_whitelist", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V1", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1585948850109 +}, "rbs":{"d": [ + { + "changeNumber": 5, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + }], "s": -1, "t": 1585948850109} +} From 553c1f0de85ebc181cca0625d22912296277f2f7 Mon Sep 17 00:00:00 2001 From: Mauro Antonio Sanz Date: Wed, 28 May 2025 13:52:20 -0300 Subject: [PATCH 10/12] update httpclient version --- client/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pom.xml b/client/pom.xml index d6910b66..e2a79eec 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -168,7 +168,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.3 + 5.4.4 com.google.code.gson From dca8fde7ab94c001d62fe5af10e73ff6cb2f052b Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 28 May 2025 11:42:53 -0700 Subject: [PATCH 11/12] polish --- .../split/engine/evaluator/EvaluatorImp.java | 20 ++++--- .../split/engine/experiments/ParsedSplit.java | 6 ++- .../engine/experiments/SplitParserTest.java | 53 ++++++++++++------- .../UserCustomSplitAdapterConsumerTest.java | 18 ++++++- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java index 1f14f359..496441b6 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -86,7 +86,7 @@ private List getFeatureFlagNamesByFlagSets(List flagSets) { private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bucketingKey, ParsedSplit parsedSplit, Map attributes) throws ChangeNumberExceptionWrapper { try { - String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; + String config = getConfig(parsedSplit, parsedSplit.defaultTreatment()); if (parsedSplit.killed()) { return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), @@ -96,7 +96,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu parsedSplit.impressionsDisabled()); } - String bk = (bucketingKey == null) ? matchingKey : bucketingKey; + String bk = getBucketingKey(bucketingKey, matchingKey); if (!parsedSplit.prerequisitesMatcher().match(matchingKey, bk, attributes, _evaluationContext)) { return new TreatmentLabelAndChangeNumber( @@ -125,8 +125,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu if (bucket > parsedSplit.trafficAllocation()) { // out of split - config = parsedSplit.configurations() != null ? - parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; + config = getConfig(parsedSplit, parsedSplit.defaultTreatment()); return new TreatmentLabelAndChangeNumber(parsedSplit.defaultTreatment(), Labels.NOT_IN_SPLIT, parsedSplit.changeNumber(), config, parsedSplit.impressionsDisabled()); } @@ -137,7 +136,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu if (parsedCondition.matcher().match(matchingKey, bucketingKey, attributes, _evaluationContext)) { String treatment = Splitter.getTreatment(bk, parsedSplit.seed(), parsedCondition.partitions(), parsedSplit.algo()); - config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(treatment) : null; + config = getConfig(parsedSplit, treatment); return new TreatmentLabelAndChangeNumber( treatment, parsedCondition.label(), @@ -147,7 +146,8 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu } } - config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null; + config = getConfig(parsedSplit, parsedSplit.defaultTreatment()); + return new TreatmentLabelAndChangeNumber( parsedSplit.defaultTreatment(), Labels.DEFAULT_RULE, @@ -159,6 +159,14 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu } } + private String getBucketingKey(String bucketingKey, String matchingKey) { + return (bucketingKey == null) ? matchingKey : bucketingKey; + } + + private String getConfig(ParsedSplit parsedSplit, String returnedTreatment) { + return parsedSplit.configurations() != null ? parsedSplit.configurations().get(returnedTreatment) : null; + } + private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, String bucketingKey, Map attributes, ParsedSplit parsedSplit) { try { diff --git a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java index f5999b50..e202474f 100644 --- a/client/src/main/java/io/split/engine/experiments/ParsedSplit.java +++ b/client/src/main/java/io/split/engine/experiments/ParsedSplit.java @@ -204,16 +204,18 @@ public boolean equals(Object obj) { if (!(obj instanceof ParsedSplit)) return false; ParsedSplit other = (ParsedSplit) obj; + boolean trafficTypeCond = _trafficTypeName == null ? other._trafficTypeName == null : _trafficTypeName.equals(other._trafficTypeName); + boolean configCond = _configurations == null ? other._configurations == null : _configurations.equals(other._configurations); return _split.equals(other._split) && _seed == other._seed && _killed == other._killed && _defaultTreatment.equals(other._defaultTreatment) && _parsedCondition.equals(other._parsedCondition) - && _trafficTypeName == null ? other._trafficTypeName == null : _trafficTypeName.equals(other._trafficTypeName) + && trafficTypeCond && _changeNumber == other._changeNumber && _algo == other._algo - && _configurations == null ? other._configurations == null : _configurations.equals(other._configurations) + && configCond && _impressionsDisabled == other._impressionsDisabled && _prerequisitesMatcher == other._prerequisitesMatcher; } diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java index a60d71c0..e65cfc14 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java @@ -95,9 +95,9 @@ public void works() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); assertTrue(expected.hashCode() != 0); assertTrue(expected.equals(expected)); } @@ -146,7 +146,7 @@ public void worksWithConfig() { Assert.assertEquals(actual.defaultTreatment(), expected.defaultTreatment()); Assert.assertEquals(actual.killed(), expected.killed()); Assert.assertEquals(actual.impressionsDisabled(), expected.impressionsDisabled()); - Assert.assertEquals(actual.flagSets(), null); + Assert.assertEquals(null, actual.flagSets()); Assert.assertEquals(actual.algo(), expected.algo()); Assert.assertEquals(actual.seed(), expected.seed()); Assert.assertEquals(actual.trafficAllocation(), expected.trafficAllocation()); @@ -188,12 +188,12 @@ public void worksForTwoConditions() { ParsedSplit actual = parser.parse(split); ParsedCondition parsedCondition1 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(EMPLOYEES)), fullyRollout); - ParsedCondition parsedCondition2 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(EMPLOYEES)), turnOff); + ParsedCondition parsedCondition2 = ParsedCondition.createParsedConditionForTests(CombiningMatcher.of(new UserDefinedSegmentMatcher(SALES_PEOPLE)), turnOff); List listOfParsedConditions = Lists.newArrayList(parsedCondition1, parsedCondition2); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfParsedConditions, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfParsedConditions, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -260,9 +260,9 @@ public void worksWithAttributes() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -293,9 +293,9 @@ public void lessThanOrEqualTo() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -325,9 +325,9 @@ public void equalTo() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -356,9 +356,9 @@ public void equalToNegativeNumber() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -392,9 +392,9 @@ public void between() { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } @Test @@ -708,11 +708,28 @@ public void setMatcherTest(Condition c, io.split.engine.matchers.Matcher m) { ParsedCondition parsedCondition = ParsedCondition.createParsedConditionForTests(combiningMatcher, partitions); List listOfMatcherAndSplits = Lists.newArrayList(parsedCondition); - ParsedSplit expected = ParsedSplit.createParsedSplitForTests("splitName", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>(), false, null); + ParsedSplit expected = ParsedSplit.createParsedSplitForTests("splitName", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, null, false, new PrerequisitesMatcher(null)); - Assert.assertEquals(actual, expected); + compareParsed(actual, expected); } + private void compareParsed(ParsedSplit actual, ParsedSplit expected) { + Assert.assertEquals(actual.getRuleBasedSegmentsNames(), expected.getRuleBasedSegmentsNames()); + Assert.assertEquals(actual.seed(), expected.seed()); + Assert.assertEquals(actual.algo(), expected.algo()); + Assert.assertEquals(actual.trafficAllocationSeed(), expected.trafficAllocationSeed()); + Assert.assertEquals(actual.flagSets(), expected.flagSets()); + Assert.assertEquals(actual.parsedConditions(), expected.parsedConditions()); + Assert.assertEquals(actual.trafficAllocation(), expected.trafficAllocation()); + Assert.assertEquals(actual.getSegmentsNames(), expected.getSegmentsNames()); + Assert.assertEquals(actual.impressionsDisabled(), expected.impressionsDisabled()); + Assert.assertEquals(actual.killed(), expected.killed()); + Assert.assertEquals(actual.defaultTreatment(), expected.defaultTreatment()); + Assert.assertEquals(actual.changeNumber(), expected.changeNumber()); + Assert.assertEquals(actual.feature(), expected.feature()); + Assert.assertEquals(actual.configurations(), expected.configurations()); + Assert.assertEquals(actual.prerequisitesMatcher().toString(), expected.prerequisitesMatcher().toString()); + } private Split makeSplit(String name, int seed, List conditions, long changeNumber) { return makeSplit(name, seed, conditions, changeNumber, null); } diff --git a/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java b/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java index 26114711..d12badc0 100644 --- a/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java +++ b/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java @@ -76,9 +76,23 @@ public void testGetSplit() { SplitParser splitParser = new SplitParser(); Split split = getSplit(SPLIT_NAME); Mockito.when(_userStorageWrapper.get(PrefixAdapter.buildSplitKey(SPLIT_NAME))).thenReturn(getSplitAsJson(split)); - ParsedSplit result = _userCustomSplitAdapterConsumer.get(SPLIT_NAME); + ParsedSplit actual = _userCustomSplitAdapterConsumer.get(SPLIT_NAME); ParsedSplit expected = splitParser.parse(split); - Assert.assertEquals(expected, result); + Assert.assertEquals(actual.getRuleBasedSegmentsNames(), expected.getRuleBasedSegmentsNames()); + Assert.assertEquals(actual.seed(), expected.seed()); + Assert.assertEquals(actual.algo(), expected.algo()); + Assert.assertEquals(actual.trafficAllocationSeed(), expected.trafficAllocationSeed()); + Assert.assertEquals(actual.flagSets(), expected.flagSets()); + Assert.assertEquals(actual.parsedConditions(), expected.parsedConditions()); + Assert.assertEquals(actual.trafficAllocation(), expected.trafficAllocation()); + Assert.assertEquals(actual.getSegmentsNames(), expected.getSegmentsNames()); + Assert.assertEquals(actual.impressionsDisabled(), expected.impressionsDisabled()); + Assert.assertEquals(actual.killed(), expected.killed()); + Assert.assertEquals(actual.defaultTreatment(), expected.defaultTreatment()); + Assert.assertEquals(actual.changeNumber(), expected.changeNumber()); + Assert.assertEquals(actual.feature(), expected.feature()); + Assert.assertEquals(actual.configurations(), expected.configurations()); + Assert.assertEquals(actual.prerequisitesMatcher().toString(), expected.prerequisitesMatcher().toString()); } @Test From ba2df25512fa1ea4e5c23158d9d849510728ff8e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 28 May 2025 12:12:29 -0700 Subject: [PATCH 12/12] polish --- .../main/java/io/split/engine/evaluator/EvaluatorImp.java | 6 +++++- .../java/io/split/client/SplitClientIntegrationTest.java | 2 +- .../java/io/split/engine/experiments/SplitParserTest.java | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java index 496441b6..6d31952c 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -117,7 +117,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) { - if (!inRollout && parsedCondition.conditionType() == ConditionType.ROLLOUT) { + if (checkRollout(inRollout, parsedCondition)) { if (parsedSplit.trafficAllocation() < 100) { // if the traffic allocation is 100%, no need to do anything special. @@ -159,6 +159,10 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu } } + private boolean checkRollout(boolean inRollout, ParsedCondition parsedCondition) { + return (!inRollout && parsedCondition.conditionType() == ConditionType.ROLLOUT); + } + private String getBucketingKey(String bucketingKey, String matchingKey) { return (bucketingKey == null) ? matchingKey : bucketingKey; } diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index f034e996..bba82452 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -528,7 +528,7 @@ public void splitClientMultiFactory() throws Exception { .until(() -> "on_whitelist".equals(client2.getTreatment("admin", "push_test"))); Awaitility.await() - .atMost(50L, TimeUnit.SECONDS) + .atMost(100L, TimeUnit.SECONDS) .until(() -> "on_whitelist".equals(client3.getTreatment("admin", "push_test"))); Awaitility.await() diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java index e65cfc14..4676a8c3 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java @@ -100,6 +100,7 @@ public void works() { compareParsed(actual, expected); assertTrue(expected.hashCode() != 0); assertTrue(expected.equals(expected)); + Assert.assertEquals(expected.toString(), actual.toString()); } @Test @@ -730,6 +731,7 @@ private void compareParsed(ParsedSplit actual, ParsedSplit expected) { Assert.assertEquals(actual.configurations(), expected.configurations()); Assert.assertEquals(actual.prerequisitesMatcher().toString(), expected.prerequisitesMatcher().toString()); } + private Split makeSplit(String name, int seed, List conditions, long changeNumber) { return makeSplit(name, seed, conditions, changeNumber, null); }