From b31d36c61569750d28b7e416d54dcc6f7d726178 Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Thu, 15 May 2025 13:45:18 +0800 Subject: [PATCH 1/4] add testcase for refresh activity --- .../RefreshTests.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/Tests.AzureAppConfiguration/RefreshTests.cs b/tests/Tests.AzureAppConfiguration/RefreshTests.cs index c27a48ff8..911791a18 100644 --- a/tests/Tests.AzureAppConfiguration/RefreshTests.cs +++ b/tests/Tests.AzureAppConfiguration/RefreshTests.cs @@ -12,6 +12,7 @@ using Moq; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -1218,6 +1219,65 @@ MockAsyncPageable GetTestKeys(SettingSelector selector, CancellationToken ct) Assert.Null(config["FeatureManagement:MyFeature"]); } + [Fact] + public async Task RefreshTests_StartsRefreshActivity() + { + var _activities = new List(); + var _activityListener = new ActivityListener + { + ShouldListenTo = source => source.Name == "Microsoft.Extensions.Configuration.AzureAppConfiguration", + Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, + ActivityStarted = activity => _activities.Add(activity), + }; + ActivitySource.AddActivityListener(_activityListener); + + IConfigurationRefresher refresher = null; + var mockClient = GetMockConfigurationClient(); + + var mockAsyncPageable = new MockAsyncPageable(_kvCollection); + + mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Callback(() => mockAsyncPageable.UpdateCollection(_kvCollection)) + .Returns(mockAsyncPageable); + + var config = new ConfigurationBuilder() + .AddAzureAppConfiguration(options => + { + options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object); + options.Select("TestKey*", "label"); + options.ConfigurationSettingPageIterator = new MockConfigurationSettingPageIterator(); + options.ConfigureRefresh(refreshOptions => + { + refreshOptions.RegisterAll() + .SetRefreshInterval(TimeSpan.FromSeconds(1)); + }); + + refresher = options.GetRefresher(); + }) + .Build(); + + Assert.Single(_activities); + Assert.Contains(_activities, a => a.OperationName == "Load"); + + // Wait for the cache to expire + await Task.Delay(1500); + await refresher.RefreshAsync(); + + Assert.Equal(2, _activities.Count); + Assert.Equal("Refresh", _activities.Last().OperationName); + + await refresher.RefreshAsync(); + Assert.Equal(2, _activities.Count); // only start refresh activity when real refresh happens + + // Wait for the cache to expire + await Task.Delay(1500); + await refresher.RefreshAsync(); + Assert.Equal(3, _activities.Count); + Assert.Equal("Refresh", _activities.Last().OperationName); + + _activityListener.Dispose(); + } + #if NET8_0 [Fact] public void RefreshTests_ChainedConfigurationProviderUsedAsRootForRefresherProvider() From 655f4debc4a49ec5c9813abb210487681a916e23 Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Wed, 21 May 2025 13:08:43 +0800 Subject: [PATCH 2/4] update --- .../AzureAppConfigurationOptions.cs | 5 +++++ .../AzureAppConfigurationProvider.cs | 10 ++++++---- tests/Tests.AzureAppConfiguration/RefreshTests.cs | 5 ++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs index bb48372ab..e5b6e2585 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs @@ -119,6 +119,11 @@ internal IEnumerable Adapters /// internal IConfigurationSettingPageIterator ConfigurationSettingPageIterator { get; set; } + /// + /// For use in tests only. An optional activity source name to specify the activity source used by the configuration provider. + /// + internal string ActivitySourceName { get; set; } + /// /// An optional timespan value to set the minimum backoff duration to a value other than the default. /// diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs index a83c74135..1ae46018c 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs @@ -23,7 +23,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigurationRefresher, IDisposable { - private readonly ActivitySource _activitySource = new ActivitySource(ActivityNames.AzureAppConfigurationActivitySource); + private readonly ActivitySource _activitySource; private bool _optional; private bool _isInitialLoadComplete = false; private bool _isAssemblyInspected; @@ -151,6 +151,8 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan { SetRequestTracingOptions(); } + + _activitySource = new ActivitySource(options.ActivitySourceName ?? ActivityNames.AzureAppConfigurationActivitySource); } /// @@ -159,7 +161,7 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan public override void Load() { var watch = Stopwatch.StartNew(); - using Activity activity = _activitySource.StartActivity(ActivityNames.Load); + using Activity activity = _activitySource?.StartActivity(ActivityNames.Load); try { using var startupCancellationTokenSource = new CancellationTokenSource(_options.Startup.Timeout); @@ -259,7 +261,7 @@ public async Task RefreshAsync(CancellationToken cancellationToken) return; } - using Activity activity = _activitySource.StartActivity(ActivityNames.Refresh); + using Activity activity = _activitySource?.StartActivity(ActivityNames.Refresh); // Check if initial configuration load had failed if (_mappedData == null) { @@ -1422,7 +1424,7 @@ private async Task ProcessKeyValueChangesAsync( public void Dispose() { (_configClientManager as ConfigurationClientManager)?.Dispose(); - _activitySource.Dispose(); + _activitySource?.Dispose(); } } } diff --git a/tests/Tests.AzureAppConfiguration/RefreshTests.cs b/tests/Tests.AzureAppConfiguration/RefreshTests.cs index 911791a18..08a414780 100644 --- a/tests/Tests.AzureAppConfiguration/RefreshTests.cs +++ b/tests/Tests.AzureAppConfiguration/RefreshTests.cs @@ -1222,10 +1222,12 @@ MockAsyncPageable GetTestKeys(SettingSelector selector, CancellationToken ct) [Fact] public async Task RefreshTests_StartsRefreshActivity() { + string activitySourceName = Guid.NewGuid().ToString(); + var _activities = new List(); var _activityListener = new ActivityListener { - ShouldListenTo = source => source.Name == "Microsoft.Extensions.Configuration.AzureAppConfiguration", + ShouldListenTo = source => source.Name == activitySourceName, Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, ActivityStarted = activity => _activities.Add(activity), }; @@ -1251,6 +1253,7 @@ public async Task RefreshTests_StartsRefreshActivity() refreshOptions.RegisterAll() .SetRefreshInterval(TimeSpan.FromSeconds(1)); }); + options.ActivitySourceName = activitySourceName; refresher = options.GetRefresher(); }) From fc5499afe9e4d5cda1c73ed401b8a22998beb00f Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Wed, 21 May 2025 13:16:04 +0800 Subject: [PATCH 3/4] update --- tests/Tests.AzureAppConfiguration/Tests.cs | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Tests.AzureAppConfiguration/Tests.cs b/tests/Tests.AzureAppConfiguration/Tests.cs index 30010f7d7..1f2ab5fa3 100644 --- a/tests/Tests.AzureAppConfiguration/Tests.cs +++ b/tests/Tests.AzureAppConfiguration/Tests.cs @@ -10,8 +10,10 @@ using Moq; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Xunit; namespace Tests.AzureAppConfiguration @@ -346,5 +348,35 @@ public void TestKeepSelectorPrecedenceAfterDedup() Assert.True(config["message"] == "message from dev label"); } + + [Fact] + public void TestActivitySource() + { + var _activities = new List(); + var _activityListener = new ActivityListener + { + ShouldListenTo = source => source.Name == "Microsoft.Extensions.Configuration.AzureAppConfiguration", + Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, + ActivityStarted = activity => _activities.Add(activity), + }; + ActivitySource.AddActivityListener(_activityListener); + + var mockResponse = new Mock(); + var mockClient = new Mock(MockBehavior.Strict); + + mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns(new MockAsyncPageable(_kvCollectionPageOne)); + + mockClient.Setup(c => c.GetConfigurationSettingAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Response.FromValue(_kv, mockResponse.Object)); + + var config = new ConfigurationBuilder() + .AddAzureAppConfiguration(options => options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object)) + .Build(); + + Assert.Contains(_activities, a => a.OperationName == "Load"); + + _activityListener.Dispose(); + } } } From 9c201ef4fbb55c9b8355547627b47d71f05c480c Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Fri, 27 Jun 2025 12:52:35 +0800 Subject: [PATCH 4/4] update --- tests/Tests.AzureAppConfiguration/{ => Unit}/HealthCheckTest.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Tests.AzureAppConfiguration/{ => Unit}/HealthCheckTest.cs (100%) diff --git a/tests/Tests.AzureAppConfiguration/HealthCheckTest.cs b/tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs similarity index 100% rename from tests/Tests.AzureAppConfiguration/HealthCheckTest.cs rename to tests/Tests.AzureAppConfiguration/Unit/HealthCheckTest.cs