diff --git a/tests/Tests.FeatureManagement/FeatureManagementTest.cs b/tests/Tests.FeatureManagement/FeatureManagementTest.cs index 26c083db..fe2e2ac5 100644 --- a/tests/Tests.FeatureManagement/FeatureManagementTest.cs +++ b/tests/Tests.FeatureManagement/FeatureManagementTest.cs @@ -849,6 +849,13 @@ public async Task TimeWindow() Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Start", DateTimeOffset.UtcNow.AddDays(-2).ToString("r")); Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:End", DateTimeOffset.UtcNow.AddDays(-1).ToString("r")); Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:Type", "Weekly"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:0", "Monday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:1", "Tuesday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:2", "Wednesday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:3", "Thursday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:4", "Friday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:5", "Saturday"); + Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Pattern:DaysOfWeek:6", "Sunday"); Environment.SetEnvironmentVariable("feature_management:feature_flags:6:conditions:client_filters:0:parameters:Recurrence:Range:Type", "NoEnd"); foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek))) @@ -874,11 +881,7 @@ public async Task TimeWindow() Assert.False(await featureManager.IsEnabledAsync(feature4)); Assert.True(await featureManager.IsEnabledAsync(feature5)); Assert.False(await featureManager.IsEnabledAsync(feature6)); - - for (int i = 0; i < 10; i++) - { - Assert.True(await featureManager.IsEnabledAsync(feature7)); - } + Assert.True(await featureManager.IsEnabledAsync(feature7)); } [Fact] diff --git a/tests/Tests.FeatureManagement/RecurrenceEvaluation.cs b/tests/Tests.FeatureManagement/RecurrenceEvaluation.cs index c65c1abb..4519456e 100644 --- a/tests/Tests.FeatureManagement/RecurrenceEvaluation.cs +++ b/tests/Tests.FeatureManagement/RecurrenceEvaluation.cs @@ -1617,17 +1617,17 @@ public void FindWeeklyClosestStartTest() [Fact] public async Task RecurrenceEvaluationThroughCacheTest() { - OnDemandClock mockedTimeProvider = new OnDemandClock(); + var mockedTimeProvider = new OnDemandClock(); - var mockedTimeWindowFilter = new TimeWindowFilter() + using (var cache = new TestCache()) { - Cache = new MemoryCache(new MemoryCacheOptions()), - SystemClock = mockedTimeProvider - }; + var mockedTimeWindowFilter = new TimeWindowFilter() + { + Cache = cache, + SystemClock = mockedTimeProvider + }; - var context = new FeatureFilterEvaluationContext() - { - Settings = new TimeWindowFilterSettings() + TimeWindowFilterSettings settings = new TimeWindowFilterSettings() { Start = DateTimeOffset.Parse("2024-2-1T00:00:00+08:00"), // Thursday End = DateTimeOffset.Parse("2024-2-1T12:00:00+08:00"), @@ -1644,43 +1644,80 @@ public async Task RecurrenceEvaluationThroughCacheTest() EndDate = DateTimeOffset.Parse("2024-2-5T12:00:00+08:00") } } - } - }; + }; - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-2T23:00:00+08:00"); + var context = new FeatureFilterEvaluationContext() + { + Settings = settings + }; - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + DateTimeOffset? closestStart; + Assert.False(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(0, cache.CountOfEntryCreation); - for (int i = 0; i < 12; i++) - { - mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddHours(1); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); - } + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-2T23:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-3T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-3T11:59:59+08:00"); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + for (int i = 0; i < 12; i++) + { + mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddHours(1); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-3T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); + } - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-3T12:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-3T11:59:59+08:00"); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-3T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-5T00:00:00+08:00"); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-3T12:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-5T00:00:00+08:00"), closestStart); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-5T12:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-5T00:00:00+08:00"); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-5T00:00:00+08:00"), closestStart); + Assert.Equal(2, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-7T00:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-5T12:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); - for (int i = 0; i < 10; i++) - { - mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddDays(1); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-7T00:00:00+08:00"); Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); + + for (int i = 0; i < 10; i++) + { + mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddDays(1); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); + } } - context = new FeatureFilterEvaluationContext() + using (var cache = new TestCache()) { - Settings = new TimeWindowFilterSettings() + var mockedTimeWindowFilter = new TimeWindowFilter() + { + Cache = cache, + SystemClock = mockedTimeProvider + }; + + var settings = new TimeWindowFilterSettings() { Start = DateTimeOffset.Parse("2024-2-1T00:00:00+08:00"), // Thursday End = DateTimeOffset.Parse("2024-2-1T12:00:00+08:00"), @@ -1698,43 +1735,82 @@ public async Task RecurrenceEvaluationThroughCacheTest() NumberOfOccurrences = 2 } } - } - }; + }; - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-1-31T23:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + var context = new FeatureFilterEvaluationContext() + { + Settings = settings + }; - for (int i = 0; i < 12; i++) - { - mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddHours(1); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); - } + DateTimeOffset? closestStart; + Assert.False(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(0, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-1T11:59:59+08:00"); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-1-31T23:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-1T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-1T12:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + for (int i = 0; i < 12; i++) + { + mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddHours(1); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-1T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); + } - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-2T00:00:00+08:00"); // Friday - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-1T11:59:59+08:00"); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-1T00:00:00+08:00"), closestStart); + Assert.Equal(1, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"); // Sunday - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-1T12:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"), closestStart); + Assert.Equal(2, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T06:00:00+08:00"); - Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-2T00:00:00+08:00"); // Friday + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"), closestStart); + Assert.Equal(2, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T12:01:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"); // Sunday + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"), closestStart); + Assert.Equal(2, cache.CountOfEntryCreation); - mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-8T00:00:00+08:00"); - Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T06:00:00+08:00"); + Assert.True(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Equal(DateTimeOffset.Parse("2024-2-4T00:00:00+08:00"), closestStart); + Assert.Equal(2, cache.CountOfEntryCreation); - for (int i = 0; i < 10; i++) - { - mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddDays(1); + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-4T12:01:00+08:00"); Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); + + mockedTimeProvider.UtcNow = DateTimeOffset.Parse("2024-2-8T00:00:00+08:00"); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); + + for (int i = 0; i < 10; i++) + { + mockedTimeProvider.UtcNow = mockedTimeProvider.UtcNow.AddDays(1); + Assert.False(await mockedTimeWindowFilter.EvaluateAsync(context)); + Assert.True(cache.TryGetValue(settings, out closestStart)); + Assert.Null(closestStart); + Assert.Equal(3, cache.CountOfEntryCreation); + } } } } diff --git a/tests/Tests.FeatureManagement/TestCache.cs b/tests/Tests.FeatureManagement/TestCache.cs new file mode 100644 index 00000000..39a02655 --- /dev/null +++ b/tests/Tests.FeatureManagement/TestCache.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +using Microsoft.Extensions.Caching.Memory; + +namespace Tests.FeatureManagement +{ + class TestCache : IMemoryCache + { + private readonly IMemoryCache _cache; + private int _countOfEntryCreation; + + public TestCache() + { + _cache = new MemoryCache(new MemoryCacheOptions()); + } + + public int CountOfEntryCreation + { + get => _countOfEntryCreation; + } + + public bool TryGetValue(object key, out object value) + { + return _cache.TryGetValue(key, out value); + } + + public ICacheEntry CreateEntry(object key) + { + _countOfEntryCreation += 1; + + return _cache.CreateEntry(key); + } + + public void Remove(object key) + { + _cache.Remove(key); + } + + public void Dispose() + { + _cache.Dispose(); + } + } +}