diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index cca655612..bcc1a9679 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -257,7 +257,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn config.getThreadFactory()); // Evaluator - _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache); + _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, null); // SplitClient _client = new SplitClientImpl(this, @@ -348,7 +348,8 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor _telemetrySynchronizer = new TelemetryConsumerSubmitter(customStorageWrapper, _sdkMetadata); UserCustomRuleBasedSegmentAdapterConsumer userCustomRuleBasedSegmentAdapterConsumer = new UserCustomRuleBasedSegmentAdapterConsumer(customStorageWrapper); - _evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer, userCustomRuleBasedSegmentAdapterConsumer); + _evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer, + userCustomRuleBasedSegmentAdapterConsumer, null); _impressionsSender = PluggableImpressionSender.create(customStorageWrapper); _uniqueKeysTracker = createUniqueKeysTracker(config); _impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer, @@ -446,7 +447,7 @@ protected SplitFactoryImpl(SplitClientConfig config) { _impressionsManager, null, null, null); // Evaluator - _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache); + _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, null); EventsStorage eventsStorage = new NoopEventsStorageImp(); diff --git a/client/src/main/java/io/split/client/dtos/FallbackTreatment.java b/client/src/main/java/io/split/client/dtos/FallbackTreatment.java index c4f406e4a..542f90157 100644 --- a/client/src/main/java/io/split/client/dtos/FallbackTreatment.java +++ b/client/src/main/java/io/split/client/dtos/FallbackTreatment.java @@ -10,13 +10,19 @@ public class FallbackTreatment { public FallbackTreatment(String treatment, Map config) { _treatment = treatment; _config = config; - _label = "fallback - "; + _label = null; } public FallbackTreatment(String treatment) { _treatment = treatment; _config = null; - _label = "fallback - "; + _label = null; + } + + public FallbackTreatment(String treatment, Map config, String label) { + _treatment = treatment; + _config = config; + _label = label; } public Map getConfig() { diff --git a/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculator.java b/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculator.java new file mode 100644 index 000000000..b172a1cb2 --- /dev/null +++ b/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculator.java @@ -0,0 +1,6 @@ +package io.split.client.dtos; + +public interface FallbackTreatmentCalculator +{ + FallbackTreatment resolve(String flagName, String label); +} diff --git a/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculatorImp.java b/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculatorImp.java new file mode 100644 index 000000000..c9854d320 --- /dev/null +++ b/client/src/main/java/io/split/client/dtos/FallbackTreatmentCalculatorImp.java @@ -0,0 +1,40 @@ +package io.split.client.dtos; + +import io.split.grammar.Treatments; + +public class FallbackTreatmentCalculatorImp implements FallbackTreatmentCalculator +{ + private final FallbackTreatmentsConfiguration _fallbackTreatmentsConfiguration; + private final String labelPrefix = "fallback - "; + + public FallbackTreatmentCalculatorImp(FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration) { + _fallbackTreatmentsConfiguration = fallbackTreatmentsConfiguration; + } + + public FallbackTreatment resolve(String flagName, String label) { + if (_fallbackTreatmentsConfiguration != null) { + if (_fallbackTreatmentsConfiguration.getByFlagFallbackTreatment() != null + && _fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get(flagName) != null) { + return copyWithLabel(_fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get(flagName), + resolveLabel(label)); + } + if (_fallbackTreatmentsConfiguration.getGlobalFallbackTreatment() != null) { + return copyWithLabel(_fallbackTreatmentsConfiguration.getGlobalFallbackTreatment(), + resolveLabel(label)); + } + } + + return new FallbackTreatment(Treatments.CONTROL, null, label); + } + + private String resolveLabel(String label) { + if (label == null) { + return null; + } + return labelPrefix + label; + } + + private FallbackTreatment copyWithLabel(FallbackTreatment fallbackTreatment, String label) { + return new FallbackTreatment(fallbackTreatment.getTreatment(), fallbackTreatment.getConfig(), label); + } +} 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 6d31952c3..773413b2b 100644 --- a/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java +++ b/client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java @@ -1,6 +1,9 @@ package io.split.engine.evaluator; import io.split.client.dtos.ConditionType; +import io.split.client.dtos.FallbackTreatment; +import io.split.client.dtos.FallbackTreatmentCalculator; +import io.split.client.dtos.FallbackTreatmentsConfiguration; import io.split.client.exceptions.ChangeNumberExceptionWrapper; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; @@ -26,19 +29,22 @@ public class EvaluatorImp implements Evaluator { private final SegmentCacheConsumer _segmentCacheConsumer; private final EvaluationContext _evaluationContext; private final SplitCacheConsumer _splitCacheConsumer; + private final FallbackTreatmentCalculator _fallbackTreatmentCalculator; public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache, - RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) { + RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer, + FallbackTreatmentCalculator fallbackTreatmentCalculator) { _splitCacheConsumer = checkNotNull(splitCacheConsumer); _segmentCacheConsumer = checkNotNull(segmentCache); _evaluationContext = new EvaluationContext(this, _segmentCacheConsumer, ruleBasedSegmentCacheConsumer); + _fallbackTreatmentCalculator = fallbackTreatmentCalculator; } @Override public TreatmentLabelAndChangeNumber evaluateFeature(String matchingKey, String bucketingKey, String featureFlag, Map attributes) { ParsedSplit parsedSplit = _splitCacheConsumer.get(featureFlag); - return evaluateParsedSplit(matchingKey, bucketingKey, attributes, parsedSplit); + return evaluateParsedSplit(matchingKey, bucketingKey, attributes, parsedSplit, featureFlag); } @Override @@ -49,7 +55,7 @@ public Map evaluateFeatures(String matchi if (parsedSplits == null) { return results; } - featureFlags.forEach(s -> results.put(s, evaluateParsedSplit(matchingKey, bucketingKey, attributes, parsedSplits.get(s)))); + featureFlags.forEach(s -> results.put(s, evaluateParsedSplit(matchingKey, bucketingKey, attributes, parsedSplits.get(s), s))); return results; } @@ -172,18 +178,22 @@ private String getConfig(ParsedSplit parsedSplit, String returnedTreatment) { } private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, String bucketingKey, Map attributes, - ParsedSplit parsedSplit) { + ParsedSplit parsedSplit, String featureName) { try { + if (parsedSplit == null) { - return new TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.DEFINITION_NOT_FOUND); + FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, Labels.DEFINITION_NOT_FOUND); + return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(), fallbackTreatment.getLabel()); } return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes); } catch (ChangeNumberExceptionWrapper e) { _log.error("Evaluator Exception", e.wrappedException()); - return new EvaluatorImp.TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.EXCEPTION, e.changeNumber()); + FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, Labels.EXCEPTION); + return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(), fallbackTreatment.getLabel(), e.changeNumber()); } catch (Exception e) { _log.error("Evaluator Exception", e); - return new EvaluatorImp.TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.EXCEPTION); + FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, Labels.EXCEPTION); + return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(), fallbackTreatment.getLabel()); } } diff --git a/client/src/test/java/io/split/client/SplitClientImplTest.java b/client/src/test/java/io/split/client/SplitClientImplTest.java index 5b56708d9..a5b55ed78 100644 --- a/client/src/test/java/io/split/client/SplitClientImplTest.java +++ b/client/src/test/java/io/split/client/SplitClientImplTest.java @@ -99,7 +99,7 @@ public void nullKeyResultsInControl() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertEquals(Treatments.CONTROL, client.getTreatment(null, "test1")); @@ -129,7 +129,7 @@ public void nullTestResultsInControl() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertEquals(Treatments.CONTROL, client.getTreatment("adil@relateiq.com", null)); @@ -152,7 +152,7 @@ public void exceptionsResultInControl() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertEquals(Treatments.CONTROL, client.getTreatment("adil@relateiq.com", "test1")); @@ -184,7 +184,7 @@ public void works() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -222,7 +222,7 @@ public void worksNullConfig() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); String randomKey = RandomStringUtils.random(10); @@ -258,7 +258,7 @@ public void worksAndHasConfig() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -295,7 +295,7 @@ public void lastConditionIsAlwaysDefault() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -335,7 +335,7 @@ public void lastConditionIsAlwaysDefaultButWithTreatment() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -371,7 +371,7 @@ public void multipleConditionsWork() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -405,7 +405,7 @@ public void killedTestAlwaysGoesToDefault() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -445,7 +445,7 @@ public void killedTestAlwaysGoesToDefaultHasConfig() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -483,7 +483,7 @@ public void dependencyMatcherOn() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -518,7 +518,7 @@ public void dependencyMatcherOff() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -547,7 +547,7 @@ public void dependencyMatcherControl() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -577,7 +577,7 @@ public void attributesWork() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -612,7 +612,7 @@ public void attributesWork2() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -648,7 +648,7 @@ public void attributesGreaterThanNegativeNumber() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -686,7 +686,7 @@ public void attributesForSets() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer ,segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer ,segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -731,7 +731,7 @@ public void labelsArePopulated() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -834,7 +834,7 @@ private void trafficAllocation(String key, int trafficAllocation, int trafficAll NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -888,7 +888,7 @@ public void notInTrafficAllocationDefaultConfig() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -932,7 +932,7 @@ public void matchingBucketingKeysWork() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -975,7 +975,7 @@ public void matchingBucketingKeysByFlagSetWork() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1016,7 +1016,7 @@ public void matchingBucketingKeysByFlagSetsWork() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1054,7 +1054,7 @@ public void impressionMetadataIsPropagated() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1097,7 +1097,7 @@ public void blockUntilReadyDoesNotTimeWhenSdkIsReady() throws TimeoutException, NoopEventsStorageImp.create(), config, ready, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1119,7 +1119,7 @@ public void blockUntilReadyTimesWhenSdkIsNotReady() throws TimeoutException, Int NoopEventsStorageImp.create(), config, ready, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1140,7 +1140,7 @@ public void trackWithValidParameters() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1166,7 +1166,7 @@ public void trackWithInvalidEventTypeIds() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Assert.assertFalse(client.track("validKey", "valid_traffic_type", "")); @@ -1191,7 +1191,7 @@ public void trackWithInvalidTrafficTypeNames() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1213,7 +1213,7 @@ public void trackWithInvalidKeys() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1245,7 +1245,7 @@ public void getTreatmentWithInvalidKeys() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Assert.assertNotEquals(Treatments.CONTROL, client.getTreatment("valid", "split")); @@ -1296,7 +1296,7 @@ public void trackWithProperties() { eventClientMock, config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1420,7 +1420,7 @@ public void clientCannotPerformActionsWhenDestroyed() throws InterruptedExceptio NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1461,7 +1461,7 @@ public void worksAndHasConfigTryKetTreatmentWithKey() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1512,7 +1512,7 @@ public void worksAndHasConfigByFlagSetTryKetTreatmentWithKey() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1561,7 +1561,7 @@ public void worksAndHasConfigByFlagSetsTryKetTreatmentWithKey() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1599,7 +1599,7 @@ public void blockUntilReadyException() throws TimeoutException, InterruptedExcep NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1629,7 +1629,7 @@ public void nullKeyResultsInControlGetTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertEquals(Treatments.CONTROL, client.getTreatments(null, Collections.singletonList("test1")).get("test1")); @@ -1660,7 +1660,7 @@ public void nullSplitsResultsInEmptyGetTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertEquals(0, client.getTreatments("key", null).size()); @@ -1683,7 +1683,7 @@ public void exceptionsResultInControlGetTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map result = client.getTreatments("adil@relateiq.com", Arrays.asList("test1", "test2")); @@ -1717,7 +1717,7 @@ public void getTreatmentsWorks() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map result = client.getTreatments("randomKey", Arrays.asList(test, "test2")); @@ -1748,7 +1748,7 @@ public void emptySplitsResultsInNullGetTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map result = client.getTreatments("key", new ArrayList<>()); @@ -1773,7 +1773,7 @@ public void exceptionsResultInControlTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map result = client.getTreatments("adil@relateiq.com", Arrays.asList("test1")); @@ -1811,7 +1811,7 @@ public void worksTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map result = client.getTreatments("anyKey", Arrays.asList(test, test2)); @@ -1849,7 +1849,7 @@ public void worksOneControlTreatments() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1894,7 +1894,7 @@ public void treatmentsWorksAndHasConfig() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map attributes = new HashMap<>(); @@ -1937,7 +1937,7 @@ public void testTreatmentsByFlagSet() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); @@ -1978,7 +1978,7 @@ public void testTreatmentsByFlagSetInvalid() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); assertTrue(client.getTreatmentsByFlagSet(RandomStringUtils.random(10), "", new HashMap<>()).isEmpty()); @@ -2022,7 +2022,7 @@ public void testTreatmentsByFlagSets() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); int numKeys = 5; @@ -2078,7 +2078,7 @@ public void treatmentsWorksAndHasConfigFlagSet() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map attributes = new HashMap<>(); @@ -2135,7 +2135,7 @@ public void treatmentsWorksAndHasConfigFlagSets() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, flagSetsFilter ); Map attributes = new HashMap<>(); @@ -2181,7 +2181,7 @@ public void impressionPropertiesTest() { NoopEventsStorageImp.create(), config, gates, - new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE, + new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer, ruleBasedSegmentCacheConsumer, null), TELEMETRY_STORAGE, TELEMETRY_STORAGE, new FlagSetsFilterImpl(new HashSet<>()) ); Map attributes = ImmutableMap.of("age", -20, "acv", "1000000"); diff --git a/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java b/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java new file mode 100644 index 000000000..4e082e007 --- /dev/null +++ b/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java @@ -0,0 +1,40 @@ +package io.split.client.dtos; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class FallbackTreatmentCalculationImpTest { + + @Test + public void TestWorks() { + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), null); + FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + assertEquals("on", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); + assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("anyflag", "exception").getLabel()); + + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), + new HashMap() {{ put("flag", new FallbackTreatment("off")); }} ); + fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + assertEquals("on", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); + assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("anyflag", "exception").getLabel()); + assertEquals("off", fallbackTreatmentCalculator.resolve("flag", "exception").getTreatment()); + assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("flag", "exception").getLabel()); + + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, + new HashMap() {{ put("flag", new FallbackTreatment("off")); }} ); + fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + assertEquals("control", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); + assertEquals("exception", fallbackTreatmentCalculator.resolve("anyflag", "exception").getLabel()); + assertEquals("off", fallbackTreatmentCalculator.resolve("flag", "exception").getTreatment()); + assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("flag", "exception").getLabel()); + } +} 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 5cc6d01d9..e686f5308 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorIntegrationTest.java @@ -174,7 +174,7 @@ private Evaluator buildEvaluatorAndLoadCache(boolean killed, int trafficAllocati SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter); SegmentCache segmentCache = new SegmentCacheInMemoryImpl(); RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp(); - Evaluator evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache); + Evaluator evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, null); Partition partition = new Partition(); partition.treatment = ON_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 cf166bd2b..9ef7343aa 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java @@ -1,8 +1,6 @@ package io.split.engine.evaluator; -import io.split.client.dtos.ConditionType; -import io.split.client.dtos.Partition; -import io.split.client.dtos.Prerequisites; +import io.split.client.dtos.*; import io.split.client.utils.Json; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedSplit; @@ -50,7 +48,7 @@ public void before() { _splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class); _segmentCacheConsumer = Mockito.mock(SegmentCacheConsumer.class); _ruleBasedSegmentCacheConsumer = Mockito.mock(RuleBasedSegmentCacheConsumer.class); - _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer); + _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, null); _matcher = Mockito.mock(CombiningMatcher.class); _evaluationContext = Mockito.mock(EvaluationContext.class); @@ -226,4 +224,76 @@ public void evaluateWithPrerequisites() { assertEquals(Labels.KILLED, result.label); assertEquals(CHANGE_NUMBER, result.changeNumber); } + + @Test + public void evaluateFallbackTreatmentWorks() { + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(null); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), null); + FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, fallbackTreatmentCalculator); + + EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("on", result.treatment); + assertEquals("fallback - definition not found", result.label); + + ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, null, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), false, null); + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("on", result.treatment); + assertEquals("fallback - exception", result.label); + + // using byflag only + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(null); + Mockito.when(_splitCacheConsumer.get("another_name")).thenReturn(null); + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, new HashMap() {{ put(SPLIT_NAME, new FallbackTreatment("off")); }} ); + fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, fallbackTreatmentCalculator); + + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("off", result.treatment); + assertEquals("fallback - definition not found", result.label); + + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, "another_name", null); + assertEquals("control", result.treatment); + assertEquals("definition not found", result.label); + + split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, null, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), false, null); + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("off", result.treatment); + assertEquals("fallback - exception", result.label); + + split = new ParsedSplit("another_name", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, null, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), false, null); + Mockito.when(_splitCacheConsumer.get("another_name")).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, "another_name", null); + assertEquals("control", result.treatment); + assertEquals("exception", result.label); + + // with byflag + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(null); + Mockito.when(_splitCacheConsumer.get("another_name")).thenReturn(null); + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), new HashMap() {{ put(SPLIT_NAME, new FallbackTreatment("off")); }} ); + fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); + _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, fallbackTreatmentCalculator); + + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("off", result.treatment); + assertEquals("fallback - definition not found", result.label); + + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, "another_name", null); + assertEquals("on", result.treatment); + assertEquals("fallback - definition not found", result.label); + + split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, null, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), false, null); + Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null); + assertEquals("off", result.treatment); + assertEquals("fallback - exception", result.label); + + split = new ParsedSplit("another_name", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, null, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), false, null); + Mockito.when(_splitCacheConsumer.get("another_name")).thenReturn(split); + result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, "another_name", null); + assertEquals("on", result.treatment); + assertEquals("fallback - exception", result.label); + } } \ No newline at end of file