diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs index 28b6749c..572a8b4e 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs @@ -95,7 +95,7 @@ public static async Task HaveCollectionsChanged( LabelFilter = keyValueSelector.LabelFilter }; - AsyncPageable pageable = client.GetConfigurationSettingsAsync(selector, cancellationToken); + AsyncPageable pageable = client.CheckConfigurationSettingsAsync(selector, cancellationToken); using IEnumerator existingPageWatcherEnumerator = pageWatchers.GetEnumerator(); diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj index 9a148c8f..f247af3e 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj @@ -1,4 +1,4 @@ - + @@ -15,7 +15,7 @@ - + diff --git a/tests/Tests.AzureAppConfiguration/Unit/AfdTests.cs b/tests/Tests.AzureAppConfiguration/Unit/AfdTests.cs index f19c93e9..1d225029 100644 --- a/tests/Tests.AzureAppConfiguration/Unit/AfdTests.cs +++ b/tests/Tests.AzureAppConfiguration/Unit/AfdTests.cs @@ -242,10 +242,12 @@ public async Task AfdTests_RegisterAllRefresh() var mockAsyncPageable4 = new MockAsyncPageable(keyValueCollection3, null, 3, responses4); mockClient.SetupSequence(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) - .Returns(mockAsyncPageable1) - .Returns(mockAsyncPageable2) - .Returns(mockAsyncPageable3) - .Returns(mockAsyncPageable4); + .Returns(mockAsyncPageable1) // initial load + .Returns(mockAsyncPageable3); // reload after change detected + + mockClient.SetupSequence(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns(mockAsyncPageable2) // first check - stale, should not refresh + .Returns(mockAsyncPageable3); // second check - should trigger refresh var afdEndpoint = new Uri("https://test.b01.azurefd.net"); IConfigurationRefresher refresher = null; @@ -381,10 +383,12 @@ public async Task AfdTests_FeatureFlagsRefresh() mockClient.SetupSequence(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Returns(mockAsyncPageable1) // default load configuration settings .Returns(mockAsyncPageable1) // load feature flag + .Returns(mockAsyncPageable3) // reload after change detected + .Returns(mockAsyncPageable3); // reload feature flags + + mockClient.SetupSequence(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Returns(mockAsyncPageable2) // watch request, should not trigger refresh - .Returns(mockAsyncPageable3) // watch request, should trigger refresh - .Returns(mockAsyncPageable3) // default load configuration settings - .Returns(mockAsyncPageable3); // load feature flag + .Returns(mockAsyncPageable3); // watch request, should trigger refresh var afdEndpoint = new Uri("https://test.b01.azurefd.net"); IConfigurationRefresher refresher = null; diff --git a/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs index de649a0d..286cf90c 100644 --- a/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/Unit/FeatureManagementTests.cs @@ -777,6 +777,10 @@ public async Task WatchesFeatureFlags() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + IConfigurationRefresher refresher = null; var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => @@ -849,6 +853,10 @@ public async Task WatchesFeatureFlagsUsingCacheExpirationInterval() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + var cacheExpirationInterval = TimeSpan.FromSeconds(1); IConfigurationRefresher refresher = null; @@ -923,6 +931,10 @@ public async Task SkipRefreshIfRefreshIntervalHasNotElapsed() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + IConfigurationRefresher refresher = null; var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => @@ -994,6 +1006,10 @@ public async Task SkipRefreshIfCacheNotExpired() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + IConfigurationRefresher refresher = null; var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => @@ -1118,6 +1134,10 @@ public async Task DoesNotUseEtagForFeatureFlagRefresh() .Callback(() => mockAsyncPageable.UpdateCollection(new List { _kv })) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(new List { _kv })) + .Returns(mockAsyncPageable); + IConfigurationRefresher refresher = null; var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => @@ -1134,7 +1154,8 @@ public async Task DoesNotUseEtagForFeatureFlagRefresh() Thread.Sleep(RefreshInterval); await refresher.TryRefreshAsync(); - mockClient.Verify(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny()), Times.Exactly(3)); + mockClient.Verify(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny()), Times.Exactly(2)); + mockClient.Verify(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny()), Times.Once()); } [Fact] @@ -1569,6 +1590,12 @@ public async Task DifferentCacheExpirationsForMultipleFeatureFlagRegistrations() (s.Key.StartsWith(FeatureManagementConstants.FeatureFlagMarker + prefix2) && s.Label == label2 && s.Key != FeatureManagementConstants.FeatureFlagMarker + "App2_Feature3")).ToList())) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlagCollection.Where(s => + (s.Key.StartsWith(FeatureManagementConstants.FeatureFlagMarker + prefix1) && s.Label == label1) || + (s.Key.StartsWith(FeatureManagementConstants.FeatureFlagMarker + prefix2) && s.Label == label2 && s.Key != FeatureManagementConstants.FeatureFlagMarker + "App2_Feature3")).ToList())) + .Returns(mockAsyncPageable); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { @@ -1739,6 +1766,11 @@ public async Task SelectAndRefreshSingleFeatureFlag() s.Key.Equals(FeatureManagementConstants.FeatureFlagMarker + prefix1) && s.Label == label1).ToList())) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlagCollection.Where(s => + s.Key.Equals(FeatureManagementConstants.FeatureFlagMarker + prefix1) && s.Label == label1).ToList())) + .Returns(mockAsyncPageable); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { @@ -1802,6 +1834,10 @@ public async Task ValidateCorrectFeatureFlagLoggedIfModifiedOrRemovedDuringRefre .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync((Func>)GetIfChanged); @@ -1886,6 +1922,10 @@ public async Task ValidateFeatureFlagsUnchangedLogged() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync((Func>)GetIfChanged); @@ -1964,6 +2004,10 @@ public async Task MapTransformFeatureFlagWithRefresh() .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(featureFlags)) + .Returns(mockAsyncPageable); + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync((Func>)GetIfChanged); diff --git a/tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs b/tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs index 4484def0..d99a6d28 100644 --- a/tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs +++ b/tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs @@ -68,6 +68,10 @@ public async Task HealthCheckTests_ReturnsUnhealthyWhenRefreshFailed() mockClient.SetupSequence(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Returns(new MockAsyncPageable(kvCollection)) + .Returns(new MockAsyncPageable(Enumerable.Empty().ToList())) + .Returns(new MockAsyncPageable(Enumerable.Empty().ToList())); + + mockClient.SetupSequence(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Throws(new RequestFailedException(503, "Request failed.")) .Returns(new MockAsyncPageable(Enumerable.Empty().ToList())) .Returns(new MockAsyncPageable(Enumerable.Empty().ToList())); @@ -145,7 +149,9 @@ public async Task HealthCheckTests_ShouldRespectHealthCheckRegistration() var mockClient = new Mock(MockBehavior.Strict); mockClient.SetupSequence(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) - .Returns(new MockAsyncPageable(kvCollection)) + .Returns(new MockAsyncPageable(kvCollection)); + + mockClient.SetupSequence(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Throws(new RequestFailedException(503, "Request failed.")); var config = new ConfigurationBuilder() diff --git a/tests/Tests.AzureAppConfiguration/Unit/RefreshTests.cs b/tests/Tests.AzureAppConfiguration/Unit/RefreshTests.cs index 25d57baa..3e69211a 100644 --- a/tests/Tests.AzureAppConfiguration/Unit/RefreshTests.cs +++ b/tests/Tests.AzureAppConfiguration/Unit/RefreshTests.cs @@ -1128,6 +1128,10 @@ public async Task RefreshTests_SelectedKeysRefreshWithRegisterAll() .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection)) + .Returns(mockAsyncPageable); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { @@ -1218,6 +1222,9 @@ MockAsyncPageable GetTestKeys(SettingSelector selector, CancellationToken ct) mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Returns((Func)GetTestKeys); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns((Func)GetTestKeys); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { @@ -1303,6 +1310,10 @@ public async Task RefreshTests_StartsRefreshActivity() .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection)) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection)) + .Returns(mockAsyncPageable); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { @@ -1417,6 +1428,12 @@ Response GetIfChanged(ConfigurationSetting setting, bool o return new MockAsyncPageable(_kvCollection.Select(setting => TestHelpers.CloneSetting(setting)).ToList()); }); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns(() => + { + return new MockAsyncPageable(_kvCollection.Select(setting => TestHelpers.CloneSetting(setting)).ToList()); + }); + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync((Func>)GetTestKey); @@ -1456,6 +1473,9 @@ Response GetIfChanged(ConfigurationSetting setting, bool o mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) .Returns((Func)GetTestKeys); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns((Func)GetTestKeys); + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync((Func>)GetTestKey); diff --git a/tests/Tests.AzureAppConfiguration/Unit/TagFiltersTests.cs b/tests/Tests.AzureAppConfiguration/Unit/TagFiltersTests.cs index f0e4e263..4ebbffd1 100644 --- a/tests/Tests.AzureAppConfiguration/Unit/TagFiltersTests.cs +++ b/tests/Tests.AzureAppConfiguration/Unit/TagFiltersTests.cs @@ -532,6 +532,11 @@ public async Task TagFiltersTests_BasicRefresh() kv.Tags.ContainsKey("Environment") && kv.Tags["Environment"] == "Development"))) .Returns(mockAsyncPageable); + mockClient.Setup(c => c.CheckConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection.FindAll(kv => + kv.Tags.ContainsKey("Environment") && kv.Tags["Environment"] == "Development"))) + .Returns(mockAsyncPageable); + var config = new ConfigurationBuilder() .AddAzureAppConfiguration(options => {