public void FlagReturnsFallthroughVariationAndEventIfPrerequisiteIsMetAndThereAreNoRules()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1")
                     .On(true)
                     .FallthroughVariation(1) // this is what makes the prerequisite pass
                     .Variations(new JValue("nogo"), new JValue("go"))
                     .Version(2)
                     .Build();

            featureStore.Upsert(VersionedDataKind.Features, f1);

            var result = f0.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Of("fall"), 0, EvaluationReason.FallthroughReason);

            Assert.Equal(expected, result.Result);

            Assert.Equal(1, result.PrerequisiteEvents.Count);
            FeatureRequestEvent e = result.PrerequisiteEvents[0];

            Assert.Equal(f1.Key, e.Key);
            Assert.Equal(LdValue.Of("go"), e.LdValue);
            Assert.Equal(f1.Version, e.Version);
            Assert.Equal(f0.Key, e.PrereqOf);
        }
Beispiel #2
0
        public void AllFlagsStateCanFilterForOnlyClientSideFlags()
        {
            var flag1 = new FeatureFlagBuilder("server-side-1").Build();
            var flag2 = new FeatureFlagBuilder("server-side-2").Build();
            var flag3 = new FeatureFlagBuilder("client-side-1").ClientSide(true)
                        .OffWithValue("value1").Build();
            var flag4 = new FeatureFlagBuilder("client-side-2").ClientSide(true)
                        .OffWithValue("value2").Build();

            featureStore.Upsert(VersionedDataKind.Features, flag1);
            featureStore.Upsert(VersionedDataKind.Features, flag2);
            featureStore.Upsert(VersionedDataKind.Features, flag3);
            featureStore.Upsert(VersionedDataKind.Features, flag4);

            var state = client.AllFlagsState(user, FlagsStateOption.ClientSideOnly);

            Assert.True(state.Valid);

            var expectedValues = new Dictionary <string, LdValue>
            {
                { "client-side-1", LdValue.Of("value1") },
                { "client-side-2", LdValue.Of("value2") }
            };

            Assert.Equal(expectedValues, state.ToValuesJsonMap());
        }
Beispiel #3
0
        public void CanSerializeToJson()
        {
            var state = new FeatureFlagsState(true);
            var flag1 = new FeatureFlagBuilder("key1").Version(100).Build();
            var flag2 = new FeatureFlagBuilder("key2").Version(200)
                        .TrackEvents(true).DebugEventsUntilDate(1000).Build();

            state.AddFlag(flag1, new JValue("value1"), 0, null);
            state.AddFlag(flag2, new JValue("value2"), 1, EvaluationReason.Fallthrough.Instance);

            var expectedString = @"{""key1"":""value1"",""key2"":""value2"",
                ""$flagsState"":{
                  ""key1"":{
                    ""variation"":0,""version"":100,""trackEvents"":false
                  },""key2"":{
                    ""variation"":1,""version"":200,""reason"":{""kind"":""FALLTHROUGH""},""trackEvents"":true,""debugEventsUntilDate"":1000
                  }
                },
                ""$valid"":true
            }";
            var expectedValue  = JsonConvert.DeserializeObject <JToken>(expectedString);
            var actualString   = JsonConvert.SerializeObject(state);
            var actualValue    = JsonConvert.DeserializeObject <JToken>(actualString);

            TestUtils.AssertJsonEqual(expectedValue, actualValue);
        }
        public void EventTrackingAndReasonAreNotForcedIfFlagIsNotSetForMatchingRule()
        {
            var clause0 = ClauseBuilder.ShouldNotMatchUser(user);
            var clause1 = ClauseBuilder.ShouldMatchUser(user);
            var rule0   = new RuleBuilder().Id("id0").Variation(1).Clauses(clause0).TrackEvents(true).Build();
            var rule1   = new RuleBuilder().Id("id1").Variation(1).Clauses(clause1).TrackEvents(false).Build();
            var flag    = new FeatureFlagBuilder("flag")
                          .On(true)
                          .Rules(rule0, rule1)
                          .OffVariation(0)
                          .Variations(new JValue("off"), new JValue("on"))
                          .Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);

            client.StringVariation("flag", user, "default");

            // It matched rule1, which has trackEvents: false, so we don't get the override behavior

            Assert.Equal(1, eventSink.Events.Count);
            var e = Assert.IsType <FeatureRequestEvent>(eventSink.Events[0]);

            Assert.False(e.TrackEvents);
            Assert.Null(e.Reason);
        }
        public void FlagReturnsOffVariationAndEventIfPrerequisiteIsOff()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1")
                     .On(false)
                     .OffVariation(1)
                     // note that even though it returns the desired variation, it is still off and therefore not a match
                     .Variations(new JValue("nogo"), new JValue("go"))
                     .Version(2)
                     .Build();

            featureStore.Upsert(VersionedDataKind.Features, f1);

            var result = f0.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Of("off"), 1,
                                                          EvaluationReason.PrerequisiteFailedReason("feature1"));

            Assert.Equal(expected, result.Result);

            Assert.Equal(1, result.PrerequisiteEvents.Count);
            FeatureRequestEvent e = result.PrerequisiteEvents[0];

            Assert.Equal(f1.Key, e.Key);
            Assert.Equal(LdValue.Of("go"), e.LdValue);
            Assert.Equal(f1.Version, e.Version);
            Assert.Equal(f0.Key, e.PrereqOf);
        }
        public void EventIsSentForExistingPrerequisiteFlag()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new List <Prerequisite> {
                new Prerequisite("feature1", 1)
            })
                     .Fallthrough(new VariationOrRollout(0, null))
                     .OffVariation(1)
                     .Variations(new List <JToken> {
                new JValue("fall"), new JValue("off"), new JValue("on")
            })
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1")
                     .On(true)
                     .Fallthrough(new VariationOrRollout(1, null))
                     .Variations(new List <JToken> {
                new JValue("nogo"), new JValue("go")
            })
                     .Version(2)
                     .Build();

            featureStore.Upsert(VersionedDataKind.Features, f0);
            featureStore.Upsert(VersionedDataKind.Features, f1);

            client.StringVariation("feature0", user, "default");

            Assert.Equal(2, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], f1, new JValue("go"), null, "feature0");
            CheckFeatureEvent(eventSink.Events[1], f0, new JValue("fall"), new JValue("default"), null);
        }
        public void MultipleLevelsOfPrerequisitesProduceMultipleEvents()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new List <Prerequisite> {
                new Prerequisite("feature1", 1)
            })
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(new List <JToken> {
                new JValue("fall"), new JValue("off"), new JValue("on")
            })
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1")
                     .On(true)
                     .Prerequisites(new List <Prerequisite> {
                new Prerequisite("feature2", 1)
            })
                     .FallthroughVariation(1)
                     .Variations(new List <JToken> {
                new JValue("nogo"), new JValue("go")
            })
                     .Version(2)
                     .Build();
            var f2 = new FeatureFlagBuilder("feature2")
                     .On(true)
                     .FallthroughVariation(1)
                     .Variations(new List <JToken> {
                new JValue("nogo"), new JValue("go")
            })
                     .Version(3)
                     .Build();

            featureStore.Upsert(VersionedDataKind.Features, f1);
            featureStore.Upsert(VersionedDataKind.Features, f2);

            var result = f0.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <JToken>(new JValue("fall"), 0, EvaluationReason.Fallthrough.Instance);

            Assert.Equal(expected, result.Result);

            Assert.Equal(2, result.PrerequisiteEvents.Count);

            FeatureRequestEvent e0 = result.PrerequisiteEvents[0];

            Assert.Equal(f2.Key, e0.Key);
            Assert.Equal(new JValue("go"), e0.Value);
            Assert.Equal(f2.Version, e0.Version);
            Assert.Equal(f1.Key, e0.PrereqOf);

            FeatureRequestEvent e1 = result.PrerequisiteEvents[1];

            Assert.Equal(f1.Key, e1.Key);
            Assert.Equal(new JValue("go"), e1.Value);
            Assert.Equal(f1.Version, e1.Version);
            Assert.Equal(f0.Key, e1.PrereqOf);
        }
        public void MarshalEntity()
        {
            var flag       = new FeatureFlagBuilder("flag").Build();
            var actualStr  = FeatureStoreHelpers.MarshalJson(flag);
            var actualJson = JsonConvert.DeserializeObject <JObject>(actualStr);

            Assert.Equal("flag", (string)actualJson.GetValue("key"));
        }
Beispiel #9
0
        public void CanGetFlagValue()
        {
            var state = new FeatureFlagsState(true);
            var flag  = new FeatureFlagBuilder("key").Build();

            state.AddFlag(flag, new JValue("value"), 1, null);

            Assert.Equal(new JValue("value"), state.GetFlagValue("key"));
        }
Beispiel #10
0
        public void CanGetFlagReason()
        {
            var state = new FeatureFlagsState(true);
            var flag  = new FeatureFlagBuilder("key").Build();

            state.AddFlag(flag, new JValue("value"), 1, EvaluationReason.Fallthrough.Instance);

            Assert.Equal(EvaluationReason.Fallthrough.Instance, state.GetFlagReason("key"));
        }
Beispiel #11
0
        public void ReasonIsNullIfReasonsWereNotRecorded()
        {
            var state = new FeatureFlagsState(true);
            var flag  = new FeatureFlagBuilder("key").Build();

            state.AddFlag(flag, new JValue("value"), 1, null);

            Assert.Null(state.GetFlagReason("key"));
        }
        public void IntVariationSendsEvent()
        {
            var flag = new FeatureFlagBuilder("key").OffWithValue(new JValue(2)).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);

            client.IntVariation("key", user, 1);
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, new JValue(2), new JValue(1), null);
        }
        public void CanGetDeprecatedFlagValue()
        {
            var state = new FeatureFlagsState(true);
            var flag  = new FeatureFlagBuilder("key").Build();

            state.AddFlag(flag, new JValue("value"), 1, null, false);
#pragma warning disable 0618
            Assert.Equal(new JValue("value"), state.GetFlagValue("key"));
#pragma warning restore 0618
        }
        public void StringVariationSendsEvent()
        {
            var flag = new FeatureFlagBuilder("key").OffWithValue(new JValue("b")).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);

            client.StringVariation("key", user, "a");
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, LdValue.Of("b"), LdValue.Of("a"), null);
        }
Beispiel #15
0
        public void AllFlagsStateReturnsEmptyStateForUserWithNullKey()
        {
            var flag = new FeatureFlagBuilder("key1").OffWithValue(new JValue("value1")).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);

            var state = client.AllFlagsState(User.WithKey(null));

            Assert.False(state.Valid);
            Assert.Equal(0, state.ToValuesJsonMap().Count);
        }
        public void JsonVariationSendsEvent()
        {
            var data = LdValue.Convert.String.ObjectFrom(new Dictionary <string, string> {
                { "thing", "stuff" }
            });
            var flag = new FeatureFlagBuilder("key").OffWithValue(data.InnerValue).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);
            var defaultVal = LdValue.Of(42);

            client.JsonVariation("key", user, defaultVal);
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, data, defaultVal, null);
        }
        public void JsonVariationSendsEvent()
        {
            var data = new JObject();

            data.Add("thing", new JValue("stuff"));
            var flag = new FeatureFlagBuilder("key").OffWithValue(data).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);
            var defaultVal = new JValue(42);

            client.JsonVariation("key", user, defaultVal);
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, data, defaultVal, null);
        }
        public void FlagReturnsNullIfFlagIsOffAndOffVariationIsUnspecified()
        {
            var f = new FeatureFlagBuilder("feature")
                    .On(false)
                    .FallthroughVariation(0)
                    .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                    .Build();
            var result = f.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Null, null, EvaluationReason.OffReason);

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
Beispiel #19
0
        public void CanDeserializeFromJson()
        {
            var state = new FeatureFlagsState(true);
            var flag1 = new FeatureFlagBuilder("key1").Version(100).Build();
            var flag2 = new FeatureFlagBuilder("key2").Version(200)
                        .TrackEvents(true).DebugEventsUntilDate(1000).Build();

            state.AddFlag(flag1, new JValue("value1"), 0, null);
            state.AddFlag(flag2, new JValue("value2"), 1, EvaluationReason.Fallthrough.Instance);

            var jsonString = JsonConvert.SerializeObject(state);
            var state1     = JsonConvert.DeserializeObject <FeatureFlagsState>(jsonString);

            Assert.Equal(state, state1);
        }
        public void FlagReturnsFallthroughIfFlagIsOnAndThereAreNoRules()
        {
            var f = new FeatureFlagBuilder("feature")
                    .On(true)
                    .OffVariation(1)
                    .FallthroughVariation(0)
                    .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                    .Build();
            var result = f.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Of("fall"), 0, EvaluationReason.FallthroughReason);

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
Beispiel #21
0
        public void CanMatchUserBySegment()
        {
            var segment = new Segment("segment1", 1, new List <string> {
                user.Key
            }, null, "", null, false);

            featureStore.Upsert(VersionedDataKind.Segments, segment);

            var clause  = new ClauseBuilder().Op("segmentMatch").Values(new JValue("segment1")).Build();
            var feature = new FeatureFlagBuilder("feature").BooleanWithClauses(clause).Build();

            featureStore.Upsert(VersionedDataKind.Features, feature);

            Assert.True(client.BoolVariation("feature", user, false));
        }
        public void FlagReturnsErrorIfFallthroughHasEmptyRolloutVariationList()
        {
            var f = new FeatureFlagBuilder("feature")
                    .On(true)
                    .OffVariation(1)
                    .Fallthrough(new VariationOrRollout(null, new Rollout(new List <WeightedVariation>(), null)))
                    .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                    .Build();
            var result = f.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Null, null,
                                                          EvaluationReason.ErrorReason(EvaluationErrorKind.MALFORMED_FLAG));

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
        public void DeprecatedJsonVariationSendsEvent()
        {
            var data = new JObject();

            data.Add("thing", new JValue("stuff"));
            var flag = new FeatureFlagBuilder("key").OffWithValue(data).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag);
            var defaultVal = new JValue(42);

#pragma warning disable 0618
            client.JsonVariation("key", user, defaultVal);
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, LdValue.FromJToken(data),
                              LdValue.FromJToken(defaultVal), null);
#pragma warning restore 0618
        }
        public void FlagReturnsOffVariationIfFlagIsOff()
        {
            var f = new FeatureFlagBuilder("feature")
                    .On(false)
                    .OffVariation(1)
                    .FallthroughVariation(0)
                    .Variations(new List <JToken> {
                new JValue("fall"), new JValue("off"), new JValue("on")
            })
                    .Build();
            var result = f.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <JToken>(new JValue("off"), 1, EvaluationReason.Off.Instance);

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
Beispiel #25
0
        public void CanConvertToValuesMap()
        {
            var state = new FeatureFlagsState(true);
            var flag1 = new FeatureFlagBuilder("key1").Build();
            var flag2 = new FeatureFlagBuilder("key2").Build();

            state.AddFlag(flag1, new JValue("value1"), 0, null);
            state.AddFlag(flag2, new JValue("value2"), 1, null);

            var expected = new Dictionary <string, JToken>
            {
                { "key1", new JValue("value1") },
                { "key2", new JValue("value2") }
            };

            Assert.Equal(expected, state.ToValuesMap());
        }
Beispiel #26
0
        public void AllFlagsReturnsNullIfNeitherClientNorFeatureStoreIsInited()
        {
            var featureStore = new InMemoryFeatureStore();
            var flag         = new FeatureFlagBuilder("key").OffWithValue(new JValue(1)).Build();

            featureStore.Upsert(VersionedDataKind.Features, flag); // but the store is still not inited

            var config = Configuration.Default("SDK_KEY").WithStartWaitTime(TimeSpan.Zero)
                         .WithFeatureStoreFactory(TestUtils.SpecificFeatureStore(featureStore))
                         .WithUpdateProcessorFactory(TestUtils.SpecificUpdateProcessor(updateProcessor))
                         .WithEventProcessorFactory(Components.NullEventProcessor);

            using (var client = new LdClient(config))
            {
                Assert.Null(client.AllFlags(User.WithKey("user")));
            }
        }
        public void FlagReturnsOffVariationIfPrerequisiteIsNotFound()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                     .Build();
            var result = f0.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <LdValue>(LdValue.Of("off"), 1,
                                                          EvaluationReason.PrerequisiteFailedReason("feature1"));

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
Beispiel #28
0
        public void AllFlagsStateCanOmitDetailsForUntrackedFlags()
        {
            var flag1 = new FeatureFlagBuilder("key1").Version(100)
                        .OffVariation(0).Variations(new List <JToken> {
                new JValue("value1")
            })
                        .Build();
            var flag2 = new FeatureFlagBuilder("key2").Version(200)
                        .OffVariation(1).Variations(new List <JToken> {
                new JValue("x"), new JValue("value2")
            })
                        .TrackEvents(true)
                        .Build();
            var flag3 = new FeatureFlagBuilder("key3").Version(300)
                        .OffVariation(1).Variations(new List <JToken> {
                new JValue("x"), new JValue("value3")
            })
                        .DebugEventsUntilDate(1000)
                        .Build();

            featureStore.Upsert(VersionedDataKind.Features, flag1);
            featureStore.Upsert(VersionedDataKind.Features, flag2);
            featureStore.Upsert(VersionedDataKind.Features, flag3);

            var state = client.AllFlagsState(user, FlagsStateOption.WithReasons);

            Assert.True(state.Valid);

            var expectedString = @"{""key1"":""value1"",""key2"":""value2"",""key3"":""value3"",
                ""$flagsState"":{
                  ""key1"":{
                    ""variation"":0,""version"":100,""reason"":{""kind"":""OFF""}
                  },""key2"":{
                    ""variation"":1,""version"":200,""reason"":{""kind"":""OFF""},""trackEvents"":true
                  },""key3"":{
                    ""variation"":1,""version"":300,""reason"":{""kind"":""OFF""},""debugEventsUntilDate"":1000
                  }
                },
                ""$valid"":true
            }";
            var expectedValue  = JsonConvert.DeserializeObject <JToken>(expectedString);
            var actualString   = JsonConvert.SerializeObject(state);
            var actualValue    = JsonConvert.DeserializeObject <JToken>(actualString);

            TestUtils.AssertJsonEqual(expectedValue, actualValue);
        }
        public void FlagReturnsErrorIfFlagIsOffAndOffVariationIsNegative()
        {
            var f = new FeatureFlagBuilder("feature")
                    .On(false)
                    .OffVariation(-1)
                    .FallthroughVariation(0)
                    .Variations(new List <JToken> {
                new JValue("fall"), new JValue("off"), new JValue("on")
            })
                    .Build();
            var result = f.Evaluate(baseUser, featureStore, EventFactory.Default);

            var expected = new EvaluationDetail <JToken>(null, null,
                                                         new EvaluationReason.Error(EvaluationErrorKind.MALFORMED_FLAG));

            Assert.Equal(expected, result.Result);
            Assert.Equal(0, result.PrerequisiteEvents.Count);
        }
        public void EventIsNotSentForUnknownPrerequisiteFlag()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .Fallthrough(new VariationOrRollout(0, null))
                     .OffVariation(1)
                     .Variations(new JValue("fall"), new JValue("off"), new JValue("on"))
                     .Version(1)
                     .Build();

            featureStore.Upsert(VersionedDataKind.Features, f0);

            client.StringVariation("feature0", user, "default");

            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], f0, LdValue.Of("off"), LdValue.Of("default"), null);
        }