diff --git a/CHANGELOG.md b/CHANGELOG.md index 2acd81c..41e838c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.1] + +- Fixed issue with empty string value sent to IsIn condition evaluation. + ## [1.0.0] - Fully implemented version 0.5.2 of the GrowthBook SDK spec. diff --git a/GrowthBook.Tests/CustomTests/RegressionTests.cs b/GrowthBook.Tests/CustomTests/RegressionTests.cs new file mode 100644 index 0000000..d6b6580 --- /dev/null +++ b/GrowthBook.Tests/CustomTests/RegressionTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json; +using Xunit; + +namespace GrowthBook.Tests.CustomTests; + +public class RegressionTests : UnitTest +{ + [Fact] + public void EvalIsInConditionShouldNotDifferWhenAttributeIsEmptyInsteadOfNull() + { + var featureJson = """ + { + "defaultValue": false, + "rules": [ + { + "condition": { + "userId": { + "$in": [ + "ac1", + "ac2", + "ac3" + ] + } + }, + "force": true + } + ] + } + """; + + var feature = JsonConvert.DeserializeObject(featureJson); + + var staticFeatures = new Dictionary + { + ["test-in-op"] = feature + }; + + var context = new Context + { + Features = staticFeatures + }; + + var growthBook = new GrowthBook(context); + + // The initial evaluation will use a null userId value. + + var flag = growthBook.IsOn("test-in-op"); + flag.Should().BeFalse("because the userId property in the attributes JSON is null"); + + // Try again with a userId that is an empty string instead. + + context.Attributes.Add("userId", string.Empty); + + var errorFlag = growthBook.IsOn("test-in-op"); + errorFlag.Should().BeFalse("because an empty string is considered falsy and should not differ from the null case"); + } +} diff --git a/GrowthBook/Extensions/JsonExtensions.cs b/GrowthBook/Extensions/JsonExtensions.cs index 5c1f5a2..972491f 100644 --- a/GrowthBook/Extensions/JsonExtensions.cs +++ b/GrowthBook/Extensions/JsonExtensions.cs @@ -22,6 +22,13 @@ internal static class JsonExtensions /// True if null, false otherwise. public static bool IsNull(this JToken token) => token is null || token.Type == JTokenType.Null; + /// + /// Determines whether the is either null, , an empty string, or whitespace. + /// + /// The JSON token to verify. + /// True if null, empty, or whitespace, false otherwise. + public static bool IsNullOrWhitespace(this JToken token) => token.IsNull() || token.ToString().IsNullOrWhitespace(); + /// /// Gets the value of the named attribute key within the current . /// diff --git a/GrowthBook/GrowthBook.csproj b/GrowthBook/GrowthBook.csproj index e53a616..681ab58 100644 --- a/GrowthBook/GrowthBook.csproj +++ b/GrowthBook/GrowthBook.csproj @@ -11,7 +11,7 @@ https://github.com/growthbook/growthbook-csharp.git git GrowthBook,A/B test,feature toggle,flag - 1.0.0 + 1.0.1 GrowthBook C# SDK https://www.growthbook.io/ diff --git a/GrowthBook/Providers/ConditionEvaluationProvider.cs b/GrowthBook/Providers/ConditionEvaluationProvider.cs index 18c89ef..4f222ea 100644 --- a/GrowthBook/Providers/ConditionEvaluationProvider.cs +++ b/GrowthBook/Providers/ConditionEvaluationProvider.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using GrowthBook.Extensions; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; @@ -383,7 +384,7 @@ private bool IsIn(JToken conditionValue, JToken actualValue) return true; } - if (conditionValue is null || actualValue is null) + if (conditionValue.IsNullOrWhitespace() || actualValue.IsNullOrWhitespace()) { return false; }