Files
git.stella-ops.org/src/__Libraries/StellaOps.FeatureFlags.Tests/FeatureFlagServiceCollectionExtensionsTests.cs
2026-01-13 18:53:39 +02:00

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));
}
}
}