Exemple #1
0
        public void FlagReturnsFallthroughVariationAndEventIfPrerequisiteIsMetAndThereAreNoRules()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(fallthroughValue, offValue, onValue)
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1")
                     .On(true)
                     .FallthroughVariation(1) // this is what makes the prerequisite pass
                     .Variations(LdValue.Of("nogo"), LdValue.Of("go"))
                     .Version(2)
                     .Build();
            var evaluator = BasicEvaluator.WithStoredFlags(f1);

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

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

            Assert.Equal(expected, result.Result);

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

            Assert.Equal(f1.Key, e.FlagKey);
            Assert.Equal(LdValue.Of("go"), e.Value);
            Assert.Equal(f1.Version, e.FlagVersion);
            Assert.Equal(f0.Key, e.PrerequisiteOf);
        }
Exemple #2
0
        public void SerializeFlagWithAllProperties()
        {
            var flag1 = new FeatureFlagBuilder()
                        .Version(1)
                        .Value(LdValue.Of(false))
                        .Variation(2)
                        .FlagVersion(3)
                        .Reason(EvaluationReason.OffReason)
                        .TrackEvents(true)
                        .DebugEventsUntilDate(UnixMillisecondTime.OfMillis(1234))
                        .Build();

            AssertJsonEqual(@"{""version"":1,""value"":false,""variation"":2,""flagVersion"":3," +
                            @"""reason"":{""kind"":""OFF""},""trackEvents"":true,""debugEventsUntilDate"":1234}",
                            DataModelSerialization.SerializeFlag(flag1));

            // make sure we're treating trackReason separately from trackEvents
            var flag2 = new FeatureFlagBuilder()
                        .Version(1)
                        .Value(LdValue.Of(false))
                        .Reason(EvaluationReason.OffReason)
                        .Variation(2)
                        .FlagVersion(3)
                        .TrackReason(true)
                        .Build();

            AssertJsonEqual(@"{""version"":1,""value"":false,""variation"":2,""flagVersion"":3," +
                            @"""reason"":{""kind"":""OFF""},""trackReason"":true}",
                            DataModelSerialization.SerializeFlag(flag2));
        }
        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(LdValue.Of("value1")).Build();
            var flag4 = new FeatureFlagBuilder("client-side-2").ClientSide(true)
                        .OffWithValue(LdValue.Of("value2")).Build();

            testData.UsePreconfiguredFlag(flag1);
            testData.UsePreconfiguredFlag(flag2);
            testData.UsePreconfiguredFlag(flag3);
            testData.UsePreconfiguredFlag(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 AddToData_DuplicateKeysHandling_Throw()
        {
            const string key = "flag1";

            FeatureFlag initialFeatureFlag = new FeatureFlagBuilder(key).Version(0).Build();

            var flagData = new Dictionary <string, ItemDescriptor>
            {
                { key, new ItemDescriptor(1, initialFeatureFlag) }
            };
            var segmentData = new Dictionary <string, ItemDescriptor>();
            var fileData    = new DataSetBuilder()
                              .Flags(new FeatureFlagBuilder(key).Version(1).Build()).Build();

            FlagFileDataMerger merger = new FlagFileDataMerger(FileDataTypes.DuplicateKeysHandling.Throw);

            Exception err = Assert.Throws <Exception>(() =>
            {
                merger.AddToData(fileData, flagData, segmentData);
            });

            Assert.Equal("in \"features\", key \"flag1\" was already defined", err.Message);

            ItemDescriptor postFeatureFlag = flagData[key];

            Assert.Same(initialFeatureFlag, postFeatureFlag.Item);
            Assert.Equal(1, postFeatureFlag.Version);
        }
Exemple #5
0
        public void AddToData_DuplicateKeysHandling_Ignore()
        {
            const string key = "flag1";

            FeatureFlag initialFeatureFlag = new FeatureFlagBuilder(key).Version(0).Build();

            var flagData = new Dictionary <string, ItemDescriptor>
            {
                { key, new ItemDescriptor(1, initialFeatureFlag) }
            };
            var          segmentData = new Dictionary <string, ItemDescriptor>();
            FlagFileData fileData    = new FlagFileData
            {
                Flags = new Dictionary <string, FeatureFlag>
                {
                    {
                        key,
                        new FeatureFlagBuilder(key).Version(1).Build()
                    }
                }
            };

            FlagFileDataMerger merger = new FlagFileDataMerger(FileDataTypes.DuplicateKeysHandling.Ignore);

            merger.AddToData(fileData, flagData, segmentData);

            ItemDescriptor postFeatureFlag = flagData[key];

            Assert.Same(initialFeatureFlag, postFeatureFlag.Item);
            Assert.Equal(1, postFeatureFlag.Version);
        }
Exemple #6
0
        public void FlagReturnsOffVariationAndEventIfPrerequisiteIsOff()
        {
            var f0 = new FeatureFlagBuilder("feature0")
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .OffVariation(1)
                     .FallthroughVariation(0)
                     .Variations(fallthroughValue, offValue, onValue)
                     .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(LdValue.Of("nogo"), LdValue.Of("go"))
                     .Version(2)
                     .Build();
            var evaluator = BasicEvaluator.WithStoredFlags(f1);

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

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

            Assert.Equal(expected, result.Result);

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

            Assert.Equal(f1.Key, e.FlagKey);
            Assert.Equal(LdValue.Of("go"), e.Value);
            Assert.Equal(f1.Version, e.FlagVersion);
            Assert.Equal(f0.Key, e.PrerequisiteOf);
        }
        public void UpsertUpdatesPersistentStore()
        {
            var flag1a = new FeatureFlagBuilder().Version(1).Value(true).Build();
            var flag1b = new FeatureFlagBuilder().Version(2).Value(true).Build();
            var flag2  = new FeatureFlagBuilder().Version(1).Value(false).Build();
            var data1a = new DataSetBuilder().Add("flag1", flag1a).Add("flag2", flag2).Build();
            var data1b = new DataSetBuilder().Add("flag1", flag1b).Add("flag2", flag2).Build();

            var store = MakeStore(1);

            store.Init(BasicUser, data1a, true);

            var updated = store.Upsert("flag1", flag1b.ToItemDescriptor());

            Assert.True(updated);

            var item = store.Get("flag1"); // this is reading only from memory, not the persistent store

            Assert.Equal(flag1b.ToItemDescriptor(), item);

            var data = _persistentStore.InspectUserData(BasicMobileKey, BasicUser.Key);

            Assert.NotNull(data);
            AssertHelpers.DataSetsEqual(data1b, data.Value);
        }
Exemple #8
0
        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").Version(1)
                          .On(true)
                          .Rules(rule0, rule1)
                          .OffVariation(0)
                          .Variations(LdValue.Of("off"), LdValue.Of("on"))
                          .Build();

            testData.UsePreconfiguredFlag(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 <EvaluationEvent>(eventSink.Events[0]);

            Assert.False(e.TrackEvents);
            Assert.Null(e.Reason);
        }
Exemple #9
0
        public void EventIsSentForExistingPrerequisiteFlag()
        {
            var f0 = new FeatureFlagBuilder("feature0").Version(1)
                     .On(true)
                     .Prerequisites(new Prerequisite("feature1", 1))
                     .Fallthrough(new VariationOrRollout(0, null))
                     .OffVariation(1)
                     .Variations(LdValue.Of("fall"), LdValue.Of("off"), LdValue.Of("on"))
                     .Version(1)
                     .Build();
            var f1 = new FeatureFlagBuilder("feature1").Version(1)
                     .On(true)
                     .Fallthrough(new VariationOrRollout(1, null))
                     .Variations(LdValue.Of("nogo"), LdValue.Of("go"))
                     .Version(2)
                     .Build();

            testData.UsePreconfiguredFlag(f0);
            testData.UsePreconfiguredFlag(f1);

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

            Assert.Equal(2, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], f1, LdValue.Of("go"), LdValue.Null, "feature0");
            CheckFeatureEvent(eventSink.Events[1], f0, LdValue.Of("fall"), LdValue.Of("default"), null);
        }
Exemple #10
0
            public async Task WhenUserNotInFlags_DoesntSave()
            {
                // Arrange
                var flags = new FeatureFlagBuilder()
                            .WithFlight("A", accounts: new List <string> {
                    "user1", "user2"
                })
                            .Build();

                _storage
                .Setup(s => s.GetFileReferenceAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName, null))
                .ReturnsAsync(BuildFileReference(flags));

                // Act
                await _target.RemoveUserAsync(new User { Username = "******" });

                // Assert
                _storage.Verify(
                    s => s.SaveFileAsync(
                        It.IsAny <string>(),
                        It.IsAny <string>(),
                        It.IsAny <Stream>(),
                        It.IsAny <IAccessCondition>()),
                    Times.Never);

                _auditing.Verify(
                    a => a.SaveAuditRecordAsync(
                        It.IsAny <FeatureFlagsAuditRecord>()),
                    Times.Never());
            }
        public void AllFlagsStateReturnsStateWithReasons()
        {
            var flag1 = new FeatureFlagBuilder("key1").Version(100)
                        .OffVariation(0).Variations(LdValue.Of("value1"))
                        .Build();
            var flag2 = new FeatureFlagBuilder("key2").Version(200)
                        .OffVariation(1).Variations(LdValue.Of("x"), LdValue.Of("value2"))
                        .TrackEvents(true).DebugEventsUntilDate(UnixMillisecondTime.OfMillis(1000))
                        .Build();

            testData.UsePreconfiguredFlag(flag1);
            testData.UsePreconfiguredFlag(flag2);

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

            Assert.True(state.Valid);

            var expectedString = @"{""key1"":""value1"",""key2"":""value2"",
                ""$flagsState"":{
                  ""key1"":{
                    ""variation"":0,""version"":100,""reason"":{""kind"":""OFF""}
                  },""key2"":{
                    ""variation"":1,""version"":200,""reason"":{""kind"":""OFF""},""trackEvents"":true,""debugEventsUntilDate"":1000
                  }
                },
                ""$valid"":true
            }";
            var actualString   = LdJsonSerialization.SerializeObject(state);

            AssertHelpers.JsonEqual(expectedString, actualString);
        }
        private static void AssertVariationIndexFromRollout(
            int expectedVariation,
            Rollout rollout,
            User user,
            string flagKey,
            string salt
            )
        {
            var flag1 = new FeatureFlagBuilder(flagKey)
                        .On(true)
                        .GeneratedVariations(3)
                        .FallthroughRollout(rollout)
                        .Salt(salt)
                        .Build();
            var result1 = BasicEvaluator.Evaluate(flag1, user, EventFactory.Default);

            Assert.Equal(EvaluationReason.FallthroughReason, result1.Result.Reason);
            Assert.Equal(expectedVariation, result1.Result.VariationIndex);

            // Make sure we consistently apply the rollout regardless of whether it's in a rule or a fallthrough
            var flag2 = new FeatureFlagBuilder(flagKey)
                        .On(true)
                        .GeneratedVariations(3)
                        .Rules(new RuleBuilder().Id("id")
                               .Rollout(rollout)
                               .Clauses(new ClauseBuilder().Attribute(UserAttribute.Key).Op(Operator.In).Values(LdValue.Of(user.Key)).Build())
                               .Build())
                        .Salt(salt)
                        .Build();
            var result2 = BasicEvaluator.Evaluate(flag2, user, EventFactory.Default);

            Assert.Equal(EvaluationReason.RuleMatchReason(0, "id"), result2.Result.Reason);
            Assert.Equal(expectedVariation, result2.Result.VariationIndex);
        }
        public void BigSegmentStateIsQueriedOnlyOncePerUserEvenIfFlagReferencesMultipleSegments()
        {
            var segment1    = new SegmentBuilder("segmentkey1").Unbounded(true).Generation(2).Build();
            var segment2    = new SegmentBuilder("segmentkey2").Unbounded(true).Generation(3).Build();
            var bigSegments = new MockBigSegmentProvider();
            var membership  = MockMembership.New().Include(segment2);

            bigSegments.Membership[baseUser.Key] = membership;
            var flag = new FeatureFlagBuilder("key").On(true)
                       .Variations(LdValue.Of(false), LdValue.Of(true))
                       .FallthroughVariation(0)
                       .Rules(
                new RuleBuilder().Variation(1).Clauses(ClauseBuilder.ShouldMatchSegment(segment1.Key)).Build(),
                new RuleBuilder().Variation(1).Clauses(ClauseBuilder.ShouldMatchSegment(segment2.Key)).Build()
                )
                       .Build();
            var evaluator = BasicEvaluator.WithStoredSegments(segment1, segment2).WithBigSegments(bigSegments);

            var result = evaluator.Evaluate(flag, baseUser, EventFactory.Default);

            Assert.Equal(LdValue.Of(true), result.Result.Value);
            Assert.Equal(BigSegmentsStatus.Healthy, result.Result.Reason.BigSegmentsStatus);

            Assert.Equal(1, bigSegments.MembershipQueryCount);
            Assert.Equal(new List <string> {
                MakeBigSegmentRef(segment1), MakeBigSegmentRef(segment2)
            },
                         membership.Queries);
        }
Exemple #14
0
        public void ClauseCanMatchCustomAttribute()
        {
            var clause = new ClauseBuilder().Attribute("legs").Op("in").Values(LdValue.Of(4)).Build();
            var f      = new FeatureFlagBuilder("key").BooleanWithClauses(clause).Build();
            var user   = User.Builder("key").Custom("legs", 4).Build();

            Assert.Equal(LdValue.Of(true), BasicEvaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
Exemple #15
0
        public void ClauseReturnsFalseForMissingAttribute()
        {
            var clause = new ClauseBuilder().Attribute("legs").Op("in").Values(LdValue.Of(4)).Build();
            var f      = new FeatureFlagBuilder("key").BooleanWithClauses(clause).Build();
            var user   = User.Builder("key").Name("bob").Build();

            Assert.Equal(LdValue.Of(false), BasicEvaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
Exemple #16
0
        public void SegmentMatchClauseFallsThroughIfSegmentNotFound()
        {
            var f         = new FeatureFlagBuilder("key").BooleanMatchingSegment("segkey").Build();
            var user      = User.WithKey("foo");
            var evaluator = BasicEvaluator.WithNonexistentSegment("segkey");

            Assert.Equal(LdValue.Of(false), evaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
        private bool SegmentMatchesUser(Segment segment, User user)
        {
            var flag      = new FeatureFlagBuilder("key").BooleanMatchingSegment(segment.Key).Build();
            var evaluator = BasicEvaluator.WithStoredSegments(segment);
            var result    = evaluator.Evaluate(flag, user, EventFactory.Default);

            return(result.Result.Value.AsBool);
        }
Exemple #18
0
        public void ClauseWithUnknownOperatorDoesNotMatch()
        {
            var clause = new ClauseBuilder().Attribute("name").Op("invalidOp").Values(LdValue.Of("Bob")).Build();
            var f      = new FeatureFlagBuilder("key").BooleanWithClauses(clause).Build();
            var user   = User.Builder("key").Name("Bob").Build();

            Assert.Equal(LdValue.Of(false), BasicEvaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
Exemple #19
0
        public void ClauseCanBeNegated()
        {
            var clause = new ClauseBuilder().Attribute("name").Op("in").Values(LdValue.Of("Bob"))
                         .Negate(true).Build();
            var f    = new FeatureFlagBuilder("key").BooleanWithClauses(clause).Build();
            var user = User.Builder("key").Name("Bob").Build();

            Assert.Equal(LdValue.Of(false), BasicEvaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
Exemple #20
0
        public void StringVariationSendsEvent()
        {
            var flag = new FeatureFlagBuilder("key").Version(1).OffWithValue(LdValue.Of("b")).Build();

            testData.UsePreconfiguredFlag(flag);

            client.StringVariation("key", user, "a");
            Assert.Equal(1, eventSink.Events.Count);
            CheckFeatureEvent(eventSink.Events[0], flag, LdValue.Of("b"), LdValue.Of("a"), null);
        }
Exemple #21
0
        public void SegmentMatchClauseRetrievesSegmentFromStore()
        {
            var segment   = new SegmentBuilder("segkey").Version(1).Included("foo").Build();
            var evaluator = BasicEvaluator.WithStoredSegments(segment);

            var f    = new FeatureFlagBuilder("key").BooleanMatchingSegment("segkey").Build();
            var user = User.WithKey("foo");

            Assert.Equal(LdValue.Of(true), evaluator.Evaluate(f, user, EventFactory.Default).Result.Value);
        }
Exemple #22
0
        public void SerializeFlagWithMinimalProperties()
        {
            var flag = new FeatureFlagBuilder()
                       .Version(1)
                       .Value(LdValue.Of(false))
                       .Build();

            AssertJsonEqual(@"{""version"":1,""value"":false}",
                            DataModelSerialization.SerializeFlag(flag));
        }
Exemple #23
0
        public void FloatVariationSendsEvent()
        {
            var flag = new FeatureFlagBuilder("key").Version(1).OffWithValue(LdValue.Of(2.5f)).Build();

            testData.UsePreconfiguredFlag(flag);

            client.FloatVariation("key", user, 1.0f);
            Assert.Single(eventSink.Events);
            CheckFeatureEvent(eventSink.Events[0], flag, LdValue.Of(2.5f), LdValue.Of(1.0f), null);
        }
            public async Task RemovesUser()
            {
                string savedJson = null;
                var    flags     = new FeatureFlagBuilder()
                                   .WithFlight("A", accounts: new List <string> {
                    "user1", "user2"
                })
                                   .WithFlight("B", accounts: new List <string> {
                    "USER1"
                })
                                   .WithFlight("C", accounts: new List <string> {
                    "user2"
                })
                                   .Build();

                _storage
                .Setup(s => s.GetFileReferenceAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName, null))
                .ReturnsAsync(BuildFileReference(flags));

                _storage
                .Setup(s => s.SaveFileAsync(
                           CoreConstants.Folders.ContentFolderName,
                           CoreConstants.FeatureFlagsFileName,
                           It.IsAny <Stream>(),
                           It.IsAny <IAccessCondition>()))
                .Callback((string folder, string file, Stream content, IAccessCondition condition) =>
                {
                    savedJson = ToString(content);
                })
                .Returns(Task.CompletedTask);

                // Act
                await _target.RemoveUserAsync(new User { Username = "******" });

                // Arrange
                Assert.NotNull(savedJson);

                var savedFlags = JsonConvert.DeserializeObject <FeatureFlags>(savedJson);

                Assert.Equal(3, savedFlags.Flights.Count);
                Assert.Single(savedFlags.Flights["A"].Accounts);
                Assert.Empty(savedFlags.Flights["B"].Accounts);
                Assert.Single(savedFlags.Flights["C"].Accounts);

                Assert.Equal("user2", savedFlags.Flights["A"].Accounts[0]);
                Assert.Equal("user2", savedFlags.Flights["C"].Accounts[0]);

                _storage.Verify(
                    s => s.SaveFileAsync(
                        CoreConstants.Folders.ContentFolderName,
                        CoreConstants.FeatureFlagsFileName,
                        It.IsAny <Stream>(),
                        It.IsAny <IAccessCondition>()),
                    Times.Once);
            }
        public void GetKnownFlag()
        {
            var flag1    = new FeatureFlagBuilder().Version(100).Value(LdValue.Of(true)).Build();
            var initData = new DataSetBuilder()
                           .Add("flag1", flag1)
                           .Build();

            _store.Init(BasicUser, initData, false);

            Assert.Equal(flag1.ToItemDescriptor(), _store.Get("flag1"));
        }
        public void AllFlagsStateReturnsEmptyStateForUserWithNullKey()
        {
            var flag = new FeatureFlagBuilder("key1").OffWithValue(LdValue.Of("value1")).Build();

            testData.UsePreconfiguredFlag(flag);

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

            Assert.False(state.Valid);
            Assert.Equal(0, state.ToValuesJsonMap().Count);
        }
Exemple #27
0
        public void JsonVariationSendsEvent()
        {
            var data = LdValue.BuildObject().Add("thing", "stuff").Build();
            var flag = new FeatureFlagBuilder("key").Version(1).OffWithValue(data).Build();

            testData.UsePreconfiguredFlag(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 async Task WhenSavePreconditionFailsOnce_Retries()
            {
                // Arrange
                var    firstTry  = true;
                string savedJson = null;
                var    flags     = new FeatureFlagBuilder()
                                   .WithFlight("A", accounts: new List <string> {
                    "user1", "user2"
                })
                                   .Build();

                _storage
                .Setup(s => s.GetFileReferenceAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName, null))
                .ReturnsAsync(BuildFileReference(flags));

                _storage
                .Setup(s => s.SaveFileAsync(
                           CoreConstants.Folders.ContentFolderName,
                           CoreConstants.FeatureFlagsFileName,
                           It.IsAny <Stream>(),
                           It.IsAny <IAccessCondition>()))
                .Callback((string folder, string file, Stream content, IAccessCondition condition) =>
                {
                    if (firstTry)
                    {
                        firstTry = false;
                        throw _preconditionException;
                    }

                    savedJson = ToString(content);
                })
                .Returns(Task.CompletedTask);

                // Act
                await _target.RemoveUserAsync(new User { Username = "******" });

                // Assert
                Assert.NotNull(savedJson);

                var savedFlags = JsonConvert.DeserializeObject <FeatureFlags>(savedJson);

                Assert.Single(savedFlags.Flights);
                Assert.Single(savedFlags.Flights["A"].Accounts);
                Assert.Equal("user2", savedFlags.Flights["A"].Accounts[0]);

                _storage.Verify(
                    s => s.SaveFileAsync(
                        CoreConstants.Folders.ContentFolderName,
                        CoreConstants.FeatureFlagsFileName,
                        It.IsAny <Stream>(),
                        It.IsAny <IAccessCondition>()),
                    Times.Exactly(2));
            }
Exemple #29
0
            public async Task WhenSavePreconditionAlwaysFails_Throws()
            {
                // Arrange
                var flags = new FeatureFlagBuilder()
                            .WithFlight("A", accounts: new List <string> {
                    "user1", "user2"
                })
                            .Build();

                _storage
                .Setup(s => s.GetFileReferenceAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName, null))
                .ReturnsAsync(BuildFileReference(flags));

                _storage
                .Setup(s => s.SaveFileAsync(
                           CoreConstants.Folders.ContentFolderName,
                           CoreConstants.FeatureFlagsFileName,
                           It.IsAny <Stream>(),
                           It.IsAny <IAccessCondition>()))
                .ThrowsAsync(_preconditionException);

                // Act
                var exception = await Assert.ThrowsAsync <InvalidOperationException>(() => _target.RemoveUserAsync(new User {
                    Username = "******"
                }));

                // Assert
                Assert.Contains("Unable to remove user from feature flags", exception.Message);

                _storage.Verify(
                    s => s.SaveFileAsync(
                        CoreConstants.Folders.ContentFolderName,
                        CoreConstants.FeatureFlagsFileName,
                        It.IsAny <Stream>(),
                        It.IsAny <IAccessCondition>()),
                    Times.Exactly(3));

                _auditing.Verify(
                    a => a.SaveAuditRecordAsync(
                        It.Is <FeatureFlagsAuditRecord>(
                            r => r.Action == AuditedFeatureFlagsAction.Update &&
                            r.ContentId == "fake-content-id" &&
                            r.Result == FeatureFlagSaveResult.Conflict &&
                            !r.Features.Any() &&
                            r.Flights.SingleOrDefault(
                                f => f.Name == "A" &&
                                !f.All &&
                                !f.SiteAdmins &&
                                f.Accounts.Single() == "user2" &&
                                !f.Domains.Any()) != null)),
                    Times.Exactly(3));
            }
Exemple #30
0
        public void FlagValueChangeListener()
        {
            var flagKey           = "important-flag";
            var user              = User.WithKey("important-user");
            var otherUser         = User.WithKey("unimportant-user");
            var store             = new InMemoryDataStore();
            var dataSourceUpdates = TestUtils.BasicDataSourceUpdates(store, testLogger);

            var resultMap = new Dictionary <KeyValuePair <string, User>, LdValue>();

            var tracker = new FlagTrackerImpl(dataSourceUpdates, (key, u) =>
                                              resultMap[new KeyValuePair <string, User>(key, u)]);

            resultMap[new KeyValuePair <string, User>(flagKey, user)]      = LdValue.Of(false);
            resultMap[new KeyValuePair <string, User>(flagKey, otherUser)] = LdValue.Of(false);

            var eventSink1 = new EventSink <FlagValueChangeEvent>();
            var eventSink2 = new EventSink <FlagValueChangeEvent>();
            var eventSink3 = new EventSink <FlagValueChangeEvent>();
            var listener1  = tracker.FlagValueChangeHandler(flagKey, user, eventSink1.Add);
            var listener2  = tracker.FlagValueChangeHandler(flagKey, user, eventSink2.Add);
            var listener3  = tracker.FlagValueChangeHandler(flagKey, otherUser, eventSink3.Add);

            tracker.FlagChanged += listener1;
            tracker.FlagChanged += listener2;
            tracker.FlagChanged -= listener2; // just verifying that removing a listener works
            tracker.FlagChanged += listener3;

            eventSink1.ExpectNoValue();
            eventSink2.ExpectNoValue();
            eventSink3.ExpectNoValue();

            // make the flag true for the first user only, and broadcast a flag change event
            resultMap[new KeyValuePair <string, User>(flagKey, user)] = LdValue.Of(true);
            var flagV1 = new FeatureFlagBuilder(flagKey).Version(1).Build();

            dataSourceUpdates.Upsert(DataModel.Features, flagKey, DescriptorOf(flagV1));

            // eventSink1 receives a value change event
            var event1 = eventSink1.ExpectValue();

            Assert.Equal(flagKey, event1.Key);
            Assert.Equal(LdValue.Of(false), event1.OldValue);
            Assert.Equal(LdValue.Of(true), event1.NewValue);
            eventSink1.ExpectNoValue();

            // eventSink2 doesn't receive one, because it was unregistered
            eventSink2.ExpectNoValue();

            // eventSink3 doesn't receive one, because the flag's value hasn't changed for otherUser
            eventSink3.ExpectNoValue();
        }