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); }
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()); }
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")); }
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")); }
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")); }
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); }
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); }
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); }
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); }
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()); }
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); }
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); }