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 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(); }
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()); }
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); }
public void NoEventsAreSentForUpsertIfNeverInited() { var events = new EventSink <FlagValueChangeEvent>(); _flagTracker.FlagValueChanged += events.Add; _updateSink.Upsert(_basicUser, "key1", new FeatureFlagBuilder().Build().ToItemDescriptor()); events.ExpectNoValue(); }
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 UpdateStatusDoesNothingIfParametersHaveNoNewData() { var updates = MakeInstance(); var statuses = new EventSink <DataSourceStatus>(); updates.StatusChanged += statuses.Add; updates.UpdateStatus(DataSourceState.Initializing, null); statuses.ExpectNoValue(); }
public void NoEventsAreSentForFirstInit() { var events = new EventSink <FlagValueChangeEvent>(); _flagTracker.FlagValueChanged += events.Add; var initData = new DataSetBuilder().Add("key1", new FeatureFlagBuilder().Build()).Build(); _updateSink.Init(_basicUser, initData); events.ExpectNoValue(); }
public void UpdateStatusDoesNothingIfNewStatusIsSame() { var statuses = new EventSink <DataStoreStatus>(); updates.StatusChanged += statuses.Add; updates.UpdateStatus(new DataStoreStatus { Available = true, RefreshNeeded = false }); statuses.ExpectNoValue(); }
public void DoesNotSendEventOnUpdateIfItemWasNotReallyUpdated() { var dataBuilder = new DataSetBuilder().Flags(flag1, flag2); var updates = MakeInstance(); updates.Init(dataBuilder.Build()); var eventSink = new EventSink <FlagChangeEvent>(); updates.FlagChanged += eventSink.Add; updates.Upsert(DataModel.Features, flag2.Key, DescriptorOf(flag2)); eventSink.ExpectNoValue(); }
public void EventIsNotSentIfUpsertFailsDueToLowerVersion() { 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 FeatureFlagBuilder().Version(99).Value(LdValue.Of(false)).Variation(1).Build().ToItemDescriptor()); events.ExpectNoValue(); }
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(); }
private void ExpectFlagChangeEvents(EventSink <FlagChangeEvent> eventSink, params string[] flagKeys) { var expectedChangedFlagKeys = new HashSet <string>(flagKeys); var actualChangedFlagKeys = new HashSet <string>(); for (var i = 0; i < flagKeys.Length; i++) { if (eventSink.TryTakeValue(out var e)) { actualChangedFlagKeys.Add(e.Key); } else { Assert.True(false, string.Format("expected flag change events: [{0}] but only got: [{1}]", string.Join(", ", expectedChangedFlagKeys), string.Join(", ", actualChangedFlagKeys))); } } Assert.Equal(expectedChangedFlagKeys, actualChangedFlagKeys); eventSink.ExpectNoValue(); }
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(); }
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(); }
public void ExpectNoMoreActions() => Actions.ExpectNoValue();
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]); } }