From 852aa98e6a23be61fcb403dd8b26241cd8bc5651 Mon Sep 17 00:00:00 2001 From: Adam Simon Date: Thu, 7 Sep 2023 14:43:29 +0200 Subject: [PATCH] Add tests for advanced flag override use cases --- .../ConfigCat.Client.Tests.csproj | 6 ++ .../ConfigV6EvaluationTests.cs | 92 ++++++++++++++++--- .../EvaluationLogTests.cs | 55 +++++++---- .../Helpers/ConfigLocation.Cdn.cs | 17 ++-- .../Helpers/ConfigLocation.LocalFile.cs | 2 +- .../Helpers/ConfigLocation.cs | 2 +- .../Helpers/LoggerExtensions.cs | 9 -- .../Helpers/LoggingHelper.cs | 38 ++++++++ .../MatrixTestRunner.cs | 2 +- .../data/sample_circulardependency_v6.json | 86 ----------------- .../data/test_circulardependency_v6.json | 86 +++++++++++++++++ .../data/test_list_truncation.json | 36 ++++++++ .../data/test_override_flagdependency_v6.json | 44 +++++++++ .../data/test_override_segments_v6.json | 66 +++++++++++++ .../Evaluation/EvaluateLogHelper.cs | 2 +- 15 files changed, 408 insertions(+), 135 deletions(-) delete mode 100644 src/ConfigCat.Client.Tests/Helpers/LoggerExtensions.cs create mode 100644 src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs delete mode 100644 src/ConfigCat.Client.Tests/data/sample_circulardependency_v6.json create mode 100644 src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json create mode 100644 src/ConfigCat.Client.Tests/data/test_list_truncation.json create mode 100644 src/ConfigCat.Client.Tests/data/test_override_flagdependency_v6.json create mode 100644 src/ConfigCat.Client.Tests/data/test_override_segments_v6.json diff --git a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj index 9fc3719d..8333afbe 100644 --- a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj +++ b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj @@ -44,4 +44,10 @@ + + + PreserveNewest + + + diff --git a/src/ConfigCat.Client.Tests/ConfigV6EvaluationTests.cs b/src/ConfigCat.Client.Tests/ConfigV6EvaluationTests.cs index 902eece8..37587ee8 100644 --- a/src/ConfigCat.Client.Tests/ConfigV6EvaluationTests.cs +++ b/src/ConfigCat.Client.Tests/ConfigV6EvaluationTests.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; +using ConfigCat.Client.Configuration; using ConfigCat.Client.Evaluation; using ConfigCat.Client.Tests.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; namespace ConfigCat.Client.Tests; @@ -92,21 +94,15 @@ public void SegmentMatrixTests(string configLocation, string settingKey, string [TestMethod] public void CircularDependencyTest() { - var config = new ConfigLocation.LocalFile("data", "sample_circulardependency_v6.json").FetchConfig(); + var config = new ConfigLocation.LocalFile("data", "test_circulardependency_v6.json").FetchConfig(); - var logEvents = new List<(LogLevel Level, LogEventId EventId, FormattableLogMessage Message, Exception? Exception)>(); + var logEvents = new List(); + var logger = LoggingHelper.CreateCapturingLogger(logEvents); - var loggerMock = new Mock(); - loggerMock.SetupGet(logger => logger.LogLevel).Returns(LogLevel.Info); - loggerMock.Setup(logger => logger.Log(It.IsAny(), It.IsAny(), ref It.Ref.IsAny, It.IsAny())) - .Callback(delegate (LogLevel level, LogEventId eventId, ref FormattableLogMessage msg, Exception ex) { logEvents.Add((level, eventId, msg, ex)); }); - - var loggerWrapper = loggerMock.Object.AsWrapper(); - - var evaluator = new RolloutEvaluator(loggerWrapper); + var evaluator = new RolloutEvaluator(logger); const string key = "key1"; - var evaluationDetails = evaluator.Evaluate(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, loggerWrapper); + var evaluationDetails = evaluator.Evaluate(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, logger); Assert.AreEqual(4, logEvents.Count); @@ -138,7 +134,77 @@ public void CircularDependencyTest() StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex( "THEN 'key3-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage))); + } - var inv = loggerMock.Invocations[0]; + [DataTestMethod] + [DataRow("stringDependsOnString", "1", "john@sensitivecompany.com", null, "Dog")] + [DataRow("stringDependsOnString", "1", "john@sensitivecompany.com", OverrideBehaviour.RemoteOverLocal, "Dog")] + [DataRow("stringDependsOnString", "1", "john@sensitivecompany.com", OverrideBehaviour.LocalOverRemote, "Dog")] + [DataRow("stringDependsOnString", "1", "john@sensitivecompany.com", OverrideBehaviour.LocalOnly, null)] + [DataRow("stringDependsOnString", "2", "john@notsensitivecompany.com", null, "Cat")] + [DataRow("stringDependsOnString", "2", "john@notsensitivecompany.com", OverrideBehaviour.RemoteOverLocal, "Cat")] + [DataRow("stringDependsOnString", "2", "john@notsensitivecompany.com", OverrideBehaviour.LocalOverRemote, "Dog")] + [DataRow("stringDependsOnString", "2", "john@notsensitivecompany.com", OverrideBehaviour.LocalOnly, null)] + [DataRow("stringDependsOnInt", "1", "john@sensitivecompany.com", null, "Dog")] + [DataRow("stringDependsOnInt", "1", "john@sensitivecompany.com", OverrideBehaviour.RemoteOverLocal, "Dog")] + [DataRow("stringDependsOnInt", "1", "john@sensitivecompany.com", OverrideBehaviour.LocalOverRemote, "Cat")] + [DataRow("stringDependsOnInt", "1", "john@sensitivecompany.com", OverrideBehaviour.LocalOnly, null)] + [DataRow("stringDependsOnInt", "2", "john@notsensitivecompany.com", null, "Cat")] + [DataRow("stringDependsOnInt", "2", "john@notsensitivecompany.com", OverrideBehaviour.RemoteOverLocal, "Cat")] + [DataRow("stringDependsOnInt", "2", "john@notsensitivecompany.com", OverrideBehaviour.LocalOverRemote, "Dog")] + [DataRow("stringDependsOnInt", "2", "john@notsensitivecompany.com", OverrideBehaviour.LocalOnly, null)] + public async Task PrerequisiteFlagOverrideTest(string key, string userId, string email, OverrideBehaviour? overrideBehaviour, object expectedValue) + { + var cdnLocation = (ConfigLocation.Cdn)new FlagDependencyMatrixTestsDescriptor().ConfigLocation; + + var options = new ConfigCatClientOptions + { + // The flag override uses a different config json salt than the downloaded one and overrides the following segments: + // * Beta Users: User.Email IS ONE OF ['jane@example.com'] + // * Developers: User.Email IS ONE OF ['john@example.com'] + FlagOverrides = overrideBehaviour is not null + ? FlagOverrides.LocalFile(Path.Combine("data", "test_override_flagdependency_v6.json"), autoReload: false, overrideBehaviour.Value) + : null, + PollingMode = PollingModes.ManualPoll, + }; + cdnLocation.ConfigureBaseUrl(options); + + using var client = new ConfigCatClient(cdnLocation.SdkKey, options); + await client.ForceRefreshAsync(); + var actualValue = await client.GetValueAsync(key, (object?)null, new User(userId) { Email = email }); + + Assert.AreEqual(expectedValue, actualValue); + } + + [DataTestMethod] + [DataRow("developerAndBetaUserSegment", "1", "john@example.com", null, false)] + [DataRow("developerAndBetaUserSegment", "1", "john@example.com", OverrideBehaviour.RemoteOverLocal, false)] + [DataRow("developerAndBetaUserSegment", "1", "john@example.com", OverrideBehaviour.LocalOverRemote, true)] + [DataRow("developerAndBetaUserSegment", "1", "john@example.com", OverrideBehaviour.LocalOnly, true)] + [DataRow("notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", null, true)] + [DataRow("notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", OverrideBehaviour.RemoteOverLocal, true)] + [DataRow("notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", OverrideBehaviour.LocalOverRemote, true)] + [DataRow("notDeveloperAndNotBetaUserSegment", "2", "kate@example.com", OverrideBehaviour.LocalOnly, null)] + public async Task ConfigSaltAndSegmentsOverrideTest(string key, string userId, string email, OverrideBehaviour? overrideBehaviour, object expectedValue) + { + var cdnLocation = (ConfigLocation.Cdn)new SegmentMatrixTestsDescriptor().ConfigLocation; + + var options = new ConfigCatClientOptions + { + // The flag override uses a different config json salt than the downloaded one and overrides the following segments: + // * Beta Users: User.Email IS ONE OF ['jane@example.com'] + // * Developers: User.Email IS ONE OF ['john@example.com'] + FlagOverrides = overrideBehaviour is not null + ? FlagOverrides.LocalFile(Path.Combine("data", "test_override_segments_v6.json"), autoReload: false, overrideBehaviour.Value) + : null, + PollingMode = PollingModes.ManualPoll, + }; + cdnLocation.ConfigureBaseUrl(options); + + using var client = new ConfigCatClient(cdnLocation.SdkKey, options); + await client.ForceRefreshAsync(); + var actualValue = await client.GetValueAsync(key, (object?)null, new User(userId) { Email = email }); + + Assert.AreEqual(expectedValue, actualValue); } } diff --git a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs index a8aee8d9..6f15897d 100644 --- a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs +++ b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs @@ -7,7 +7,6 @@ using ConfigCat.Client.Tests.Helpers; using ConfigCat.Client.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; #if NET45 using Newtonsoft.Json; @@ -198,6 +197,36 @@ public void SemVerValidationTests(string testSetName, string? sdkKey, string? ba } } + [TestMethod] + public void ComparisonValueListTruncation() + { + var config = new ConfigLocation.LocalFile("data", "test_list_truncation.json").FetchConfig(); + + var logEvents = new List(); + var logger = LoggingHelper.CreateCapturingLogger(logEvents); + + var evaluator = new RolloutEvaluator(logger); + var evaluationDetails = evaluator.Evaluate(config.Settings, "key1", (bool?)null, new User("12"), remoteConfig: null, logger); + var actualReturnValue = evaluationDetails.Value; + + Assert.AreEqual(true, actualReturnValue); + Assert.AreEqual(1, logEvents.Count); + + var expectedLogLines = new[] + { + "INFO [5000] Evaluating 'key1' for User '{\"Identifier\":\"12\"}'", + " Evaluating targeting rules and applying the first match if any:", + " - IF User.Identifier CONTAINS ANY OF ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] => true", + " AND User.Identifier CONTAINS ANY OF ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ... <1 more value>] => true", + " AND User.Identifier CONTAINS ANY OF ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ... <2 more values>] => true", + " THEN 'True' => MATCH, applying rule", + " Returning 'True'.", + }; + + var evt = logEvents[0]; + Assert.AreEqual(string.Join(Environment.NewLine, expectedLogLines), FormatLogEvent(ref evt)); + } + private static void RunTest(string testSetName, string? sdkKey, string? baseUrlOrOverrideFileName, string key, string? defaultValue, string? userObject, string? expectedReturnValue, string expectedLogFileName) { var defaultValueParsed = defaultValue?.Deserialize()!.ToSettingValue(out var settingType).GetValue(); @@ -232,13 +261,8 @@ private static void RunTest(string testSetName, string? sdkKey, string? baseUrlO user = null; } - var logEvents = new List<(LogLevel Level, LogEventId EventId, FormattableLogMessage Message, Exception? Exception)>(); - - var loggerMock = new Mock(); - loggerMock.SetupGet(logger => logger.LogLevel).Returns(LogLevel.Info); - loggerMock.Setup(logger => logger.Log(It.IsAny(), It.IsAny(), ref It.Ref.IsAny, It.IsAny())) - .Callback(delegate (LogLevel level, LogEventId eventId, ref FormattableLogMessage msg, Exception ex) { logEvents.Add((level, eventId, msg, ex)); }); - var logger = loggerMock.Object.AsWrapper(); + var logEvents = new List(); + var logger = LoggingHelper.CreateCapturingLogger(logEvents); ConfigLocation configLocation = sdkKey is { Length: > 0 } ? new ConfigLocation.Cdn(sdkKey, baseUrlOrOverrideFileName) @@ -255,28 +279,27 @@ private static void RunTest(string testSetName, string? sdkKey, string? baseUrlO var expectedLogFilePath = Path.Combine(TestDataRootPath, testSetName, expectedLogFileName); var expectedLogText = string.Join(Environment.NewLine, File.ReadAllLines(expectedLogFilePath)); - var actualLogText = string.Join(Environment.NewLine, logEvents - .Select(evt => FormatLogEvent(evt.Level, evt.EventId, ref evt.Message, evt.Exception))); + var actualLogText = string.Join(Environment.NewLine, logEvents.Select(evt => FormatLogEvent(ref evt))); Assert.AreEqual(expectedLogText, actualLogText); } - private static string FormatLogEvent(LogLevel level, LogEventId eventId, ref FormattableLogMessage message, Exception? exception) + private static string FormatLogEvent(ref LogEvent evt) { - var levelString = level switch + var levelString = evt.Level switch { LogLevel.Debug => "DEBUG", LogLevel.Info => "INFO", LogLevel.Warning => "WARNING", LogLevel.Error => "ERROR", - _ => level.ToString().ToUpperInvariant().PadRight(5) + _ => evt.Level.ToString().ToUpperInvariant().PadRight(5) }; - var eventIdString = eventId.Id.ToString(CultureInfo.InvariantCulture); + var eventIdString = evt.EventId.Id.ToString(CultureInfo.InvariantCulture); - var exceptionString = exception is null ? string.Empty : Environment.NewLine + exception; + var exceptionString = evt.Exception is null ? string.Empty : Environment.NewLine + evt.Exception; - return $"{levelString} [{eventIdString}] {message.InvariantFormattedMessage}{exceptionString}"; + return $"{levelString} [{eventIdString}] {evt.Message.InvariantFormattedMessage}{exceptionString}"; } #pragma warning disable IDE1006 // Naming Styles diff --git a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.Cdn.cs b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.Cdn.cs index a206e1f5..a1091748 100644 --- a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.Cdn.cs +++ b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.Cdn.cs @@ -12,13 +12,11 @@ public sealed record class Cdn : ConfigLocation public string SdkKey { get; } public string? BaseUrl { get; } - public override string RealLocation + public override string GetRealLocation() { - get - { - var options = new ConfigCatClientOptions { BaseUrl = BaseUrl is not null ? new Uri(BaseUrl) : ConfigCatClientOptions.BaseUrlEu }; - return options.CreateUri(SdkKey).ToString(); - } + var options = new ConfigCatClientOptions(); + ConfigureBaseUrl(options); + return options.CreateUri(SdkKey).ToString(); } internal override Config FetchConfig() @@ -27,8 +25,8 @@ internal override Config FetchConfig() { PollingMode = PollingModes.ManualPoll, Logger = new ConsoleLogger(), - BaseUrl = BaseUrl is not null ? new Uri(BaseUrl) : ConfigCatClientOptions.BaseUrlEu }; + ConfigureBaseUrl(options); using var configFetcher = new HttpConfigFetcher( options.CreateUri(SdkKey), @@ -43,5 +41,10 @@ internal override Config FetchConfig() ? fetchResult.Config.Config! : throw new InvalidOperationException("Could not fetch config from CDN: " + fetchResult.ErrorMessage); } + + internal void ConfigureBaseUrl(ConfigCatClientOptions options) + { + options.BaseUrl = BaseUrl is not null ? new Uri(BaseUrl) : ConfigCatClientOptions.BaseUrlEu; + } } } diff --git a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.LocalFile.cs b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.LocalFile.cs index bf64a1d3..ef6bd60f 100644 --- a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.LocalFile.cs +++ b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.LocalFile.cs @@ -15,7 +15,7 @@ public sealed record class LocalFile : ConfigLocation public string FilePath { get; } - public override string RealLocation => FilePath; + public override string GetRealLocation() => FilePath; internal override Config FetchConfig() { diff --git a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.cs b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.cs index edc0f9c1..1afc6237 100644 --- a/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.cs +++ b/src/ConfigCat.Client.Tests/Helpers/ConfigLocation.cs @@ -8,7 +8,7 @@ public abstract partial record class ConfigLocation { private ConfigLocation() { } - public abstract string RealLocation { get; } + public abstract string GetRealLocation(); internal abstract Config FetchConfig(); } diff --git a/src/ConfigCat.Client.Tests/Helpers/LoggerExtensions.cs b/src/ConfigCat.Client.Tests/Helpers/LoggerExtensions.cs deleted file mode 100644 index 23b100f3..00000000 --- a/src/ConfigCat.Client.Tests/Helpers/LoggerExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ConfigCat.Client; - -internal static class LoggerExtensions -{ - public static LoggerWrapper AsWrapper(this IConfigCatLogger logger, Hooks? hooks = null) - { - return new LoggerWrapper(logger, hooks); - } -} diff --git a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs new file mode 100644 index 00000000..3cfef7aa --- /dev/null +++ b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs @@ -0,0 +1,38 @@ +using Moq; +using System.Collections.Generic; +using System; + +namespace ConfigCat.Client; + +internal struct LogEvent +{ + public LogEvent(LogLevel level, LogEventId eventId, ref FormattableLogMessage message, Exception? exception) + { + (this.Level, this.EventId, this.Message, this.Exception) = (level, eventId, message, exception); + } + + public readonly LogLevel Level; + public readonly LogEventId EventId; + public FormattableLogMessage Message; + public readonly Exception? Exception; +} + +internal static class LoggingHelper +{ + public static LoggerWrapper AsWrapper(this IConfigCatLogger logger, Hooks? hooks = null) + { + return new LoggerWrapper(logger, hooks); + } + + public static LoggerWrapper CreateCapturingLogger(List logEvents, LogLevel logLevel = LogLevel.Info) + { + var loggerMock = new Mock(); + + loggerMock.SetupGet(logger => logger.LogLevel).Returns(logLevel); + + loggerMock.Setup(logger => logger.Log(It.IsAny(), It.IsAny(), ref It.Ref.IsAny, It.IsAny())) + .Callback(delegate (LogLevel level, LogEventId eventId, ref FormattableLogMessage msg, Exception ex) { logEvents.Add(new LogEvent(level, eventId, ref msg, ex)); }); + + return loggerMock.Object.AsWrapper(); + } +} diff --git a/src/ConfigCat.Client.Tests/MatrixTestRunner.cs b/src/ConfigCat.Client.Tests/MatrixTestRunner.cs index c6cb08b9..9c0ca7a5 100644 --- a/src/ConfigCat.Client.Tests/MatrixTestRunner.cs +++ b/src/ConfigCat.Client.Tests/MatrixTestRunner.cs @@ -11,7 +11,7 @@ public class MatrixTestRunner : MatrixTestRunnerBase protected override bool AssertValue(string expected, Func parse, T actual, string keyName, string? userId) { - Assert.AreEqual(parse(expected), actual, $"config: {DescriptorInstance.ConfigLocation.RealLocation} | keyName: {keyName} | userId: {userId}"); + Assert.AreEqual(parse(expected), actual, $"config: {DescriptorInstance.ConfigLocation.GetRealLocation()} | keyName: {keyName} | userId: {userId}"); return true; } } diff --git a/src/ConfigCat.Client.Tests/data/sample_circulardependency_v6.json b/src/ConfigCat.Client.Tests/data/sample_circulardependency_v6.json deleted file mode 100644 index e86ed5af..00000000 --- a/src/ConfigCat.Client.Tests/data/sample_circulardependency_v6.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "p": { - "u": "https://cdn-global.configcat.com", - "r": 0 - }, - "f": { - "key1": { - "t": 1, - "v": { "s": "value1" }, - "r": [ - { - "c": [ - { - "d": { - "f": "key1", - "c": 0, - "v": { "s": "key1-prereq1" } - } - } - ], - "s": { "v": { "s": "key1-prereq1" } } - }, - { - "c": [ - { - "d": { - "f": "key2", - "c": 0, - "v": { "s": "key1-prereq2" } - } - } - ], - "s": { "v": { "s": "key1-prereq2" } } - }, - { - "c": [ - { - "d": { - "f": "key3", - "c": 0, - "v": { "s": "key1-prereq3" } - } - } - ], - "s": { "v": { "s": "key1-prereq3" } } - } - ] - }, - "key2": { - "t": 1, - "v": { "s": "value2" }, - "r": [ - { - "c": [ - { - "d": { - "f": "key1", - "c": 0, - "v": { "s": "key2-prereq1" } - } - } - ], - "s": { "v": { "s": "key2-prereq1" } } - } - ] - }, - "key3": { - "t": 1, - "v": { "s": "value3" }, - "r": [ - { - "c": [ - { - "d": { - "f": "key3", - "c": 0, - "v": { "s": "key3-prereq1" } - } - } - ], - "s": { "v": { "s": "key3-prereq1" } } - } - ] - } - } -} diff --git a/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json new file mode 100644 index 00000000..8ae493ee --- /dev/null +++ b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json @@ -0,0 +1,86 @@ +{ + "p": { + "u": "https://cdn-global.configcat.com", + "r": 0 + }, + "f": { + "key1": { + "t": 1, + "v": { "s": "value1" }, + "r": [ + { + "c": [ + { + "d": { + "f": "key1", + "c": 0, + "v": { "s": "key1-prereq1" } + } + } + ], + "s": { "v": { "s": "key1-prereq1" } } + }, + { + "c": [ + { + "d": { + "f": "key2", + "c": 0, + "v": { "s": "key1-prereq2" } + } + } + ], + "s": { "v": { "s": "key1-prereq2" } } + }, + { + "c": [ + { + "d": { + "f": "key3", + "c": 0, + "v": { "s": "key1-prereq3" } + } + } + ], + "s": { "v": { "s": "key1-prereq3" } } + } + ] + }, + "key2": { + "t": 1, + "v": { "s": "value2" }, + "r": [ + { + "c": [ + { + "d": { + "f": "key1", + "c": 0, + "v": { "s": "key2-prereq1" } + } + } + ], + "s": { "v": { "s": "key2-prereq1" } } + } + ] + }, + "key3": { + "t": 1, + "v": { "s": "value3" }, + "r": [ + { + "c": [ + { + "d": { + "f": "key3", + "c": 0, + "v": { "s": "key3-prereq1" } + } + } + ], + "s": { "v": { "s": "key3-prereq1" } } + } + ] + } + } +} diff --git a/src/ConfigCat.Client.Tests/data/test_list_truncation.json b/src/ConfigCat.Client.Tests/data/test_list_truncation.json new file mode 100644 index 00000000..a665062b --- /dev/null +++ b/src/ConfigCat.Client.Tests/data/test_list_truncation.json @@ -0,0 +1,36 @@ +{ + "f": { + "key1": { + "t": 0, + "v": { "b": false }, + "r": [ + { + "c": [ + { + "t": { + "a": "Identifier", + "c": 2, + "l": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" ] + } + }, + { + "t": { + "a": "Identifier", + "c": 2, + "l": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" ] + } + }, + { + "t": { + "a": "Identifier", + "c": 2, + "l": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ] + } + } + ], + "s": { "v": { "b": true } } + } + ] + } + } +} diff --git a/src/ConfigCat.Client.Tests/data/test_override_flagdependency_v6.json b/src/ConfigCat.Client.Tests/data/test_override_flagdependency_v6.json new file mode 100644 index 00000000..0eead859 --- /dev/null +++ b/src/ConfigCat.Client.Tests/data/test_override_flagdependency_v6.json @@ -0,0 +1,44 @@ +{ + "p": { + "u": "https://test-cdn-eu.configcat.com", + "r": 0, + "s": "TsTuRHo\u002BMHs8h8j16HQY83sooJsLg34Ir5KIVOletFU=" + }, + "f": { + "mainStringFlag": { + "t": 1, + "v": { + "s": "private" + }, + "i": "24c96275" + }, + "stringDependsOnInt": { + "t": 1, + "r": [ + { + "c": [ + { + "d": { + "f": "mainIntFlag", + "c": 0, + "v": { + "i": 42 + } + } + } + ], + "s": { + "v": { + "s": "Dog" + }, + "i": "12531eec" + } + } + ], + "v": { + "s": "Cat" + }, + "i": "e227d926" + } + } +} diff --git a/src/ConfigCat.Client.Tests/data/test_override_segments_v6.json b/src/ConfigCat.Client.Tests/data/test_override_segments_v6.json new file mode 100644 index 00000000..47bf15ce --- /dev/null +++ b/src/ConfigCat.Client.Tests/data/test_override_segments_v6.json @@ -0,0 +1,66 @@ +{ + "p": { + "u": "https://test-cdn-eu.configcat.com", + "r": 0, + "s": "80xCU/SlDz1lCiWFaxIBjyJeJecWjq46T4eu6GtozkM=" + }, + "s": [ + { + "n": "Beta Users", + "r": [ + { + "a": "Email", + "c": 16, + "l": [ + "9189c42f6035bd1d2df5eda347a4f62926d27c80540a7aa6cc72cc75bc6757ff" + ] + } + ] + }, + { + "n": "Developers", + "r": [ + { + "a": "Email", + "c": 16, + "l": [ + "a7cdf54e74b5527bd2617889ec47f6d29b825ccfc97ff00832886bcb735abded" + ] + } + ] + } + ], + "f": { + "developerAndBetaUserSegment": { + "t": 0, + "r": [ + { + "c": [ + { + "s": { + "s": 1, + "c": 0 + } + }, + { + "s": { + "s": 0, + "c": 1 + } + } + ], + "s": { + "v": { + "b": true + }, + "i": "ddc50638" + } + } + ], + "v": { + "b": false + }, + "i": "6427f4b8" + } + } +} diff --git a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs index 101af873..0d5cf10c 100644 --- a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs +++ b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs @@ -12,7 +12,7 @@ internal static class EvaluateLogHelper public const string InvalidReferencePlaceholder = ""; public const string InvalidValuePlaceholder = ""; - private const int StringListMaxLength = 10; + internal const int StringListMaxLength = 10; public static IndentedTextBuilder AppendEvaluationResult(this IndentedTextBuilder builder, bool result) {