public void CanBucketByIntAttributeSameAsString() { var user = User.Builder("key") .Custom("stringattr", "33333") .Custom("intattr", 33333) .Build(); var resultForString = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("stringattr"), "salt"); var resultForInt = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("intattr"), "salt"); Assert.Equal((double)resultForInt, (double)resultForString, 10); }
public void FlagKeyAndSaltDoNotMatterWhenSeedIsUsed() { var user = User.WithKey("userkey"); const string flagKey1 = "flagkey", flagKey2 = "flagkey2"; const string salt1 = "salt", salt2 = "salt2"; const int seed = 123; var bucketValue1 = Bucketing.BucketUser(seed, user, flagKey1, UserAttribute.Key, salt1); var bucketValue2 = Bucketing.BucketUser(seed, user, flagKey2, UserAttribute.Key, salt2); Assert.Equal(bucketValue1, bucketValue2); }
public void DifferentSeedsProduceDifferentAssignment() { var user = User.WithKey("userkey"); const string flagKey = "flagkey"; const string salt = "salt"; const int seed1 = 123, seed2 = 456; var bucketValue1 = Bucketing.BucketUser(seed1, user, flagKey, UserAttribute.Key, salt); var bucketValue2 = Bucketing.BucketUser(seed2, user, flagKey, UserAttribute.Key, salt); Assert.NotEqual(bucketValue1, bucketValue2); }
public void UsingSeedIsDifferentThanSalt() { var user = User.WithKey("userkey"); const string flagKey = "flagkey"; const string salt = "salt"; const int seed = 123; var bucketValue1 = Bucketing.BucketUser(null, user, flagKey, UserAttribute.Key, salt); var bucketValue2 = Bucketing.BucketUser(seed, user, flagKey, UserAttribute.Key, salt); Assert.NotEqual(bucketValue1, bucketValue2); }
public void UserSecondaryKeyAffectsBucketValue() { var user1 = User.WithKey("key"); var user2 = User.Builder("key").Secondary("other").Build(); const string flagKey = "flagkey"; const string salt = "salt"; var result1 = Bucketing.BucketUser(null, user1, flagKey, UserAttribute.Key, salt); var result2 = Bucketing.BucketUser(null, user2, flagKey, UserAttribute.Key, salt); Assert.NotEqual(result1, result2); }
public void BucketUserByKeyTest() { var user1 = User.WithKey("userKeyA"); var point1 = Bucketing.BucketUser(noSeed, user1, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.42157587, point1, decimalPlacesOfEquality); var user2 = User.WithKey("userKeyB"); var point2 = Bucketing.BucketUser(noSeed, user2, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.6708485, point2, decimalPlacesOfEquality); var user3 = User.WithKey("userKeyC"); var point3 = Bucketing.BucketUser(noSeed, user3, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.10343106, point3, decimalPlacesOfEquality); }
public void LastBucketIsUsedIfBucketValueEqualsTotalWeight() { var user = User.WithKey("userkey"); const string flagKey = "flagkey"; const string salt = "salt"; // We'll construct a list of variations that stops right at the target bucket value int bucketValue = (int)(Bucketing.BucketUser(null, user, flagKey, UserAttribute.Key, salt) * 100000); var variations = new List <WeightedVariation>() { new WeightedVariation(0, bucketValue, true) }; var rollout = new Rollout(RolloutKind.Rollout, null, variations, null); AssertVariationIndexFromRollout(0, rollout, user, flagKey, salt); }
public void CannotBucketByOtherDataTypes() { foreach (var attributeValue in new LdValue[] { LdValue.Null, LdValue.Of(true), LdValue.Of(33333.5) }) { var user = User.Builder("key") .Custom("badattr", attributeValue) .Build(); var result = Bucketing.BucketUser(null, user, "key", UserAttribute.ForName("badattr"), "salt"); if (result != 0f) { Assert.True(false, "got unexpected value " + result + " for attribute value " + attributeValue); } } }
public void BucketUserWithSeedTest() { const int seed = 61; var user1 = User.WithKey("userKeyA"); var point1 = Bucketing.BucketUser(seed, user1, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.09801207, point1, decimalPlacesOfEquality); var user2 = User.WithKey("userKeyB"); var point2 = Bucketing.BucketUser(seed, user2, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.14483777, point2, decimalPlacesOfEquality); var user3 = User.WithKey("userKeyC"); var point3 = Bucketing.BucketUser(seed, user3, "hashKey", UserAttribute.Key, "saltyA"); Assert.Equal(0.9242641, point3, decimalPlacesOfEquality); }
private EvaluationDetail <LdValue> GetValueForVariationOrRollout(int?variation, Rollout?rollout, EvaluationReason reason) { if (variation.HasValue) { return(GetVariation(variation.Value, reason)); } if (rollout.HasValue && rollout.Value.Variations.Count() > 0) { WeightedVariation?selectedVariation = null; var bucketBy = rollout.Value.BucketBy.GetValueOrDefault(UserAttribute.Key); float bucket = Bucketing.BucketUser(rollout.Value.Seed, _user, _flag.Key, bucketBy, _flag.Salt); float sum = 0F; foreach (WeightedVariation wv in rollout.Value.Variations) { sum += (float)wv.Weight / 100000F; if (bucket < sum) { selectedVariation = wv; break; } } if (!selectedVariation.HasValue) { // The user's bucket value was greater than or equal to the end of the last bucket. This could happen due // to a rounding error, or due to the fact that we are scaling to 100000 rather than 99999, or the flag // data could contain buckets that don't actually add up to 100000. Rather than returning an error in // this case (or changing the scaling, which would potentially change the results for *all* users), we // will simply put the user in the last bucket. selectedVariation = rollout.Value.Variations.Last(); } var inExperiment = (rollout.Value.Kind == RolloutKind.Experiment) && !selectedVariation.Value.Untracked; return(GetVariation(selectedVariation.Value.Variation, inExperiment ? reason.WithInExperiment(true) : reason)); } else { _parent._logger.Error("Data inconsistency in feature flag \"{0}\": variation/rollout object with no variation or rollout", _flag.Key); return(ErrorResult(EvaluationErrorKind.MalformedFlag)); } }
private bool MatchSegmentRule(Segment segment, SegmentRule segmentRule) { foreach (var c in segmentRule.Clauses) { if (!MatchClauseNoSegments(c)) { return(false); } } // If the Weight is absent, this rule matches if (!segmentRule.Weight.HasValue) { return(true); } // All of the clauses are met. See if the user buckets in var by = segmentRule.BucketBy.GetValueOrDefault(UserAttribute.Key); double bucket = Bucketing.BucketUser(null, _user, segment.Key, by, segment.Salt); double weight = (double)segmentRule.Weight / 100000F; return(bucket < weight); }
public void VariationIndexForBucket() { var user = User.WithKey("userkey"); const string flagKey = "flagkey"; const string salt = "salt"; // First verify that with our test inputs, the bucket value will be greater than zero and less than 100000, // so we can construct a rollout whose second bucket just barely contains that value var bucketValue = (int)(Bucketing.BucketUser(null, user, flagKey, UserAttribute.Key, salt) * 100000); Assert.InRange(bucketValue, 1, 99999); const int badVariationA = 0, matchedVariation = 1, badVariationB = 2; var variations = new List <WeightedVariation>() { new WeightedVariation(badVariationA, bucketValue, true), // end of bucket range is not inclusive, so it will *not* match the target value new WeightedVariation(matchedVariation, 1, true), // size of this bucket is 1, so it only matches that specific value new WeightedVariation(badVariationB, 100000 - (bucketValue + 1), true) }; var rollout = new Rollout(RolloutKind.Rollout, null, variations, null); AssertVariationIndexFromRollout(matchedVariation, rollout, user, flagKey, salt); }