231 lines
7.2 KiB
C#
231 lines
7.2 KiB
C#
using FluentAssertions;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.FeatureFlags.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public class FeatureFlagServiceCollectionExtensionsTests
|
|
{
|
|
[Fact]
|
|
public void AddFeatureFlags_RegistersFeatureFlagService()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
// Act
|
|
services.AddFeatureFlags();
|
|
var provider = services.BuildServiceProvider();
|
|
|
|
// Assert
|
|
var service = provider.GetService<IFeatureFlagService>();
|
|
service.Should().NotBeNull();
|
|
service.Should().BeOfType<CompositeFeatureFlagService>();
|
|
}
|
|
|
|
[Fact]
|
|
public void AddFeatureFlags_WithOptions_ConfiguresOptions()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
// Act
|
|
services.AddFeatureFlags(options =>
|
|
{
|
|
options.EnableCaching = true;
|
|
options.CacheDuration = TimeSpan.FromMinutes(5);
|
|
options.DefaultValue = true;
|
|
});
|
|
|
|
var provider = services.BuildServiceProvider();
|
|
var options = provider.GetRequiredService<Microsoft.Extensions.Options.IOptions<FeatureFlagOptions>>();
|
|
|
|
// Assert
|
|
options.Value.EnableCaching.Should().BeTrue();
|
|
options.Value.CacheDuration.Should().Be(TimeSpan.FromMinutes(5));
|
|
options.Value.DefaultValue.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void AddConfigurationFeatureFlags_RegistersConfigurationProvider()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
var configData = new Dictionary<string, string?>
|
|
{
|
|
{ "FeatureFlags:TestFlag", "true" }
|
|
};
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(configData)
|
|
.Build();
|
|
|
|
services.AddSingleton<IConfiguration>(configuration);
|
|
services.AddFeatureFlags();
|
|
services.AddConfigurationFeatureFlags();
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagProviders = provider.GetServices<IFeatureFlagProvider>().ToList();
|
|
|
|
// Assert
|
|
featureFlagProviders.Should().ContainSingle();
|
|
featureFlagProviders[0].Name.Should().Be("Configuration");
|
|
}
|
|
|
|
[Fact]
|
|
public void AddConfigurationFeatureFlags_WithCustomSectionName_UsesCorrectSection()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
var configData = new Dictionary<string, string?>
|
|
{
|
|
{ "CustomFlags:MyFlag", "true" }
|
|
};
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(configData)
|
|
.Build();
|
|
|
|
services.AddSingleton<IConfiguration>(configuration);
|
|
services.AddFeatureFlags();
|
|
services.AddConfigurationFeatureFlags(sectionName: "CustomFlags");
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagService = provider.GetRequiredService<IFeatureFlagService>();
|
|
var result = featureFlagService.IsEnabledAsync("MyFlag").Result;
|
|
|
|
// Assert
|
|
result.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void AddInMemoryFeatureFlags_RegistersInMemoryProvider()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
var flags = new Dictionary<string, bool>
|
|
{
|
|
{ "InMemoryFlag", true }
|
|
};
|
|
|
|
services.AddFeatureFlags();
|
|
services.AddInMemoryFeatureFlags(flags);
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagService = provider.GetRequiredService<IFeatureFlagService>();
|
|
var result = featureFlagService.IsEnabledAsync("InMemoryFlag").Result;
|
|
|
|
// Assert
|
|
result.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void AddInMemoryFeatureFlags_WithPriority_SetsCorrectPriority()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
services.AddFeatureFlags();
|
|
services.AddInMemoryFeatureFlags(new Dictionary<string, bool> { { "Flag", true } }, priority: 5);
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagProviders = provider.GetServices<IFeatureFlagProvider>().ToList();
|
|
|
|
// Assert
|
|
featureFlagProviders.Single().Priority.Should().Be(5);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddFeatureFlagProvider_RegistersCustomProvider()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
services.AddFeatureFlags();
|
|
services.AddFeatureFlagProvider<TestFeatureFlagProvider>();
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagProviders = provider.GetServices<IFeatureFlagProvider>().ToList();
|
|
|
|
// Assert
|
|
featureFlagProviders.Should().ContainSingle();
|
|
featureFlagProviders[0].Should().BeOfType<TestFeatureFlagProvider>();
|
|
}
|
|
|
|
[Fact]
|
|
public void AddFeatureFlagProvider_WithFactory_RegistersCustomProvider()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
services.AddFeatureFlags();
|
|
services.AddFeatureFlagProvider(_ => new TestFeatureFlagProvider());
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagProviders = provider.GetServices<IFeatureFlagProvider>().ToList();
|
|
|
|
// Assert
|
|
featureFlagProviders.Should().ContainSingle();
|
|
featureFlagProviders[0].Should().BeOfType<TestFeatureFlagProvider>();
|
|
}
|
|
|
|
[Fact]
|
|
public void MultipleProviders_AreRegisteredInPriorityOrder()
|
|
{
|
|
// Arrange
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
|
|
var configData = new Dictionary<string, string?>
|
|
{
|
|
{ "FeatureFlags:SharedFlag", "false" }
|
|
};
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(configData)
|
|
.Build();
|
|
|
|
services.AddSingleton<IConfiguration>(configuration);
|
|
services.AddFeatureFlags();
|
|
services.AddConfigurationFeatureFlags(priority: 50);
|
|
services.AddInMemoryFeatureFlags(new Dictionary<string, bool> { { "SharedFlag", true } }, priority: 10);
|
|
|
|
// Act
|
|
var provider = services.BuildServiceProvider();
|
|
var featureFlagService = provider.GetRequiredService<IFeatureFlagService>();
|
|
var result = featureFlagService.IsEnabledAsync("SharedFlag").Result;
|
|
|
|
// Assert - InMemory has lower priority number (higher precedence), so it wins
|
|
result.Should().BeTrue();
|
|
}
|
|
|
|
private class TestFeatureFlagProvider : FeatureFlagProviderBase
|
|
{
|
|
public override string Name => "Test";
|
|
|
|
public override Task<FeatureFlagResult?> TryGetFlagAsync(
|
|
string flagKey,
|
|
FeatureFlagEvaluationContext context,
|
|
CancellationToken ct = default)
|
|
{
|
|
return Task.FromResult<FeatureFlagResult?>(
|
|
new FeatureFlagResult(flagKey, true, null, "Test", Name));
|
|
}
|
|
}
|
|
}
|