Skip to content

Commit

Permalink
Add more tests (sdk key format, circular dependency detection)
Browse files Browse the repository at this point in the history
  • Loading branch information
adams85 committed Jul 3, 2023
1 parent 7bbf1d6 commit 3c1e539
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<!--Do not remove this reference, it was added due to a SNYK security report-->
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net45'" />
</ItemGroup>

<ItemGroup>
Expand Down
38 changes: 38 additions & 0 deletions src/ConfigCat.Client.Tests/ConfigCatClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,44 @@ public void CreateAnInstance_WhenSdkKeyIsNull_ShouldThrowArgumentNullException()
using var _ = ConfigCatClient.Get(sdkKey!);
}

[DataRow("sdk-key-90123456789012", false, false)]
[DataRow("sdk-key-9012345678901/1234567890123456789012", false, false)]
[DataRow("sdk-key-90123456789012/123456789012345678901", false, false)]
[DataRow("sdk-key-90123456789012/12345678901234567890123", false, false)]
[DataRow("sdk-key-901234567890123/1234567890123456789012", false, false)]
[DataRow("sdk-key-90123456789012/1234567890123456789012", false, true)]
[DataRow("configcat-sdk-1/sdk-key-90123456789012", false, false)]
[DataRow("configcat-sdk-1/sdk-key-9012345678901/1234567890123456789012", false, false)]
[DataRow("configcat-sdk-1/sdk-key-90123456789012/123456789012345678901", false, false)]
[DataRow("configcat-sdk-1/sdk-key-90123456789012/12345678901234567890123", false, false)]
[DataRow("configcat-sdk-1/sdk-key-901234567890123/1234567890123456789012", false, false)]
[DataRow("configcat-sdk-1/sdk-key-90123456789012/1234567890123456789012", false, true)]
[DataRow("configcat-sdk-2/sdk-key-90123456789012/1234567890123456789012", false, false)]
[DataRow("configcat-proxy/", false, false)]
[DataRow("configcat-proxy/", true, false)]
[DataRow("configcat-proxy/sdk-key-90123456789012", false, false)]
[DataRow("configcat-proxy/sdk-key-90123456789012", true, true)]
[DataTestMethod]
[DoNotParallelize]
public void SdkKeyFormat_ShouldBeValidated(string sdkKey, bool customBaseUrl, bool isValid)
{
Action<ConfigCatClientOptions>? configureOptions = customBaseUrl
? o => o.BaseUrl = new Uri("https://my-configcat-proxy")
: null;

if (isValid)
{
using var _ = ConfigCatClient.Get(sdkKey, configureOptions);
}
else
{
Assert.ThrowsException<ArgumentException>(() =>
{
using var _ = ConfigCatClient.Get(sdkKey, configureOptions);
});
}
}

[ExpectedException(typeof(ArgumentOutOfRangeException))]
[TestMethod]
[DoNotParallelize]
Expand Down
59 changes: 59 additions & 0 deletions src/ConfigCat.Client.Tests/ConfigV6EvaluationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ConfigCat.Client.Evaluation;
using ConfigCat.Client.Tests.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace ConfigCat.Client.Tests;

Expand Down Expand Up @@ -79,4 +84,58 @@ public void SegmentTests(string jsonFileName, string settingKey, string expected
MatrixTestRunner<SegmentTestsDescriptor>.Default.RunTest(this.configEvaluator, this.logger, settingKey, expectedReturnValue,
userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}

[TestMethod]
public void CircularDependencyTest()
{
var configJson = ConfigHelper.GetSampleJson("sample_circulardependency_v6.json");
var config = configJson.Deserialize<Config>()!;

var logEvents = new List<(LogLevel Level, LogEventId EventId, FormattableLogMessage Message, Exception? Exception)>();

var loggerMock = new Mock<IConfigCatLogger>();
loggerMock.SetupGet(logger => logger.LogLevel).Returns(LogLevel.Info);
loggerMock.Setup(logger => logger.Log(It.IsAny<LogLevel>(), It.IsAny<LogEventId>(), ref It.Ref<FormattableLogMessage>.IsAny, It.IsAny<Exception>()))
.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);

const string key = "key1";
var evaluationDetails = evaluator.Evaluate<object?>(config.Settings, key, defaultValue: null, user: null, remoteConfig: null, loggerWrapper);

Assert.AreEqual(4, logEvents.Count);

Assert.AreEqual(3, logEvents.Count(evt => evt.EventId == 2003));

Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Error
&& (string?)evt.Message.ArgValues[0] == "key1"
&& (string?)evt.Message.ArgValues[1] == "'key1' -> 'key1'"));

Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Error
&& (string?)evt.Message.ArgValues[0] == "key2"
&& (string?)evt.Message.ArgValues[1] == "'key1' -> 'key2' -> 'key1'"));

Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Error
&& (string?)evt.Message.ArgValues[0] == "key3"
&& (string?)evt.Message.ArgValues[1] == "'key1' -> 'key3' -> 'key3'"));

var evaluateLogEvent = logEvents.FirstOrDefault(evt => evt.Level == LogLevel.Info && evt.EventId == 5000);
Assert.IsNotNull(evaluateLogEvent);

StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex(
"THEN 'key1-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine
+ @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage)));

StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex(
"THEN 'key2-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine
+ @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage)));

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];
}
}
86 changes: 86 additions & 0 deletions src/ConfigCat.Client.Tests/data/sample_circulardependency_v6.json
Original file line number Diff line number Diff line change
@@ -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" } }
}
]
}
}
}
12 changes: 6 additions & 6 deletions src/ConfigCatClient/Evaluation/RolloutEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ namespace ConfigCat.Client.Evaluation;

internal sealed class RolloutEvaluator : IRolloutEvaluator
{
private const string MissingUserObjectError = "cannot evaluate, User Object is missing";
private const string CircularDependencyError = "cannot evaluate, circular dependency detected";
internal const string MissingUserObjectError = "cannot evaluate, User Object is missing";
internal const string CircularDependencyError = "cannot evaluate, circular dependency detected";

internal const string TargetingRuleIgnoredMessage = "The current targeting rule is ignored and the evaluation continues with the next rule.";

private readonly LoggerWrapper logger;

Expand Down Expand Up @@ -95,14 +97,12 @@ private bool TryEvaluateTargetingRules(TargetingRule[] targetingRules, ref Evalu
hasConditions = true;
}

const string targetingRuleIgnoredMessage = "The current targeting rule is ignored and the evaluation continues with the next rule.";

// TODO: error handling - condition.GetCondition() - what to do when the condition is invalid (not available/multiple values specified)?
if (!TryEvaluateConditions(conditions, static condition => condition.GetCondition()!, targetingRule, contextSalt: context.Key, ref context, out var isMatch))
{
logBuilder?
.IncreaseIndent()
.NewLine(targetingRuleIgnoredMessage)
.NewLine(TargetingRuleIgnoredMessage)
.DecreaseIndent();
continue;
}
Expand Down Expand Up @@ -133,7 +133,7 @@ private bool TryEvaluateTargetingRules(TargetingRule[] targetingRules, ref Evalu
else
{
logBuilder?
.NewLine(targetingRuleIgnoredMessage)
.NewLine(TargetingRuleIgnoredMessage)
.DecreaseIndent();
continue;
}
Expand Down

0 comments on commit 3c1e539

Please sign in to comment.