Skip to content

Commit

Permalink
Add matrix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
adams85 committed Jun 30, 2023
1 parent 32b6759 commit e3a71e3
Show file tree
Hide file tree
Showing 11 changed files with 1,595 additions and 107 deletions.
111 changes: 4 additions & 107 deletions src/ConfigCat.Client.Tests/ConfigEvaluatorTestsBase.cs
Original file line number Diff line number Diff line change
@@ -1,135 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using ConfigCat.Client.Evaluation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConfigCat.Client.Tests;

public interface IMatrixTestDescriptor
{
public string SampleJsonFileName { get; }
public string MatrixResultFileName { get; }
}

public abstract class ConfigEvaluatorTestsBase<TDescriptor> where TDescriptor : IMatrixTestDescriptor, new()
public abstract class ConfigEvaluatorTestsBase<TDescriptor> : MatrixTestRunner<TDescriptor>
where TDescriptor : IMatrixTestDescriptor, new()
{
#pragma warning disable IDE1006 // Naming Styles
private protected readonly LoggerWrapper Logger;
#pragma warning restore IDE1006 // Naming Styles

private protected readonly Dictionary<string, Setting> config;

internal readonly IRolloutEvaluator configEvaluator;

public ConfigEvaluatorTestsBase()
{
var descriptor = new TDescriptor();
this.config = GetSampleJson(descriptor.SampleJsonFileName).Deserialize<Config>()!.Settings;

this.Logger = new ConsoleLogger(LogLevel.Debug).AsWrapper();
this.configEvaluator = new RolloutEvaluator(this.Logger);
}

protected virtual void AssertValue(string jsonFileName, string keyName, string expected, User? user)
{
var k = keyName.ToLowerInvariant();

if (k.StartsWith("bool"))
{
var actual = this.configEvaluator.Evaluate(this.config, keyName, false, user, null, this.Logger).Value;

Assert.AreEqual(bool.Parse(expected), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else if (k.StartsWith("double"))
{
var actual = this.configEvaluator.Evaluate(this.config, keyName, double.NaN, user, null, this.Logger).Value;

Assert.AreEqual(double.Parse(expected, CultureInfo.InvariantCulture), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else if (k.StartsWith("integer"))
{
var actual = this.configEvaluator.Evaluate(this.config, keyName, int.MinValue, user, null, this.Logger).Value;

Assert.AreEqual(int.Parse(expected), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else
{
var actual = this.configEvaluator.Evaluate(this.config, keyName, string.Empty, user, null, this.Logger).Value;

Assert.AreEqual(expected, actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
}

protected string GetSampleJson(string fileName)
{
using Stream stream = File.OpenRead(Path.Combine("data", fileName));
using StreamReader reader = new(stream);
return reader.ReadToEnd();
}

public static IEnumerable<object?[]> GetMatrixTests()
{
var descriptor = new TDescriptor();

var resultFilePath = Path.Combine("data", descriptor.MatrixResultFileName);
using var reader = new StreamReader(resultFilePath);
var header = reader.ReadLine()!;

var columns = header.Split(new[] { ';' });

while (!reader.EndOfStream)
{
var rawline = reader.ReadLine();

if (string.IsNullOrEmpty(rawline))
{
continue;
}

var row = rawline.Split(new[] { ';' });

string? userId = null, userEmail = null, userCountry = null, userCustomAttributeName = null, userCustomAttributeValue = null;
if (row[0] != "##null##")
{
userId = row[0];
userEmail = row[1] == "##null##" ? null : row[1];
userCountry = row[2] == "##null##" ? null : row[2];
if (row[3] != "##null##")
{
userCustomAttributeName = columns[3];
userCustomAttributeValue = row[3];
}
}

for (var i = 4; i < columns.Length; i++)
{
yield return new[]
{
descriptor.SampleJsonFileName, columns[i], row[i],
userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue
};
}
}
}
public static IEnumerable<object?[]> GetMatrixTests() => GetTests();

[TestCategory("MatrixTests")]
[DataTestMethod]
[DynamicData(nameof(GetMatrixTests), DynamicDataSourceType.Method)]
public void Run_MatrixTests(string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
User? user = null;
if (userId is not null)
{
user = new User(userId) { Email = userEmail, Country = userCountry };
if (userCustomAttributeValue is not null)
{
user.Custom[userCustomAttributeName!] = userCustomAttributeValue;
}
}

AssertValue(jsonFileName, settingKey, expectedReturnValue, user);
RunTest(this.configEvaluator, this.Logger, jsonFileName, settingKey, expectedReturnValue, userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}
}
83 changes: 83 additions & 0 deletions src/ConfigCat.Client.Tests/ConfigV6MatrixTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Collections.Generic;
using ConfigCat.Client.Evaluation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConfigCat.Client.Tests;

[TestCategory("MatrixTests")]
[TestClass]
public class ConfigV6MatrixTests
{
public class AndOrTestsDescriptor : IMatrixTestDescriptor
{
public string SampleJsonFileName => "sample_and_or_v6.json";
public string MatrixResultFileName => "testmatrix_and_or.csv";
public static IEnumerable<object?[]> GetTests() => MatrixTestRunner<AndOrTestsDescriptor>.GetTests();
}

public class ComparatorTestsDescriptor : IMatrixTestDescriptor
{
public string SampleJsonFileName => "sample_comparators_v6.json";
public string MatrixResultFileName => "testmatrix_comparators_v6.csv";
public static IEnumerable<object?[]> GetTests() => MatrixTestRunner<ComparatorTestsDescriptor>.GetTests();
}

public class FlagDependencyTestsDescriptor : IMatrixTestDescriptor
{
public string SampleJsonFileName => "sample_flagdependency_v6.json";
public string MatrixResultFileName => "testmatrix_dependent_flag.csv";
public static IEnumerable<object?[]> GetTests() => MatrixTestRunner<FlagDependencyTestsDescriptor>.GetTests();
}

public class SegmentTestsDescriptor : IMatrixTestDescriptor
{
public string SampleJsonFileName => "sample_segments_v6.json";
public string MatrixResultFileName => "testmatrix_segments.csv";
public static IEnumerable<object?[]> GetTests() => MatrixTestRunner<SegmentTestsDescriptor>.GetTests();
}

private readonly LoggerWrapper logger;
private readonly IRolloutEvaluator configEvaluator;

public ConfigV6MatrixTests()
{
this.logger = new ConsoleLogger(LogLevel.Debug).AsWrapper();
this.configEvaluator = new RolloutEvaluator(this.logger);
}

[DataTestMethod]
[DynamicData(nameof(AndOrTestsDescriptor.GetTests), typeof(AndOrTestsDescriptor), DynamicDataSourceType.Method)]
public void AndOrTests(string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
MatrixTestRunner<AndOrTestsDescriptor>.Default.RunTest(this.configEvaluator, this.logger,
jsonFileName, settingKey, expectedReturnValue, userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}

[DataTestMethod]
[DynamicData(nameof(ComparatorTestsDescriptor.GetTests), typeof(ComparatorTestsDescriptor), DynamicDataSourceType.Method)]
public void ComparatorTests(string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
MatrixTestRunner<ComparatorTestsDescriptor>.Default.RunTest(this.configEvaluator, this.logger,
jsonFileName, settingKey, expectedReturnValue, userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}

[DataTestMethod]
[DynamicData(nameof(FlagDependencyTestsDescriptor.GetTests), typeof(FlagDependencyTestsDescriptor), DynamicDataSourceType.Method)]
public void FlagDependencyTests(string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
MatrixTestRunner<FlagDependencyTestsDescriptor>.Default.RunTest(this.configEvaluator, this.logger,
jsonFileName, settingKey, expectedReturnValue, userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}

[DataTestMethod]
[DynamicData(nameof(SegmentTestsDescriptor.GetTests), typeof(SegmentTestsDescriptor), DynamicDataSourceType.Method)]
public void SegmentTests(string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
MatrixTestRunner<SegmentTestsDescriptor>.Default.RunTest(this.configEvaluator, this.logger,
jsonFileName, settingKey, expectedReturnValue, userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
}
}
128 changes: 128 additions & 0 deletions src/ConfigCat.Client.Tests/MatrixTestRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using ConfigCat.Client.Evaluation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConfigCat.Client.Tests;

public interface IMatrixTestDescriptor
{
public string SampleJsonFileName { get; }
public string MatrixResultFileName { get; }
}

public class MatrixTestRunner<TDescriptor> where TDescriptor : IMatrixTestDescriptor, new()
{
private static readonly TDescriptor Descriptor = new();

private static readonly Lazy<MatrixTestRunner<TDescriptor>> DefaultLazy = new(() => new MatrixTestRunner<TDescriptor>(), isThreadSafe: true);
public static MatrixTestRunner<TDescriptor> Default => DefaultLazy.Value;

private protected readonly Dictionary<string, Setting> config;

protected MatrixTestRunner()
{
this.config = GetSampleJson(Descriptor.SampleJsonFileName).Deserialize<Config>()!.Settings;
}

private static string GetSampleJson(string fileName)
{
using Stream stream = File.OpenRead(Path.Combine("data", fileName));
using StreamReader reader = new(stream);
return reader.ReadToEnd();
}

public static IEnumerable<object?[]> GetTests()
{
var resultFilePath = Path.Combine("data", Descriptor.MatrixResultFileName);
using var reader = new StreamReader(resultFilePath);
var header = reader.ReadLine()!;

var columns = header.Split(new[] { ';' });

while (!reader.EndOfStream)
{
var rawline = reader.ReadLine();

if (string.IsNullOrEmpty(rawline))
continue;

var row = rawline.Split(new[] { ';' });

string? userId = null, userEmail = null, userCountry = null, userCustomAttributeName = null, userCustomAttributeValue = null;
if (row[0] != "##null##")
{
userId = row[0];
userEmail = row[1] is "" or "##null##" ? null : row[1];
userCountry = row[2] is "" or "##null##" ? null : row[2];
if (row[3] is not ("" or "##null##"))
{
userCustomAttributeName = columns[3];
userCustomAttributeValue = row[3];
}
}

for (var i = 4; i < columns.Length; i++)
{
yield return new[]
{
Descriptor.SampleJsonFileName, columns[i], row[i],
userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue
};
}
}
}

private protected virtual void AssertValue(IRolloutEvaluator evaluator, LoggerWrapper logger, string jsonFileName, string keyName, string expected, User? user)
{
var k = keyName.ToLowerInvariant();

if (k.StartsWith("bool"))
{
var actual = evaluator.Evaluate(this.config, keyName, false, user, null, logger).Value;

Assert.AreEqual(bool.Parse(expected), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else if (k.StartsWith("double"))
{
var actual = evaluator.Evaluate(this.config, keyName, double.NaN, user, null, logger).Value;

Assert.AreEqual(double.Parse(expected, CultureInfo.InvariantCulture), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else if (k.StartsWith("integer"))
{
var actual = evaluator.Evaluate(this.config, keyName, int.MinValue, user, null, logger).Value;

Assert.AreEqual(int.Parse(expected), actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else if (k.StartsWith("string"))
{
var actual = evaluator.Evaluate(this.config, keyName, string.Empty, user, null, logger).Value;

Assert.AreEqual(expected, actual, $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
else
{
var actual = evaluator.Evaluate(this.config, keyName, (object?)null, user, null, logger).Value;
Assert.IsNotNull(actual);
Assert.AreEqual(expected, Convert.ToString(actual, CultureInfo.InvariantCulture), $"jsonFileName: {jsonFileName} | keyName: {keyName} | userId: {user?.Identifier}");
}
}

internal void RunTest(IRolloutEvaluator evaluator, LoggerWrapper logger,
string jsonFileName, string settingKey, string expectedReturnValue,
string? userId, string? userEmail, string? userCountry, string? userCustomAttributeName, string? userCustomAttributeValue)
{
User? user = null;
if (userId is not null)
{
user = new User(userId) { Email = userEmail, Country = userCountry };
if (userCustomAttributeValue is not null)
user.Custom[userCustomAttributeName!] = userCustomAttributeValue;
}

AssertValue(evaluator, logger, jsonFileName, settingKey, expectedReturnValue, user);
}
}
Loading

0 comments on commit e3a71e3

Please sign in to comment.