using FluentAssertions; using Xunit; namespace StellaOps.FeatureFlags.Tests; [Trait("Category", "Unit")] public class InMemoryFeatureFlagProviderTests { [Fact] public void Name_ReturnsInMemory() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Assert provider.Name.Should().Be("InMemory"); } [Fact] public void Priority_ReturnsConfiguredValue() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary(), priority: 5); // Assert provider.Priority.Should().Be(5); } [Fact] public async Task TryGetFlagAsync_ReturnsTrueForEnabledFlag() { // Arrange var flags = new Dictionary { { "enabled-flag", true } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act var result = await provider.TryGetFlagAsync("enabled-flag", FeatureFlagEvaluationContext.Empty); // Assert result.Should().NotBeNull(); result!.Enabled.Should().BeTrue(); result.Key.Should().Be("enabled-flag"); } [Fact] public async Task TryGetFlagAsync_ReturnsFalseForDisabledFlag() { // Arrange var flags = new Dictionary { { "disabled-flag", false } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act var result = await provider.TryGetFlagAsync("disabled-flag", FeatureFlagEvaluationContext.Empty); // Assert result.Should().NotBeNull(); result!.Enabled.Should().BeFalse(); } [Fact] public async Task TryGetFlagAsync_ReturnsNullForUnknownFlag() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Act var result = await provider.TryGetFlagAsync("unknown", FeatureFlagEvaluationContext.Empty); // Assert result.Should().BeNull(); } [Fact] public async Task TryGetFlagAsync_IsCaseInsensitive() { // Arrange var flags = new Dictionary { { "MyFlag", true } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act var result = await provider.TryGetFlagAsync("myflag", FeatureFlagEvaluationContext.Empty); // Assert result.Should().NotBeNull(); result!.Enabled.Should().BeTrue(); } [Fact] public void SetFlag_AddsNewFlag() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Act provider.SetFlag("new-flag", true); var result = provider.TryGetFlagAsync("new-flag", FeatureFlagEvaluationContext.Empty).Result; // Assert result.Should().NotBeNull(); result!.Enabled.Should().BeTrue(); } [Fact] public void SetFlag_UpdatesExistingFlag() { // Arrange var flags = new Dictionary { { "toggle-flag", false } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act provider.SetFlag("toggle-flag", true); var result = provider.TryGetFlagAsync("toggle-flag", FeatureFlagEvaluationContext.Empty).Result; // Assert result.Should().NotBeNull(); result!.Enabled.Should().BeTrue(); } [Fact] public void SetFlag_WithVariant_SetsVariantValue() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Act provider.SetFlag("variant-flag", true, "blue"); var result = provider.TryGetFlagAsync("variant-flag", FeatureFlagEvaluationContext.Empty).Result; // Assert result.Should().NotBeNull(); result!.Variant.Should().Be("blue"); } [Fact] public void RemoveFlag_RemovesExistingFlag() { // Arrange var flags = new Dictionary { { "to-remove", true } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act provider.RemoveFlag("to-remove"); var result = provider.TryGetFlagAsync("to-remove", FeatureFlagEvaluationContext.Empty).Result; // Assert result.Should().BeNull(); } [Fact] public void RemoveFlag_DoesNotThrowForNonexistentFlag() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Act & Assert provider.Invoking(p => p.RemoveFlag("nonexistent")) .Should().NotThrow(); } [Fact] public void Clear_RemovesAllFlags() { // Arrange var flags = new Dictionary { { "flag-1", true }, { "flag-2", false } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act provider.Clear(); var result1 = provider.TryGetFlagAsync("flag-1", FeatureFlagEvaluationContext.Empty).Result; var result2 = provider.TryGetFlagAsync("flag-2", FeatureFlagEvaluationContext.Empty).Result; // Assert result1.Should().BeNull(); result2.Should().BeNull(); } [Fact] public async Task ListFlagsAsync_ReturnsAllFlags() { // Arrange var flags = new Dictionary { { "flag-a", true }, { "flag-b", false }, { "flag-c", true } }; var provider = new InMemoryFeatureFlagProvider(flags); // Act var result = await provider.ListFlagsAsync(); // Assert result.Should().HaveCount(3); result.Select(f => f.Key).Should().Contain(["flag-a", "flag-b", "flag-c"]); } [Fact] public async Task ListFlagsAsync_ReturnsEmptyWhenNoFlags() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); // Act var result = await provider.ListFlagsAsync(); // Assert result.Should().BeEmpty(); } [Fact] public async Task ListFlagsAsync_ReflectsCurrentStateAfterModifications() { // Arrange var provider = new InMemoryFeatureFlagProvider(new Dictionary()); provider.SetFlag("dynamic-flag", true); // Act var result = await provider.ListFlagsAsync(); // Assert result.Should().HaveCount(1); result.Single().Key.Should().Be("dynamic-flag"); } }