public void BackgroundDisabledState()
        {
            var testData     = TestData.DataSource();
            var backgrounder = new MockBackgroundModeManager();
            var config       = BasicConfig()
                               .BackgroundModeManager(backgrounder)
                               .DataSource(testData)
                               .EnableBackgroundUpdating(false)
                               .Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                Assert.True(client.DataSourceStatusProvider.WaitFor(DataSourceState.Valid, TimeSpan.FromSeconds(5)));

                var statuses = new EventSink <DataSourceStatus>();
                client.DataSourceStatusProvider.StatusChanged += statuses.Add;

                backgrounder.UpdateBackgroundMode(true);

                var newStatus1 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.BackgroundDisabled, newStatus1.State);

                backgrounder.UpdateBackgroundMode(false);

                var newStatus2 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Initializing, newStatus2.State);

                var newStatus3 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Valid, newStatus3.State);
            }
        }
        public void DataSourceStatusIsInterruptedAfterErrorIfAlreadyInitialized()
        {
            var dataSourceFactory = new CapturingDataSourceFactory();

            var config = BasicConfig()
                         .DataSource(dataSourceFactory)
                         .Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                var statuses = new EventSink <DataSourceStatus>();
                client.DataSourceStatusProvider.StatusChanged += statuses.Add;

                dataSourceFactory.UpdateSink.UpdateStatus(DataSourceState.Valid, null);

                var newStatus1 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Valid, newStatus1.State);

                var errorInfo = DataSourceStatus.ErrorInfo.FromHttpError(503);
                dataSourceFactory.UpdateSink.UpdateStatus(DataSourceState.Interrupted, errorInfo);

                var newStatus2 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Interrupted, newStatus2.State);
                Assert.Equal(errorInfo, newStatus2.LastError);
            }
        }
        public void SetOfflineStatusOverridesNetworkUnavailableStatus()
        {
            var testData     = TestData.DataSource();
            var connectivity = new MockConnectivityStateManager(false);
            var config       = BasicConfig()
                               .DataSource(testData)
                               .ConnectivityStateManager(connectivity)
                               .Offline(true)
                               .Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                Assert.True(client.DataSourceStatusProvider.WaitFor(DataSourceState.SetOffline, TimeSpan.FromSeconds(5)));

                var statuses = new EventSink <DataSourceStatus>();
                client.DataSourceStatusProvider.StatusChanged += statuses.Add;

                client.SetOffline(false, TimeSpan.FromSeconds(1));

                var newStatus1 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.NetworkUnavailable, newStatus1.State);

                connectivity.Connect(true);

                var newStatus2 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Initializing, newStatus2.State);

                var newStatus3 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Valid, newStatus3.State);
            }
        }
Esempio n. 4
0
        public void PollingDetectsStaleStatus()
        {
            SetStoreTimestamp(UnixMillisecondTime.Now.PlusMillis(5000)); // future time, definitely not stale

            var bsConfig = Components.BigSegments(_storeFactory)
                           .StatusPollInterval(TimeSpan.FromMilliseconds(10))
                           .StaleAfter(TimeSpan.FromMilliseconds(200))
                           .CreateBigSegmentsConfiguration(BasicContext);

            using (var sw = new BigSegmentStoreWrapper(bsConfig, BasicTaskExecutor, TestLogger))
            {
                var status1 = sw.GetStatus();
                Assert.False(status1.Stale);

                var statuses = new EventSink <BigSegmentStoreStatus>();
                sw.StatusChanged += statuses.Add;

                SetStoreTimestamp(UnixMillisecondTime.Now.PlusMillis(-200));

                var status2 = statuses.ExpectValue();
                Assert.True(status2.Stale);

                SetStoreTimestamp(UnixMillisecondTime.Now.PlusMillis(5000));

                var status3 = statuses.ExpectValue();
                Assert.False(status3.Stale);
            }
        }
        public void StatusListenerIsNotifiedOnFailureAndRecovery(TestParams testParams)
        {
            using (var wrapper = MakeWrapper(testParams))
            {
                var dataStoreStatusProvider = new DataStoreStatusProviderImpl(wrapper, _dataStoreUpdates);

                var statuses = new EventSink <DataStoreStatus>();
                dataStoreStatusProvider.StatusChanged += statuses.Add;

                CauseStoreError(_core, wrapper);

                var status1 = statuses.ExpectValue();
                Assert.False(status1.Available);
                Assert.False(status1.RefreshNeeded);

                Assert.True(logCapture.HasMessageWithRegex(LogLevel.Warn, "Detected persistent store unavailability"));

                MakeStoreAvailable(_core);

                var status2 = statuses.ExpectValue();
                Assert.True(status2.Available);
                Assert.Equal(!testParams.CacheMode.IsCachedIndefinitely, status2.RefreshNeeded);

                Assert.True(logCapture.HasMessageWithRegex(LogLevel.Warn, "Persistent store is available again"));
            }
        }
Esempio n. 6
0
        public void CanUseCustomEventDispatcher()
        {
            var actions        = new EventSink <Action>();
            var customExecutor = new TaskExecutor(MyEventSender, actions.Enqueue, testLogger);

            var values1 = new EventSink <string>();
            var values2 = new EventSink <string>();

            myEvent += values1.Add;
            myEvent += values2.Add;

            customExecutor.ScheduleEvent("hello", myEvent);

            values1.ExpectNoValue();
            values2.ExpectNoValue();

            var action1 = actions.ExpectValue();
            var action2 = actions.ExpectValue();

            actions.ExpectNoValue();

            action1();
            action2();
            Assert.Equal("hello", values1.ExpectValue());
            Assert.Equal("hello", values2.ExpectValue());
        }
Esempio n. 7
0
        public FullDataSet ExpectInit(User user)
        {
            var action = Assert.IsType <ReceivedInit>(Actions.ExpectValue(TimeSpan.FromSeconds(5)));

            AssertHelpers.UsersEqual(user, action.User);
            return(action.Data);
        }
        public void CacheIsWrittenToStoreAfterRecoveryIfTtlIsInfinite()
        {
            using (var wrapper = MakeWrapper(CachedIndefinitely))
            {
                var dataStoreStatusProvider = new DataStoreStatusProviderImpl(wrapper, _dataStoreUpdates);

                var statuses = new EventSink <DataStoreStatus>();
                dataStoreStatusProvider.StatusChanged += statuses.Add;

                string key1 = "key1", key2 = "key2";
                var    item1 = new TestItem("name1");
                var    item2 = new TestItem("name2");

                wrapper.Init(new TestDataBuilder()
                             .Add(TestDataKind, key1, 1, item1)
                             .Build());

                // In infinite cache mode, we do *not* expect exceptions thrown by the store to be propagated; it will
                // swallow the error, but also go into polling/recovery mode. Note that even though the store rejects
                // this update, it'll still be cached.
                CauseStoreError(_core, wrapper);
                Assert.Equal(FakeError, Assert.Throws(FakeError.GetType(),
                                                      () => wrapper.Upsert(TestDataKind, key1, item1.WithVersion(2))));

                Assert.Equal(item1.WithVersion(2), wrapper.Get(TestDataKind, key1));

                var status1 = statuses.ExpectValue();
                Assert.False(status1.Available);
                Assert.False(status1.RefreshNeeded);

                // While the store is still down, try to update it again - the update goes into the cache

                Assert.Equal(FakeError, Assert.Throws(FakeError.GetType(),
                                                      () => wrapper.Upsert(TestDataKind, key2, item2.WithVersion(1))));

                Assert.Equal(item2.WithVersion(1), wrapper.Get(TestDataKind, key2));

                // Verify that this update did not go into the underlying data yet

                Assert.False(_core.Data[TestDataKind].TryGetValue(key2, out _));

                // Now simulate the store coming back up
                MakeStoreAvailable(_core);

                // Wait for the poller to notice this and publish a new status
                var status2 = statuses.ExpectValue();
                Assert.True(status2.Available);
                Assert.False(status2.RefreshNeeded);

                // Once that has happened, the cache should have been written to the store
                Assert.Equal(item1.SerializedWithVersion(2), _core.Data[TestDataKind][key1]);
                Assert.Equal(item2.SerializedWithVersion(1), _core.Data[TestDataKind][key2]);
            }
        }
        public void ClientSendsFlagValueChangeEvents()
        {
            var testData = TestData.DataSource();
            var config   = BasicConfig().DataSource(testData).Build();

            var flagKey = "flagkey";

            testData.Update(testData.Flag(flagKey).Variation(true));

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                var eventSink1 = new EventSink <FlagValueChangeEvent>();
                var eventSink2 = new EventSink <FlagValueChangeEvent>();
                EventHandler <FlagValueChangeEvent> listener1 = eventSink1.Add;
                EventHandler <FlagValueChangeEvent> listener2 = eventSink2.Add;
                client.FlagTracker.FlagValueChanged += listener1;
                client.FlagTracker.FlagValueChanged += listener2;

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

                testData.Update(testData.Flag(flagKey).Variation(false));

                var event1 = eventSink1.ExpectValue();
                var event2 = eventSink2.ExpectValue();
                Assert.Equal(flagKey, event1.Key);
                Assert.Equal(LdValue.Of(true), event1.OldValue);
                Assert.Equal(LdValue.Of(false), event1.NewValue);
                Assert.Equal(event1, event2);

                eventSink1.ExpectNoValue();
                eventSink2.ExpectNoValue();
            }
        }
        public void EventIsSentForChangedFlagOnInit()
        {
            var events = new EventSink <FlagValueChangeEvent>();

            _flagTracker.FlagValueChanged += events.Add;

            var initData1 = new DataSetBuilder()
                            .Add("key1", new FeatureFlagBuilder().Value(LdValue.Of(true)).Variation(0).Build())
                            .Add("key2", new FeatureFlagBuilder().Value(LdValue.Of(true)).Variation(0).Build())
                            .Build();

            _updateSink.Init(_basicUser, initData1);

            var initData2 = new DataSetBuilder()
                            .Add("key1", new FeatureFlagBuilder().Value(LdValue.Of(false)).Variation(1).Build())
                            .Add("key2", new FeatureFlagBuilder().Value(LdValue.Of(true)).Variation(0).Build())
                            .Build();

            _updateSink.Init(_basicUser, initData2);

            var e = events.ExpectValue();

            Assert.Equal("key1", e.Key);
            Assert.Equal(LdValue.Of(true), e.OldValue);
            Assert.Equal(LdValue.Of(false), e.NewValue);
            Assert.False(e.Deleted);
        }
        public void ValueChangesAreTrackedSeparatelyForEachUser()
        {
            var events = new EventSink <FlagValueChangeEvent>();

            _flagTracker.FlagValueChanged += events.Add;

            var initDataForBasicUser = new DataSetBuilder()
                                       .Add("key1", new FeatureFlagBuilder().Version(100).Value(LdValue.Of("a")).Variation(1).Build())
                                       .Add("key2", new FeatureFlagBuilder().Version(200).Value(LdValue.Of("b")).Variation(2).Build())
                                       .Build();

            _updateSink.Init(_basicUser, initDataForBasicUser);

            var initDataForOtherUser = new DataSetBuilder()
                                       .Add("key1", new FeatureFlagBuilder().Version(100).Value(LdValue.Of("c")).Variation(3).Build())
                                       .Add("key2", new FeatureFlagBuilder().Version(200).Value(LdValue.Of("d")).Variation(4).Build())
                                       .Build();

            _updateSink.Init(_otherUser, initDataForOtherUser);

            events.ExpectNoValue();

            _updateSink.Upsert(_basicUser, "key1",
                               new FeatureFlagBuilder().Version(101).Value(LdValue.Of("c")).Variation(3).Build().ToItemDescriptor());

            var e = events.ExpectValue();

            Assert.Equal("key1", e.Key);
            Assert.Equal(LdValue.Of("a"), e.OldValue);
            Assert.Equal(LdValue.Of("c"), e.NewValue);
        }
Esempio n. 12
0
        public void PassesConfiguredEventSenderToEventHandler()
        {
            var gotSender = new EventSink <object>();

            myEvent += (sender, args) => gotSender.Enqueue(sender);

            executor.ScheduleEvent("hello", myEvent);

            Assert.Equal(MyEventSender, gotSender.ExpectValue());
        }
Esempio n. 13
0
        public void FlagChangeListeners()
        {
            var flagKey           = "flagKey";
            var store             = new InMemoryDataStore();
            var dataSourceUpdates = TestUtils.BasicDataSourceUpdates(store, testLogger);

            var tracker = new FlagTrackerImpl(dataSourceUpdates, null);

            var eventSink1 = new EventSink <FlagChangeEvent>();
            var eventSink2 = new EventSink <FlagChangeEvent>();
            EventHandler <FlagChangeEvent> listener1 = eventSink1.Add;
            EventHandler <FlagChangeEvent> listener2 = eventSink2.Add;

            tracker.FlagChanged += listener1;
            tracker.FlagChanged += listener2;

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

            var flagV1 = new FeatureFlagBuilder(flagKey).Version(1).Build();

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

            var event1 = eventSink1.ExpectValue();
            var event2 = eventSink2.ExpectValue();

            Assert.Equal(flagKey, event1.Key);
            Assert.Equal(flagKey, event2.Key);

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

            tracker.FlagChanged -= listener2;

            var flagV2 = new FeatureFlagBuilder(flagKey).Version(2).Build();

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

            var event3 = eventSink1.ExpectValue();

            Assert.Equal(flagKey, event3.Key);
            eventSink2.ExpectNoValue();
        }
        public void DataSourceStatusIsRestoredWhenNoLongerSetOffline()
        {
            var testData = TestData.DataSource();
            var config   = BasicConfig().DataSource(testData).Offline(true).Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                Assert.True(client.DataSourceStatusProvider.WaitFor(DataSourceState.SetOffline, TimeSpan.FromSeconds(5)));

                var statuses = new EventSink <DataSourceStatus>();
                client.DataSourceStatusProvider.StatusChanged += statuses.Add;

                client.SetOffline(false, TimeSpan.FromSeconds(1));

                var newStatus1 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Initializing, newStatus1.State);

                var newStatus2 = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Valid, newStatus2.State);
            }
        }
Esempio n. 15
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();
        }
Esempio n. 16
0
        public void SendsEvent()
        {
            var values1 = new EventSink <string>();
            var values2 = new EventSink <string>();

            myEvent += values1.Add;
            myEvent += values2.Add;

            executor.ScheduleEvent("hello", myEvent);

            Assert.Equal("hello", values1.ExpectValue());
            Assert.Equal("hello", values2.ExpectValue());
        }
Esempio n. 17
0
        public void PollingDetectsStoreUnavailability()
        {
            SetStoreTimestamp(UnixMillisecondTime.Now);

            var bsConfig = Components.BigSegments(_storeFactory)
                           .StatusPollInterval(TimeSpan.FromMilliseconds(10))
                           .StaleAfter(TimeSpan.FromDays(1))
                           .CreateBigSegmentsConfiguration(BasicContext);

            using (var sw = new BigSegmentStoreWrapper(bsConfig, BasicTaskExecutor, TestLogger))
            {
                var status1 = sw.GetStatus();
                Assert.True(status1.Available);

                var statuses = new EventSink <BigSegmentStoreStatus>();
                sw.StatusChanged += statuses.Add;

                SetStoreStatusError(new Exception("sorry"));

                var status2 = statuses.ExpectValue();
                if (status2.Available)
                {
                    // depending on timing, we might or might not receive an initial update of Available = true
                    status2 = statuses.ExpectValue();
                    Assert.False(status2.Available);
                }
                Assert.Equal(status2, sw.GetStatus());

                SetStoreTimestamp(UnixMillisecondTime.Now);

                var status3 = statuses.ExpectValue();
                Assert.True(status3.Available);
                Assert.Equal(status3, sw.GetStatus());
            }

            AssertLogMessageRegex(true, Logging.LogLevel.Error,
                                  "Big Segment store status.*Exception.*sorry");
        }
Esempio n. 18
0
        private void VerifyInvalidDataEvent(string eventName, string eventData)
        {
            var statuses = new EventSink <DataSourceStatus>();

            _dataSourceStatusProvider.StatusChanged += statuses.Add;

            VerifyEventCausesStreamRestartWithInMemoryStore(eventName, eventData);

            // We did not allow the stream to successfully process an event before causing the error, so the
            // state will still be Initializing, but we should be able to see that an error happened.
            var status = statuses.ExpectValue();

            Assert.NotNull(status.LastError);
            Assert.Equal(DataSourceStatus.ErrorKind.InvalidData, status.LastError.Value.Kind);
        }
Esempio n. 19
0
        public void ExceptionFromEventHandlerIsLoggedAndDoesNotStopOtherHandlers()
        {
            var values1 = new EventSink <string>();

            myEvent += (sender, args) => throw new Exception("sorry");
            myEvent += values1.Add;

            executor.ScheduleEvent("hello", myEvent);

            Assert.Equal("hello", values1.ExpectValue());

            AssertEventually(TimeSpan.FromSeconds(10), TimeSpan.FromMilliseconds(20), () =>
                             logCapture.HasMessageWithText(LogLevel.Error, "Unexpected exception from event handler for String: System.Exception: sorry") &&
                             logCapture.HasMessageWithRegex(LogLevel.Debug, "at LaunchDarkly.Sdk.Internal.Concurrent.TaskExecutorTest"));
        }
Esempio n. 20
0
        public void UpdateStatusBroadcastsNewStatus()
        {
            var updates  = MakeInstance();
            var statuses = new EventSink <DataSourceStatus>();

            updates.StatusChanged += statuses.Add;

            var timeBeforeUpdate = DateTime.Now;
            var errorInfo        = DataSourceStatus.ErrorInfo.FromHttpError(401);

            updates.UpdateStatus(DataSourceState.Off, errorInfo);

            var status = statuses.ExpectValue();

            Assert.Equal(DataSourceState.Off, status.State);
            Assert.InRange(status.StateSince, timeBeforeUpdate, timeBeforeUpdate.AddSeconds(1));
            Assert.Equal(errorInfo, status.LastError);
        }
Esempio n. 21
0
        public void UpdateStatusBroadcastsNewStatus()
        {
            var statuses = new EventSink <DataStoreStatus>();

            updates.StatusChanged += statuses.Add;

            var expectedStatus = new DataStoreStatus
            {
                Available     = false,
                RefreshNeeded = true
            };

            updates.UpdateStatus(expectedStatus);

            var newStatus = statuses.ExpectValue();

            Assert.Equal(expectedStatus, newStatus);
            statuses.ExpectNoValue();
        }
        public void DataSourceStatusProviderSendsStatusUpdates()
        {
            var testData = TestData.DataSource();
            var config   = BasicConfig().DataSource(testData).Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                var statuses = new EventSink <DataSourceStatus>();
                client.DataSourceStatusProvider.StatusChanged += statuses.Add;

                var errorInfo = DataSourceStatus.ErrorInfo.FromHttpError(401);
                testData.UpdateStatus(DataSourceState.Shutdown, errorInfo);

                var newStatus = statuses.ExpectValue();
                Assert.Equal(DataSourceState.Shutdown, newStatus.State);
                Assert.True(newStatus.StateSince >= errorInfo.Time);
                Assert.Equal(errorInfo, newStatus.LastError);
            }
        }
        public void Listeners()
        {
            var statuses = new EventSink <DataStoreStatus>();

            _dataStoreStatusProvider.StatusChanged += statuses.Add;

            var unwantedStatuses = new EventSink <DataStoreStatus>();

            _dataStoreStatusProvider.StatusChanged += unwantedStatuses.Add;
            _dataStoreStatusProvider.StatusChanged -= unwantedStatuses.Add; // testing that a listener can be removed

            var status = new DataStoreStatus {
                Available = false, RefreshNeeded = false
            };

            _dataStoreUpdates.UpdateStatus(status);

            Assert.Equal(status, statuses.ExpectValue());
            unwantedStatuses.ExpectNoValue();
        }
Esempio n. 24
0
        public void StatusListeners()
        {
            var statuses = new EventSink <DataSourceStatus>();

            statusProvider.StatusChanged += statuses.Add;

            var unwantedStatuses = new EventSink <DataSourceStatus>();
            EventHandler <DataSourceStatus> listener2 = unwantedStatuses.Add;

            statusProvider.StatusChanged += listener2;
            statusProvider.StatusChanged -= listener2; // testing that a listener can be unregistered

            updates.UpdateStatus(DataSourceState.Valid, null);

            var newStatus = statuses.ExpectValue();

            Assert.Equal(DataSourceState.Valid, newStatus.State);

            statuses.ExpectNoValue();
        }
Esempio n. 25
0
        public void UpdateStatusKeepsStateUnchangedIfStateWasInitializingAndNewStateIsInterrupted()
        {
            var updates = MakeInstance();

            Assert.Equal(DataSourceState.Initializing, updates.LastStatus.State);
            var originalTime = updates.LastStatus.StateSince;

            var statuses = new EventSink <DataSourceStatus>();

            updates.StatusChanged += statuses.Add;

            var errorInfo = DataSourceStatus.ErrorInfo.FromHttpError(401);

            updates.UpdateStatus(DataSourceState.Interrupted, errorInfo);

            var status = statuses.ExpectValue();

            Assert.Equal(DataSourceState.Initializing, status.State);
            Assert.Equal(originalTime, status.StateSince);
            Assert.Equal(errorInfo, status.LastError);
        }
Esempio n. 26
0
        public void EventHandlerIsCalledOnUIThread()
        {
            var td     = TestData.DataSource();
            var config = BasicConfig().DataSource(td).Build();

            var captureMainThread = new EventSink <Thread>();

            NSRunLoop.Main.BeginInvokeOnMainThread(() => captureMainThread.Enqueue(Thread.CurrentThread));
            var mainThread = captureMainThread.ExpectValue();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                var receivedOnThread = new EventSink <Thread>();
                client.FlagTracker.FlagValueChanged += (sender, args) =>
                                                       receivedOnThread.Enqueue(Thread.CurrentThread);

                td.Update(td.Flag("flagkey").Variation(true));

                var t = receivedOnThread.ExpectValue();
                Assert.Equal(mainThread, t);
            }
        }
        public void EventIsSentForDeletedFlagOnUpsert()
        {
            var events = new EventSink <FlagValueChangeEvent>();

            _flagTracker.FlagValueChanged += events.Add;

            var initData = new DataSetBuilder()
                           .Add("key1", new FeatureFlagBuilder().Version(100).Value(LdValue.Of(true)).Variation(0).Build())
                           .Add("key2", new FeatureFlagBuilder().Version(200).Value(LdValue.Of(true)).Variation(0).Build())
                           .Build();

            _updateSink.Init(_basicUser, initData);

            _updateSink.Upsert(_basicUser, "key1", new ItemDescriptor(101, null));

            var e = events.ExpectValue();

            Assert.Equal("key1", e.Key);
            Assert.Equal(LdValue.Of(true), e.OldValue);
            Assert.Equal(LdValue.Null, e.NewValue);
            Assert.True(e.Deleted);
        }
Esempio n. 28
0
        public void FlagChangeEventIsGeneratedWhenModifiedFileIsReloaded()
        {
            using (var file = TempFile.Create())
            {
                file.SetContent(@"{""flagValues"":{""flag1"":""a""}}");

                var config = BasicConfig()
                             .DataSource(FileData.DataSource().FilePaths(file.Path).AutoUpdate(true))
                             .Build();

                using (var client = new LdClient(config))
                {
                    var events = new EventSink <FlagChangeEvent>();
                    client.FlagTracker.FlagChanged += events.Add;

                    file.SetContent(@"{""flagValues"":{""flag1"":""b""}}");

                    var e = events.ExpectValue(TimeSpan.FromSeconds(5));
                    Assert.Equal("flag1", e.Key);
                    Assert.Equal("b", client.StringVariation("flag1", user, ""));
                }
            }
        }
        public void EventSenderIsClientInstance()
        {
            // We're only checking one kind of events here (FlagValueChanged), but since the SDK uses the
            // same TaskExecutor instance for all event dispatches and the sender is configured in
            // that object, the sender should be the same for all events.

            var flagKey  = "flagKey";
            var testData = TestData.DataSource();

            testData.Update(testData.Flag(flagKey).Variation(true));
            var config = BasicConfig().DataSource(testData).Build();

            using (var client = TestUtil.CreateClient(config, BasicUser))
            {
                var receivedSender = new EventSink <object>();
                client.FlagTracker.FlagValueChanged += (s, e) => receivedSender.Enqueue(s);

                testData.Update(testData.Flag(flagKey).Variation(false));

                var sender = receivedSender.ExpectValue();
                Assert.Same(client, sender);
            }
        }
        public void StatusRemainsUnavailableIfStoreSaysItIsAvailableButInitFails()
        {
            // Most of this test is identical to CacheIsWrittenToStoreAfterRecoveryIfTtlIsInfinite() except as noted below.
            using (var wrapper = MakeWrapper(CachedIndefinitely))
            {
                var dataStoreStatusProvider = new DataStoreStatusProviderImpl(wrapper, _dataStoreUpdates);

                var statuses = new EventSink <DataStoreStatus>();
                dataStoreStatusProvider.StatusChanged += statuses.Add;

                string key1 = "key1", key2 = "key2";
                var    item1 = new TestItem("name1");
                var    item2 = new TestItem("name2");

                wrapper.Init(new TestDataBuilder()
                             .Add(TestDataKind, key1, 1, item1)
                             .Build());

                // In infinite cache mode, we do *not* expect exceptions thrown by the store to be propagated; it will
                // swallow the error, but also go into polling/recovery mode. Note that even though the store rejects
                // this update, it'll still be cached.
                CauseStoreError(_core, wrapper);
                Assert.Equal(FakeError, Assert.Throws(FakeError.GetType(),
                                                      () => wrapper.Upsert(TestDataKind, key1, item1.WithVersion(2))));

                Assert.Equal(item1.WithVersion(2), wrapper.Get(TestDataKind, key1));

                var status1 = statuses.ExpectValue();
                Assert.False(status1.Available);
                Assert.False(status1.RefreshNeeded);

                // While the store is still down, try to update it again - the update goes into the cache

                Assert.Equal(FakeError, Assert.Throws(FakeError.GetType(),
                                                      () => wrapper.Upsert(TestDataKind, key2, item2.WithVersion(1))));

                Assert.Equal(item2.WithVersion(1), wrapper.Get(TestDataKind, key2));

                // Verify that this update did not go into the underlying data yet

                Assert.False(_core.Data[TestDataKind].TryGetValue(key2, out _));

                // Here's what is unique to this test: we are telling the store to report its status as "available",
                // but *not* clearing the fake exception, so when the poller tries to write the cached data with
                // init() it should fail.
                _core.Available = true;

                // We can't prove that an unwanted status transition will never happen, but we can verify that it
                // does not happen within two status poll intervals.
                Thread.Sleep(PersistentDataStoreStatusManager.PollInterval + PersistentDataStoreStatusManager.PollInterval);

                statuses.ExpectNoValue();
                Assert.InRange(_core.InitCalledCount, 2, 100); // that is, it *tried* to do at least one more init

                // Now simulate the store coming back up and actually working
                _core.Error = null;

                // Wait for the poller to notice this and publish a new status
                var status2 = statuses.ExpectValue();
                Assert.True(status2.Available);
                Assert.False(status2.RefreshNeeded);

                // Once that has happened, the cache should have been written to the store
                Assert.Equal(item1.SerializedWithVersion(2), _core.Data[TestDataKind][key1]);
                Assert.Equal(item2.SerializedWithVersion(1), _core.Data[TestDataKind][key2]);
            }
        }