コード例 #1
0
        public void EvaluateFeature_WhenSplitWithoutConditions_ReturnsDefaultTreatment()
        {
            // Arrange.
            var splitName   = "always_on";
            var key         = new Key("test", "test");
            var parsedSplit = new ParsedSplit
            {
                algo             = AlgorithmEnum.Murmur,
                changeNumber     = 123123,
                name             = splitName,
                defaultTreatment = "off",
                trafficTypeName  = "tt",
                conditions       = new List <ConditionWithLogic>()
            };

            _splitCache
            .Setup(mock => mock.GetSplit(splitName))
            .Returns(parsedSplit);

            // Act.
            var result = _evaluator.EvaluateFeature(key, splitName);

            // Assert.
            Assert.AreEqual(parsedSplit.defaultTreatment, result.Treatment);
            Assert.AreEqual(parsedSplit.changeNumber, result.ChangeNumber);
            Assert.AreEqual(Labels.DefaultRule, result.Label);
        }
コード例 #2
0
        private TreatmentResult EvaluateTreatment(Key key, ParsedSplit parsedSplit, string featureName, Stopwatch clock = null, Dictionary <string, object> attributes = null)
        {
            try
            {
                if (clock == null)
                {
                    clock = new Stopwatch();
                    clock.Start();
                }

                if (parsedSplit == null)
                {
                    _log.Warn($"GetTreatment: you passed {featureName} that does not exist in this environment, please double check what Splits exist in the web console.");

                    return(new TreatmentResult(Labels.SplitNotFound, Control, elapsedMilliseconds: clock.ElapsedMilliseconds));
                }

                var treatmentResult = GetTreatmentResult(key, parsedSplit, attributes);

                if (parsedSplit.configurations != null && parsedSplit.configurations.ContainsKey(treatmentResult.Treatment))
                {
                    treatmentResult.Config = parsedSplit.configurations[treatmentResult.Treatment];
                }

                treatmentResult.ElapsedMilliseconds = clock.ElapsedMilliseconds;

                return(treatmentResult);
            }
            catch (Exception e)
            {
                _log.Error($"Exception caught getting treatment for feature: {featureName}", e);

                return(new TreatmentResult(Labels.Exception, Control, elapsedMilliseconds: clock.ElapsedMilliseconds));
            }
        }
コード例 #3
0
        public ParsedSplit Parse(Split split)
        {
            try
            {
                StatusEnum result;
                var        isValidStatus = Enum.TryParse(split.status, out result);
                if (!isValidStatus || result != StatusEnum.ACTIVE)
                {
                    return(null);
                }

                ParsedSplit parsedSplit = new ParsedSplit()
                {
                    name                  = split.name,
                    killed                = split.killed,
                    defaultTreatment      = split.defaultTreatment,
                    seed                  = split.seed,
                    conditions            = new List <ConditionWithLogic>(),
                    changeNumber          = split.changeNumber,
                    trafficTypeName       = split.trafficTypeName,
                    algo                  = split.algo == 0 || split.algo == null ? AlgorithmEnum.LegacyHash : (AlgorithmEnum)split.algo,
                    trafficAllocation     = split.trafficAllocation,
                    trafficAllocationSeed = split.trafficAllocationSeed.HasValue ? split.trafficAllocationSeed.Value : 0
                };
                parsedSplit = ParseConditions(split, parsedSplit);
                return(parsedSplit);
            }
            catch (Exception e)
            {
                Log.Error("Exception caught parsing split", e);
                return(null);
            }
        }
コード例 #4
0
        public void AddDuplicateSplitTest()
        {
            //Arrange
            var splitCache = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());
            var splitName  = "test1";

            //Act
            var parsedSplit1 = new ParsedSplit()
            {
                name = splitName
            };

            splitCache.AddSplit(splitName, parsedSplit1);
            var parsedSplit2 = new ParsedSplit()
            {
                name = splitName
            };

            splitCache.AddSplit(splitName, parsedSplit2);
            var result = splitCache.GetAllSplits();

            //Assert
            Assert.IsNotNull(result);
            Assert.AreEqual(1, result.Count);
            Assert.AreEqual(result[0], parsedSplit1);
            Assert.AreNotEqual(result[0], parsedSplit2);
        }
コード例 #5
0
        public void ExecuteGetSuccessfulWithResultsFromJSONFileIncludingTrafficAllocation()
        {
            //Arrange
            var segmentCache       = new InMemorySegmentCache(new ConcurrentDictionary <string, Segment>());
            var splitParser        = new InMemorySplitParser(new JSONFileSegmentFetcher("segment_payed.json", segmentCache), segmentCache);
            var splitChangeFetcher = new JSONFileSplitChangeFetcher("splits_staging_4.json");
            var splitChangesResult = splitChangeFetcher.Fetch(-1);
            var splitCache         = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());
            var gates = new InMemoryReadinessGatesCache();
            var selfRefreshingSplitFetcher = new SelfRefreshingSplitFetcher(splitChangeFetcher, splitParser, gates, 30, splitCache);

            selfRefreshingSplitFetcher.Start();
            gates.IsSDKReady(1000);

            //Act
            ParsedSplit result = (ParsedSplit)splitCache.GetSplit("Traffic_Allocation_UI");

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.name == "Traffic_Allocation_UI");
            Assert.IsTrue(result.trafficAllocation == 100);
            Assert.IsTrue(result.trafficAllocationSeed == 0);
            Assert.IsTrue(result.conditions.Count > 0);
            Assert.IsNotNull(result.conditions.Find(x => x.conditionType == ConditionType.ROLLOUT));
        }
コード例 #6
0
        public void EvaluateFeature_WithWhitelistCondition_EqualToBooleanMatcher_ReturnsOff()
        {
            // Arrange.
            var splitName   = "always_on";
            var key         = new Key("true", "true");
            var parsedSplit = new ParsedSplit
            {
                algo                  = AlgorithmEnum.Murmur,
                changeNumber          = 123123,
                name                  = splitName,
                defaultTreatment      = "off",
                trafficTypeName       = "tt",
                trafficAllocationSeed = 18,
                trafficAllocation     = 20,
                conditions            = new List <ConditionWithLogic>
                {
                    new ConditionWithLogic
                    {
                        label         = "label",
                        conditionType = ConditionType.WHITELIST,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "on",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EqualToBooleanMatcher(false)
                                }
                            }
                        }
                    }
                }
            };

            _splitCache
            .Setup(mock => mock.GetSplit(splitName))
            .Returns(parsedSplit);

            // Act.
            var result = _evaluator.EvaluateFeature(key, splitName);

            // Assert.
            Assert.AreEqual("off", result.Treatment);
            Assert.AreEqual(Labels.DefaultRule, result.Label);
            Assert.AreEqual(parsedSplit.changeNumber, result.ChangeNumber);
        }
コード例 #7
0
        public void EvaluateFeature_WithRolloutCondition_BucketIsBiggerTrafficAllocation_ReturnsDefailtTreatment()
        {
            // Arrange.
            var splitName   = "always_on";
            var key         = new Key("test", "test");
            var parsedSplit = new ParsedSplit
            {
                algo                  = AlgorithmEnum.Murmur,
                changeNumber          = 123123,
                name                  = splitName,
                defaultTreatment      = "off",
                trafficTypeName       = "tt",
                trafficAllocationSeed = 12,
                trafficAllocation     = 10,
                conditions            = new List <ConditionWithLogic>
                {
                    new ConditionWithLogic
                    {
                        conditionType = ConditionType.ROLLOUT,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "on",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher()
                            }
                        }
                    }
                }
            };

            _splitCache
            .Setup(mock => mock.GetSplit(splitName))
            .Returns(parsedSplit);

            _splitter
            .Setup(mock => mock.GetBucket(key.bucketingKey, parsedSplit.trafficAllocationSeed, AlgorithmEnum.Murmur))
            .Returns(18);

            // Act.
            var result = _evaluator.EvaluateFeature(key, splitName);

            // Assert.
            Assert.AreEqual(parsedSplit.defaultTreatment, result.Treatment);
            Assert.AreEqual(parsedSplit.changeNumber, result.ChangeNumber);
            Assert.AreEqual(Labels.TrafficAllocationFailed, result.Label);
        }
コード例 #8
0
        /// <summary>
        /// Creates a ParsedSplit instance that always returns
        /// treatment specified in input file. It is implemented this way
        /// for simplification. When a split is killed, the client
        /// returns default treatment for that feature.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="treatment"></param>
        /// <returns></returns>
        private ParsedSplit CreateParsedSplit(string name, string treatment)
        {
            var split = new ParsedSplit()
            {
                name             = name,
                seed             = 0,
                killed           = true,
                defaultTreatment = treatment,
                conditions       = null
            };

            return(split);
        }
コード例 #9
0
        private CombiningMatcher ParseMatcherGroup(ParsedSplit parsedSplit, MatcherGroupDefinition matcherGroupDefinition)
        {
            if (matcherGroupDefinition.matchers == null || matcherGroupDefinition.matchers.Count() == 0)
            {
                throw new Exception("Missing or empty matchers");
            }

            return(new CombiningMatcher()
            {
                delegates = matcherGroupDefinition.matchers.Select(x => ParseMatcher(parsedSplit, x)).ToList(),
                combiner = ParseCombiner(matcherGroupDefinition.combiner)
            });
        }
コード例 #10
0
        protected ParsedSplit CreateParsedSplit(string name, string treatment, List <ConditionWithLogic> codnitions = null)
        {
            var split = new ParsedSplit()
            {
                name              = name,
                seed              = 0,
                defaultTreatment  = treatment,
                conditions        = codnitions,
                algo              = AlgorithmEnum.Murmur,
                trafficAllocation = 100
            };

            return(split);
        }
コード例 #11
0
 private ParsedSplit ParseConditions(Split split, ParsedSplit parsedSplit)
 {
     foreach (var condition in split.conditions)
     {
         ConditionType result;
         var           isValidCondition = Enum.TryParse(condition.conditionType, out result);
         parsedSplit.conditions.Add(new ConditionWithLogic()
         {
             conditionType = isValidCondition ? result : ConditionType.WHITELIST,
             partitions    = condition.partitions,
             matcher       = ParseMatcherGroup(parsedSplit, condition.matcherGroup),
             label         = condition.label
         });
     }
     return(parsedSplit);
 }
コード例 #12
0
        public void EvaluateFeature_WhenSplitNameDoesntExist_ReturnsControl()
        {
            // Arrange.
            var         splitName   = "always_on";
            var         key         = new Key("test", "test");
            ParsedSplit parsedSplit = null;

            _splitCache
            .Setup(mock => mock.GetSplit(splitName))
            .Returns(parsedSplit);

            // Act.
            var result = _evaluator.EvaluateFeature(key, splitName);

            // Assert.
            Assert.AreEqual("control", result.Treatment);
            Assert.AreEqual(Labels.SplitNotFound, result.Label);
            _log.Verify(mock => mock.Warn($"GetTreatment: you passed {splitName} that does not exist in this environment, please double check what Splits exist in the web console."), Times.Once);
        }
コード例 #13
0
        protected override string GetTreatmentForFeature(Key key, string feature, Dictionary <string, object> attributes = null, bool logMetricsAndImpressions = true)
        {
            long start = CurrentTimeHelper.CurrentTimeMillis();
            var  clock = new Stopwatch();

            clock.Start();

            try
            {
                var split = splitCache.GetSplit(feature);

                if (split == null)
                {
                    if (logMetricsAndImpressions)
                    {
                        //if split definition was not found, impression label = "rules not found"
                        RecordStats(key, feature, null, LabelSplitNotFound, start, Control, SdkGetTreatment, clock);
                    }

                    Log.Warn(String.Format("Unknown or invalid feature: {0}", feature));
                    return(Control);
                }

                ParsedSplit parsedSplit = splitParser.Parse((Split)split);

                var treatment = GetTreatment(key, parsedSplit, attributes, start, clock, this, logMetricsAndImpressions);

                return(treatment);
            }
            catch (Exception e)
            {
                if (logMetricsAndImpressions)
                {
                    //if there was an exception, impression label = "exception"
                    RecordStats(key, feature, null, LabelException, start, Control, SdkGetTreatment, clock);
                }

                Log.Error(String.Format("Exception caught getting treatment for feature: {0}", feature), e);
                return(Control);
            }
        }
コード例 #14
0
        protected TreatmentResult GetTreatment(Key key, ParsedSplit split, Dictionary <string, object> attributes, ISplitClient splitClient)
        {
            if (!split.killed)
            {
                bool inRollout = false;
                // use the first matching condition
                foreach (var condition in split.conditions)
                {
                    if (!inRollout && condition.conditionType == ConditionType.ROLLOUT)
                    {
                        if (split.trafficAllocation < 100)
                        {
                            // bucket ranges from 1-100.
                            var bucket = split.algo == AlgorithmEnum.LegacyHash ? splitter.LegacyBucket(key.bucketingKey, split.trafficAllocationSeed) : splitter.Bucket(key.bucketingKey, split.trafficAllocationSeed);

                            if (bucket > split.trafficAllocation)
                            {
                                return(new TreatmentResult(LabelTrafficAllocationFailed, split.defaultTreatment, split.changeNumber));
                            }
                        }

                        inRollout = true;
                    }

                    var combiningMatcher = condition.matcher;

                    if (combiningMatcher.Match(key, attributes, splitClient))
                    {
                        var treatment = splitter.GetTreatment(key.bucketingKey, split.seed, condition.partitions, split.algo);

                        return(new TreatmentResult(condition.label, treatment, split.changeNumber));
                    }
                }

                return(new TreatmentResult(LabelDefaultRule, split.defaultTreatment, split.changeNumber));
            }
            else
            {
                return(new TreatmentResult(LabelKilled, split.defaultTreatment, split.changeNumber));
            }
        }
コード例 #15
0
        private void DecreaseTrafficTypeCount(ParsedSplit split)
        {
            if (split == null || string.IsNullOrEmpty(split.trafficTypeName))
            {
                return;
            }

            if (_trafficTypes.TryGetValue(split.trafficTypeName, out int quantity))
            {
                if (quantity <= 1)
                {
                    _trafficTypes.TryRemove(split.trafficTypeName, out int value);

                    return;
                }

                var newQuantity = quantity - 1;

                _trafficTypes.TryUpdate(split.trafficTypeName, newQuantity, quantity);
            }
        }
コード例 #16
0
        private TreatmentResult GetTreatmentResult(Key key, ParsedSplit split, Dictionary <string, object> attributes = null)
        {
            if (split.killed)
            {
                return(new TreatmentResult(Labels.Killed, split.defaultTreatment, split.changeNumber));
            }

            var inRollout = false;

            // use the first matching condition
            foreach (var condition in split.conditions)
            {
                if (!inRollout && condition.conditionType == ConditionType.ROLLOUT)
                {
                    if (split.trafficAllocation < 100)
                    {
                        // bucket ranges from 1-100.
                        var bucket = _splitter.GetBucket(key.bucketingKey, split.trafficAllocationSeed, split.algo);

                        if (bucket > split.trafficAllocation)
                        {
                            return(new TreatmentResult(Labels.TrafficAllocationFailed, split.defaultTreatment, split.changeNumber));
                        }
                    }

                    inRollout = true;
                }

                var combiningMatcher = condition.matcher;

                if (combiningMatcher.Match(key, attributes, this))
                {
                    var treatment = _splitter.GetTreatment(key.bucketingKey, split.seed, condition.partitions, split.algo);

                    return(new TreatmentResult(condition.label, treatment, split.changeNumber));
                }
            }

            return(new TreatmentResult(Labels.DefaultRule, split.defaultTreatment, split.changeNumber));
        }
コード例 #17
0
        public void ExecuteGetSuccessfulWithResults()
        {
            //Arrange
            var baseUrl = "https://sdk-aws-staging.split.io/api/";
            //var baseUrl = "http://localhost:3000/api/";
            var headers = new Dictionary <string, string>
            {
                { "SplitSDKMachineIP", "1.0.0.0" },
                { "SplitSDKMachineName", "localhost" },
                { "SplitSDKVersion", "1" }
            };

            var telemetryStorage        = new InMemoryTelemetryStorage();
            var sdkApiClient            = new SplitSdkApiClient("///PUT API KEY HERE///", headers, baseUrl, 10000, 10000, telemetryStorage);
            var apiSplitChangeFetcher   = new ApiSplitChangeFetcher(sdkApiClient);
            var sdkSegmentApiClient     = new SegmentSdkApiClient("///PUT API KEY HERE///", headers, baseUrl, 10000, 10000, telemetryStorage);
            var apiSegmentChangeFetcher = new ApiSegmentChangeFetcher(sdkSegmentApiClient);
            var gates                        = new InMemoryReadinessGatesCache();
            var segmentCache                 = new InMemorySegmentCache(new ConcurrentDictionary <string, Segment>());
            var segmentTaskQueue             = new SegmentTaskQueue();
            var wrapperAdapter               = new WrapperAdapter();
            var selfRefreshingSegmentFetcher = new SelfRefreshingSegmentFetcher(apiSegmentChangeFetcher, gates, 30, segmentCache, 4, segmentTaskQueue, new TasksManager(wrapperAdapter), wrapperAdapter);

            var splitParser = new InMemorySplitParser(selfRefreshingSegmentFetcher, segmentCache);
            var splitCache  = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());
            var selfRefreshingSplitFetcher = new SelfRefreshingSplitFetcher(apiSplitChangeFetcher, splitParser, gates, 30, new TasksManager(wrapperAdapter), splitCache);

            selfRefreshingSplitFetcher.Start();

            //Act
            gates.WaitUntilReady(1000);
            selfRefreshingSplitFetcher.Stop();
            ParsedSplit result  = (ParsedSplit)splitCache.GetSplit("Pato_Test_1");
            ParsedSplit result2 = (ParsedSplit)splitCache.GetSplit("Manu_Test_1");

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.name == "Pato_Test_1");
            Assert.IsTrue(result.conditions.Count > 0);
        }
コード例 #18
0
        public void ExecuteGetSuccessfulWithResults()
        {
            //Arrange
            var baseUrl = "https://sdk-aws-staging.split.io/api/";
            //var baseUrl = "http://localhost:3000/api/";
            var httpHeader = new HTTPHeader()
            {
                authorizationApiKey = "///PUT API KEY HERE///",
                splitSDKMachineIP   = "1.0.0.0",
                splitSDKMachineName = "localhost",
                splitSDKVersion     = "net-0.0.0",
                splitSDKSpecVersion = "1.2",
                encoding            = "gzip"
            };
            var sdkApiClient            = new SplitSdkApiClient(httpHeader, baseUrl, 10000, 10000);
            var apiSplitChangeFetcher   = new ApiSplitChangeFetcher(sdkApiClient);
            var sdkSegmentApiClient     = new SegmentSdkApiClient(httpHeader, baseUrl, 10000, 10000);
            var apiSegmentChangeFetcher = new ApiSegmentChangeFetcher(sdkSegmentApiClient);
            var gates        = new InMemoryReadinessGatesCache();
            var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary <string, Segment>());
            var selfRefreshingSegmentFetcher = new SelfRefreshingSegmentFetcher(apiSegmentChangeFetcher, gates, 30, segmentCache, 4);

            var splitParser = new InMemorySplitParser(selfRefreshingSegmentFetcher, segmentCache);
            var splitCache  = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());
            var selfRefreshingSplitFetcher = new SelfRefreshingSplitFetcher(apiSplitChangeFetcher, splitParser, gates, 30, splitCache);

            selfRefreshingSplitFetcher.Start();

            //Act
            gates.IsSDKReady(1000);
            selfRefreshingSplitFetcher.Stop();
            ParsedSplit result  = (ParsedSplit)splitCache.GetSplit("Pato_Test_1");
            ParsedSplit result2 = (ParsedSplit)splitCache.GetSplit("Manu_Test_1");

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.name == "Pato_Test_1");
            Assert.IsTrue(result.conditions.Count > 0);
        }
コード例 #19
0
        public void ExecuteGetSuccessfulWithResultsFromJSONFile()
        {
            //Arrange
            var segmentCache       = new InMemorySegmentCache(new ConcurrentDictionary <string, Segment>());
            var splitParser        = new InMemorySplitParser(new JSONFileSegmentFetcher("segment_payed.json", segmentCache), segmentCache);
            var splitChangeFetcher = new JSONFileSplitChangeFetcher("splits_staging.json");
            var splitChangesResult = splitChangeFetcher.Fetch(-1);
            var splitCache         = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());
            var gates = new InMemoryReadinessGatesCache();
            var selfRefreshingSplitFetcher = new SelfRefreshingSplitFetcher(splitChangeFetcher, splitParser, gates, 30, splitCache);

            selfRefreshingSplitFetcher.Start();
            gates.IsSDKReady(1000);

            //Act
            ParsedSplit result = (ParsedSplit)splitCache.GetSplit("Pato_Test_1");

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.name == "Pato_Test_1");
            Assert.IsTrue(result.conditions.Count > 0);
        }
コード例 #20
0
        public void AddOrUpdate_WhenUpdateTraffictType_ReturnsTrue()
        {
            // Arrange
            var splitCache = new InMemorySplitCache(new ConcurrentDictionary <string, ParsedSplit>());

            var splitName  = "split_1";
            var splitName2 = "split_2";

            var split = new ParsedSplit {
                name = splitName, trafficTypeName = "traffic_type_1"
            };
            var split2 = new ParsedSplit {
                name = splitName, trafficTypeName = "traffic_type_2"
            };
            var split3 = new ParsedSplit {
                name = splitName, trafficTypeName = "traffic_type_3"
            };
            var split4 = new ParsedSplit {
                name = splitName2, trafficTypeName = "traffic_type_4"
            };

            splitCache.AddOrUpdate(splitName, split);
            splitCache.AddOrUpdate(splitName, split2);
            splitCache.AddOrUpdate(splitName, split3);
            splitCache.AddOrUpdate(splitName2, split4);

            // Act
            var result1 = splitCache.TrafficTypeExists("traffic_type_1");
            var result2 = splitCache.TrafficTypeExists("traffic_type_2");
            var result3 = splitCache.TrafficTypeExists("traffic_type_3");

            // Assert
            Assert.IsFalse(result1);
            Assert.IsFalse(result2);
            Assert.IsTrue(result3);
        }
コード例 #21
0
 protected abstract IMatcher GetInSegmentMatcher(MatcherDefinition matcherDefinition, ParsedSplit parsedSplit);
コード例 #22
0
        private AttributeMatcher ParseMatcher(ParsedSplit parsedSplit, MatcherDefinition matcherDefinition)
        {
            if (matcherDefinition.matcherType == null)
            {
                throw new Exception("Missing matcher type value");
            }
            var matcherType = matcherDefinition.matcherType;

            IMatcher matcher = null;

            try
            {
                MatcherTypeEnum result;
                var             isValidMatcherType = Enum.TryParse(matcherType, out result);
                if (isValidMatcherType)
                {
                    switch (result)
                    {
                    case MatcherTypeEnum.ALL_KEYS:
                        matcher = GetAllKeysMatcher(); break;

                    case MatcherTypeEnum.BETWEEN:
                        matcher = GetBetweenMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.EQUAL_TO:
                        matcher = GetEqualToMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.GREATER_THAN_OR_EQUAL_TO:
                        matcher = GetGreaterThanOrEqualToMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.IN_SEGMENT:
                        matcher = GetInSegmentMatcher(matcherDefinition, parsedSplit); break;

                    case MatcherTypeEnum.LESS_THAN_OR_EQUAL_TO:
                        matcher = GetLessThanOrEqualToMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.WHITELIST:
                        matcher = GetWhitelistMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.EQUAL_TO_SET:
                        matcher = GetEqualToSetMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.CONTAINS_ANY_OF_SET:
                        matcher = GetContainsAnyOfSetMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.CONTAINS_ALL_OF_SET:
                        matcher = GetContainsAllOfSetMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.PART_OF_SET:
                        matcher = GetPartOfSetMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.STARTS_WITH:
                        matcher = GetStartsWithMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.ENDS_WITH:
                        matcher = GetEndsWithMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.CONTAINS_STRING:
                        matcher = GetContainsStringMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.IN_SPLIT_TREATMENT: matcher = GetDependencyMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.EQUAL_TO_BOOLEAN: matcher = GetEqualToBooleanMatcher(matcherDefinition); break;

                    case MatcherTypeEnum.MATCHES_STRING: matcher = GetMatchesStringMatcher(matcherDefinition); break;
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error("Error parsing matcher", e);
            }

            if (matcher == null)
            {
                throw new Exception(string.Format("Unable to create matcher for matcher type: {0}", matcherType));
            }

            AttributeMatcher attributeMatcher = new AttributeMatcher()
            {
                matcher = matcher,
                negate  = matcherDefinition.negate
            };

            if (matcherDefinition.keySelector != null && matcherDefinition.keySelector.attribute != null)
            {
                attributeMatcher.attribute = matcherDefinition.keySelector.attribute;
            }

            return(attributeMatcher);
        }
コード例 #23
0
        public void EvaluateFeatures_WhenSplitNameDoesntExist_ReturnsControl()
        {
            // Arrange.
            var splitNames = new List <string> {
                "always_on", "always_off"
            };
            var key           = new Key("*****@*****.**", "test");
            var parsedSplitOn = new ParsedSplit
            {
                algo                  = AlgorithmEnum.Murmur,
                changeNumber          = 123123,
                name                  = splitNames.First(s => s.Equals("always_on")),
                defaultTreatment      = "off",
                trafficTypeName       = "tt",
                trafficAllocationSeed = 18,
                trafficAllocation     = 20,
                seed                  = 123123133,
                conditions            = new List <ConditionWithLogic>
                {
                    new ConditionWithLogic
                    {
                        label         = "labelEndsWithMatcher",
                        conditionType = ConditionType.ROLLOUT,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "on",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EndsWithMatcher(new List <string> {
                                        "@split.io"
                                    })
                                }
                            }
                        }
                    }
                }
            };

            var parsedSplitOff = new ParsedSplit
            {
                algo             = AlgorithmEnum.Murmur,
                changeNumber     = 123123,
                name             = splitNames.First(s => s.Equals("always_off")),
                defaultTreatment = "off",
                trafficTypeName  = "tt",
                seed             = 5647567,
                conditions       = new List <ConditionWithLogic>
                {
                    new ConditionWithLogic
                    {
                        label         = "labelWhiteList",
                        conditionType = ConditionType.WHITELIST,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "off",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EndsWithMatcher(new List <string> {
                                        "@split.io"
                                    })
                                }
                            }
                        }
                    },
                    new ConditionWithLogic
                    {
                        label         = "labelRollout",
                        conditionType = ConditionType.ROLLOUT,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "off",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EqualToMatcher(DataTypeEnum.NUMBER, 123)
                                }
                            }
                        }
                    }
                }
            };

            _splitter
            .Setup(mock => mock.GetBucket(key.bucketingKey, parsedSplitOn.trafficAllocationSeed, AlgorithmEnum.Murmur))
            .Returns(18);

            _splitCache
            .Setup(mock => mock.FetchMany(It.IsAny <List <string> >()))
            .Returns(new List <ParsedSplit>
            {
                parsedSplitOff,
                parsedSplitOn
            });

            _splitter
            .Setup(mock => mock.GetTreatment(key.bucketingKey, parsedSplitOn.seed, It.IsAny <List <PartitionDefinition> >(), parsedSplitOn.algo))
            .Returns("on");

            _splitter
            .Setup(mock => mock.GetTreatment(key.bucketingKey, parsedSplitOff.seed, It.IsAny <List <PartitionDefinition> >(), parsedSplitOff.algo))
            .Returns("off");

            // Act.
            var result = _evaluator.EvaluateFeatures(key, splitNames);

            // Assert.
            var resultOn = result.TreatmentResults.FirstOrDefault(tr => tr.Key.Equals("always_on"));

            Assert.AreEqual("on", resultOn.Value.Treatment);
            Assert.AreEqual(parsedSplitOn.changeNumber, resultOn.Value.ChangeNumber);
            Assert.AreEqual("labelEndsWithMatcher", resultOn.Value.Label);

            var resultOff = result.TreatmentResults.FirstOrDefault(tr => tr.Key.Equals("always_off"));

            Assert.AreEqual("off", resultOff.Value.Treatment);
            Assert.AreEqual(parsedSplitOn.changeNumber, resultOff.Value.ChangeNumber);
            Assert.AreEqual("labelWhiteList", resultOff.Value.Label);
        }
コード例 #24
0
        protected override IMatcher GetInSegmentMatcher(MatcherDefinition matcherDefinition, ParsedSplit parsedSplit)
        {
            var matcherData = matcherDefinition.userDefinedSegmentMatcherData;

            segmentFetcher.InitializeSegment(matcherData.segmentName);
            return(new UserDefinedSegmentMatcher(matcherData.segmentName, segmentsCache));
        }
コード例 #25
0
        public void EvaluateFeature_WithTwoConditions_EndsWithMatch_ReturnsOn()
        {
            // Arrange.
            var splitName   = "always_on";
            var key         = new Key("*****@*****.**", "true");
            var parsedSplit = new ParsedSplit
            {
                algo                  = AlgorithmEnum.Murmur,
                changeNumber          = 123123,
                name                  = splitName,
                defaultTreatment      = "off",
                trafficTypeName       = "tt",
                trafficAllocationSeed = 18,
                trafficAllocation     = 20,
                conditions            = new List <ConditionWithLogic>
                {
                    new ConditionWithLogic
                    {
                        label         = "label",
                        conditionType = ConditionType.WHITELIST,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "on",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EqualToBooleanMatcher(false)
                                }
                            }
                        }
                    },
                    new ConditionWithLogic
                    {
                        label         = "labelEndsWith",
                        conditionType = ConditionType.WHITELIST,
                        partitions    = new List <PartitionDefinition>
                        {
                            new PartitionDefinition
                            {
                                treatment = "on",
                                size      = 100
                            }
                        },
                        matcher = new CombiningMatcher
                        {
                            combiner  = CombinerEnum.AND,
                            delegates = new List <AttributeMatcher>
                            {
                                new AttributeMatcher
                                {
                                    matcher = new EndsWithMatcher(new List <string> {
                                        "@split.io"
                                    })
                                }
                            }
                        }
                    },
                }
            };

            _splitCache
            .Setup(mock => mock.GetSplit(splitName))
            .Returns(parsedSplit);

            _splitter
            .Setup(mock => mock.GetTreatment(key.bucketingKey, parsedSplit.seed, It.IsAny <List <PartitionDefinition> >(), parsedSplit.algo))
            .Returns("on");

            // Act.
            var result = _evaluator.EvaluateFeature(key, splitName);

            // Assert.
            Assert.AreEqual("on", result.Treatment);
            Assert.AreEqual("labelEndsWith", result.Label);
            Assert.AreEqual(parsedSplit.changeNumber, result.ChangeNumber);
        }
コード例 #26
0
        protected string GetTreatment(Key key, ParsedSplit split, Dictionary <string, object> attributes, long start, Stopwatch clock, ISplitClient splitClient, bool logMetricsAndImpressions)
        {
            if (!split.killed)
            {
                bool inRollout = false;
                // use the first matching condition
                foreach (ConditionWithLogic condition in split.conditions)
                {
                    if (!inRollout && condition.conditionType == ConditionType.ROLLOUT)
                    {
                        if (split.trafficAllocation < 100)
                        {
                            // bucket ranges from 1-100.
                            int bucket = split.algo == AlgorithmEnum.LegacyHash ? splitter.LegacyBucket(key.bucketingKey, split.trafficAllocationSeed) : splitter.Bucket(key.bucketingKey, split.trafficAllocationSeed);

                            if (bucket >= split.trafficAllocation)
                            {
                                if (logMetricsAndImpressions)
                                {
                                    // If not in traffic allocation, abort and return
                                    // default treatment
                                    RecordStats(key, split.name, split.changeNumber, LabelTrafficAllocationFailed, start, split.defaultTreatment, SdkGetTreatment, clock);
                                }

                                return(split.defaultTreatment);
                            }
                        }
                        inRollout = true;
                    }
                    var combiningMatcher = condition.matcher;
                    if (combiningMatcher.Match(key, attributes, splitClient))
                    {
                        var treatment = splitter.GetTreatment(key.bucketingKey, split.seed, condition.partitions, split.algo);

                        if (logMetricsAndImpressions)
                        {
                            //If condition matched, impression label = condition.label
                            RecordStats(key, split.name, split.changeNumber, condition.label, start, treatment, SdkGetTreatment, clock);
                        }

                        return(treatment);
                    }
                }

                if (logMetricsAndImpressions)
                {
                    //If no condition matched, impression label = "no rule matched"
                    RecordStats(key, split.name, split.changeNumber, LabelNoConditionMatched, start, split.defaultTreatment, SdkGetTreatment, clock);
                }
                return(split.defaultTreatment);
            }
            else
            {
                if (logMetricsAndImpressions)
                {
                    //If split was killed, impression label = "killed"
                    RecordStats(key, split.name, split.changeNumber, LabelKilled, start, split.defaultTreatment, SdkGetTreatment, clock);
                }
                return(split.defaultTreatment);
            }
        }