-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adds and implements GetAllValueDetails/GetAllValueDetailsAsync methods (+ revises RolloutEvaluatorExtensions) * Deprecates GetVariationId/GetVariationIdAsync and GetAllVariationId/GetAllVariationIdAsync * Adds tests
- Loading branch information
Showing
8 changed files
with
471 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ public class BasicConfigEvaluatorTests : ConfigEvaluatorTestsBase | |
[TestMethod] | ||
public void GetValue_WithSimpleKey_ShouldReturnCat() | ||
{ | ||
string actual = configEvaluator.Evaluate(config, "stringDefaultCat", string.Empty, user: null, null, this.logger, out _); | ||
string actual = configEvaluator.Evaluate(config, "stringDefaultCat", string.Empty, user: null, null, this.logger).Value; | ||
|
||
Assert.AreNotEqual(string.Empty, actual); | ||
Assert.AreEqual("Cat", actual); | ||
|
@@ -28,15 +28,15 @@ public void GetValue_WithSimpleKey_ShouldReturnCat() | |
[TestMethod] | ||
public void GetValue_WithNonExistingKey_ShouldReturnDefaultValue() | ||
{ | ||
string actual = configEvaluator.Evaluate(config, "NotExistsKey", "NotExistsValue", user: null, null, this.logger, out _); | ||
string actual = configEvaluator.Evaluate(config, "NotExistsKey", "NotExistsValue", user: null, null, this.logger).Value; | ||
|
||
Assert.AreEqual("NotExistsValue", actual); | ||
} | ||
|
||
[TestMethod] | ||
public void GetValue_WithEmptyProjectConfig_ShouldReturnDefaultValue() | ||
{ | ||
string actual = configEvaluator.Evaluate(new Dictionary<string, Setting>(), "stringDefaultCat", "Default", user: null, null, this.logger, out _); | ||
string actual = configEvaluator.Evaluate(new Dictionary<string, Setting>(), "stringDefaultCat", "Default", user: null, null, this.logger).Value; | ||
|
||
Assert.AreEqual("Default", actual); | ||
} | ||
|
@@ -49,13 +49,13 @@ public void GetValue_WithUser_ShouldReturnEvaluatedValue() | |
Email = "[email protected]", | ||
Country = "United Kingdom", | ||
Custom = new Dictionary<string, string> { { "Custom1", "admin" } } | ||
}, null, this.logger, out _); | ||
}, null, this.logger).Value; | ||
|
||
Assert.AreEqual(3.1415, actual); | ||
} | ||
|
||
private delegate object EvaluateDelegate(IRolloutEvaluator evaluator, IDictionary<string, Setting> settings, string key, object defaultValue, User user, | ||
ProjectConfig remoteConfig, ILogger logger, out EvaluationDetails<object> evaluationDetails); | ||
private delegate EvaluationDetails<object> EvaluateDelegate(IRolloutEvaluator evaluator, IDictionary<string, Setting> settings, string key, object defaultValue, User user, | ||
ProjectConfig remoteConfig, ILogger logger); | ||
|
||
private static readonly MethodInfo evaluateMethodDefinition = new EvaluateDelegate(RolloutEvaluatorExtensions.Evaluate).Method.GetGenericMethodDefinition(); | ||
|
||
|
@@ -84,13 +84,10 @@ public void GetValue_WithCompatibleDefaultValue_ShouldSucceed(string key, object | |
null, | ||
null, | ||
this.logger, | ||
null | ||
}; | ||
|
||
var actualValue = evaluateMethodDefinition.MakeGenericMethod(settingClrType).Invoke(null, args); | ||
var evaluationDetails = (EvaluationDetails)args.Last(); | ||
var evaluationDetails = (EvaluationDetails)evaluateMethodDefinition.MakeGenericMethod(settingClrType).Invoke(null, args); | ||
|
||
Assert.AreEqual(expectedValue, actualValue); | ||
Assert.AreEqual(expectedValue, evaluationDetails.Value); | ||
} | ||
|
||
|
@@ -110,7 +107,6 @@ public void GetValue_WithIncompatibleDefaultValueType_ShouldThrowWithImprovedErr | |
null, | ||
null, | ||
this.logger, | ||
null | ||
}; | ||
|
||
var ex = Assert.ThrowsException<InvalidOperationException>(() => | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -648,6 +648,224 @@ public async Task GetValueDetails_EvaluateServiceThrowException_ShouldReturnDefa | |
Assert.AreSame(actual, flagEvaluatedEvents[0].EvaluationDetails); | ||
} | ||
|
||
[DataRow(false)] | ||
[DataRow(true)] | ||
[DataTestMethod] | ||
public async Task GetAllValueDetails_ShouldReturnCorrectEvaluationDetails(bool isAsync) | ||
{ | ||
// Arrange | ||
|
||
const string cacheKey = "123"; | ||
var configJsonFilePath = Path.Combine("data", "sample_variationid_v5.json"); | ||
var timeStamp = DateTime.UtcNow; | ||
|
||
var client = CreateClientWithMockedFetcher(cacheKey, loggerMock, fetcherMock, | ||
onFetch: _ => FetchResult.Success(new ProjectConfig { JsonString = File.ReadAllText(configJsonFilePath), HttpETag = "12345", TimeStamp = timeStamp }), | ||
configServiceFactory: (fetcher, cacheParams, loggerWrapper) => | ||
{ | ||
return new ManualPollConfigService(fetcherMock.Object, cacheParams, loggerWrapper); | ||
}, | ||
evaluatorFactory: loggerWrapper => new RolloutEvaluator(loggerWrapper), new Hooks(), | ||
out var configService, out _); | ||
|
||
if (isAsync) | ||
{ | ||
await client.ForceRefreshAsync(); | ||
} | ||
else | ||
{ | ||
client.ForceRefresh(); | ||
} | ||
|
||
var flagEvaluatedEvents = new List<FlagEvaluatedEventArgs>(); | ||
client.FlagEvaluated += (s, e) => flagEvaluatedEvents.Add(e); | ||
|
||
var user = new User("[email protected]") { Email = "[email protected]" }; | ||
|
||
// Act | ||
|
||
var actual = isAsync | ||
? await client.GetAllValueDetailsAsync(user) | ||
: client.GetAllValueDetails(user); | ||
|
||
// Assert | ||
|
||
var expected = new[] | ||
{ | ||
new { Key = "boolean", Value = (object)true, VariationId = "67787ae4" }, | ||
new { Key = "text", Value = (object)"true", VariationId = "9bdc6a1f" }, | ||
new { Key = "whole", Value = (object)1, VariationId = "ab30533b" }, | ||
new { Key = "decimal", Value = (object)-2147483647.2147484, VariationId = "8f9559cf" }, | ||
}; | ||
|
||
foreach (var expectedItem in expected) | ||
{ | ||
var actualDetails = actual.FirstOrDefault(details => details.Key == expectedItem.Key); | ||
|
||
Assert.IsNotNull(actualDetails); | ||
Assert.AreEqual(expectedItem.Value, actualDetails.Value); | ||
Assert.IsFalse(actualDetails.IsDefaultValue); | ||
Assert.AreEqual(expectedItem.VariationId, actualDetails.VariationId); | ||
Assert.AreEqual(timeStamp, actualDetails.FetchTime); | ||
Assert.AreSame(user, actualDetails.User); | ||
Assert.IsNull(actualDetails.ErrorMessage); | ||
Assert.IsNull(actualDetails.ErrorException); | ||
Assert.IsNotNull(actualDetails.MatchedEvaluationRule); | ||
Assert.IsNull(actualDetails.MatchedEvaluationPercentageRule); | ||
|
||
var flagEvaluatedDetails = flagEvaluatedEvents.Select(e => e.EvaluationDetails).FirstOrDefault(details => details.Key == expectedItem.Key); | ||
|
||
Assert.IsNotNull(flagEvaluatedDetails); | ||
Assert.AreSame(actualDetails, flagEvaluatedDetails); | ||
} | ||
} | ||
|
||
[DataRow(false)] | ||
[DataRow(true)] | ||
[DataTestMethod] | ||
public async Task GetAllValueDetails_DeserializeFailed_ShouldReturnWithEmptyArray(bool isAsync) | ||
{ | ||
// Arrange | ||
|
||
configServiceMock.Setup(m => m.GetConfig()).Returns(ProjectConfig.Empty); | ||
configServiceMock.Setup(m => m.GetConfigAsync()).ReturnsAsync(ProjectConfig.Empty); | ||
var o = new SettingsWithPreferences(); | ||
configDeserializerMock | ||
.Setup(m => m.TryDeserialize(It.IsAny<string>(), It.IsAny<string>(), out o)) | ||
.Returns(false); | ||
|
||
using IConfigCatClient client = new ConfigCatClient(configServiceMock.Object, loggerMock.Object, evaluatorMock.Object, configDeserializerMock.Object, new Hooks()); | ||
|
||
var flagEvaluatedEvents = new List<FlagEvaluatedEventArgs>(); | ||
client.FlagEvaluated += (s, e) => flagEvaluatedEvents.Add(e); | ||
|
||
// Act | ||
|
||
var actual = isAsync | ||
? await client.GetAllValueDetailsAsync() | ||
: client.GetAllValueDetails(); | ||
|
||
// Assert | ||
|
||
Assert.IsNotNull(actual); | ||
Assert.AreEqual(0, actual.Count); | ||
Assert.AreEqual(0, flagEvaluatedEvents.Count); | ||
loggerMock.Verify(m => m.Error(It.IsAny<string>()), Times.Once); | ||
} | ||
|
||
[DataRow(false)] | ||
[DataRow(true)] | ||
[DataTestMethod] | ||
public async Task GetAllValueDetails_ConfigServiceThrowException_ShouldReturnEmptyEnumerable(bool isAsync) | ||
{ | ||
// Arrange | ||
|
||
configServiceMock | ||
.Setup(m => m.GetConfigAsync()) | ||
.Throws<Exception>(); | ||
|
||
using IConfigCatClient client = new ConfigCatClient(configServiceMock.Object, loggerMock.Object, evaluatorMock.Object, configDeserializerMock.Object, new Hooks()); | ||
|
||
var flagEvaluatedEvents = new List<FlagEvaluatedEventArgs>(); | ||
client.FlagEvaluated += (s, e) => flagEvaluatedEvents.Add(e); | ||
|
||
// Act | ||
|
||
var actual = isAsync | ||
? await client.GetAllValueDetailsAsync() | ||
: client.GetAllValueDetails(); | ||
|
||
// Assert | ||
|
||
Assert.IsNotNull(actual); | ||
Assert.AreEqual(0, actual.Count); | ||
Assert.AreEqual(0, flagEvaluatedEvents.Count); | ||
} | ||
|
||
[DataRow(false)] | ||
[DataRow(true)] | ||
[DataTestMethod] | ||
public async Task GetAllValueDetails_EvaluateServiceThrowException_ShouldReturnDefaultValue(bool isAsync) | ||
{ | ||
// Arrange | ||
|
||
const string errorMessage = "Error"; | ||
|
||
const string cacheKey = "123"; | ||
var configJsonFilePath = Path.Combine("data", "sample_variationid_v5.json"); | ||
var timeStamp = DateTime.UtcNow; | ||
|
||
evaluatorMock | ||
.Setup(m => m.Evaluate(It.IsAny<Setting>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<User>(), It.IsAny<ProjectConfig>(), It.IsNotNull<EvaluationDetailsFactory>())) | ||
.Throws(new ApplicationException(errorMessage)); | ||
|
||
var client = CreateClientWithMockedFetcher(cacheKey, loggerMock, fetcherMock, | ||
onFetch: _ => FetchResult.Success(new ProjectConfig { JsonString = File.ReadAllText(configJsonFilePath), HttpETag = "12345", TimeStamp = timeStamp }), | ||
configServiceFactory: (fetcher, cacheParams, loggerWrapper) => | ||
{ | ||
return new ManualPollConfigService(fetcherMock.Object, cacheParams, loggerWrapper); | ||
}, | ||
evaluatorFactory: _ => evaluatorMock.Object, new Hooks(), | ||
out var configService, out _); | ||
|
||
if (isAsync) | ||
{ | ||
await client.ForceRefreshAsync(); | ||
} | ||
else | ||
{ | ||
client.ForceRefresh(); | ||
} | ||
|
||
var flagEvaluatedEvents = new List<FlagEvaluatedEventArgs>(); | ||
var errorEvents = new List<ConfigCatClientErrorEventArgs>(); | ||
client.FlagEvaluated += (s, e) => flagEvaluatedEvents.Add(e); | ||
client.Error += (s, e) => errorEvents.Add(e); | ||
|
||
var user = new User("[email protected]") { Email = "[email protected]" }; | ||
|
||
// Act | ||
|
||
var actual = isAsync | ||
? await client.GetAllValueDetailsAsync(user) | ||
: client.GetAllValueDetails(user); | ||
|
||
// Assert | ||
|
||
foreach (var key in new[] { "boolean", "text", "whole", "decimal" }) | ||
{ | ||
var actualDetails = actual.FirstOrDefault(details => details.Key == key); | ||
|
||
Assert.IsNotNull(actualDetails); | ||
Assert.AreEqual(key, actualDetails.Key); | ||
Assert.IsNull(actualDetails.Value); | ||
Assert.IsTrue(actualDetails.IsDefaultValue); | ||
Assert.IsNull(actualDetails.VariationId); | ||
Assert.AreEqual(timeStamp, actualDetails.FetchTime); | ||
Assert.AreSame(user, actualDetails.User); | ||
Assert.AreEqual(errorMessage, actualDetails?.ErrorMessage); | ||
Assert.IsInstanceOfType(actualDetails.ErrorException, typeof(ApplicationException)); | ||
Assert.IsNull(actualDetails.MatchedEvaluationRule); | ||
Assert.IsNull(actualDetails.MatchedEvaluationPercentageRule); | ||
|
||
var flagEvaluatedDetails = flagEvaluatedEvents.Select(e => e.EvaluationDetails).FirstOrDefault(details => details.Key == key); | ||
|
||
Assert.IsNotNull(flagEvaluatedDetails); | ||
Assert.AreSame(actualDetails, flagEvaluatedDetails); | ||
} | ||
|
||
Assert.AreEqual(1, errorEvents.Count); | ||
var errorEventArgs = errorEvents[0]; | ||
StringAssert.Contains(errorEventArgs.Message, isAsync ? nameof(IConfigCatClient.GetAllValueDetailsAsync) : nameof(IConfigCatClient.GetAllValueDetails)); | ||
Assert.IsInstanceOfType(errorEventArgs.Exception, typeof(AggregateException)); | ||
var actualException = (AggregateException)errorEventArgs.Exception; | ||
Assert.AreEqual(actual.Count, actualException.InnerExceptions.Count); | ||
foreach (var ex in actualException.InnerExceptions) | ||
{ | ||
Assert.IsInstanceOfType(ex, typeof(ApplicationException)); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public async Task GetAllKeys_ConfigServiceThrowException_ShouldReturnsWithEmptyArray() | ||
{ | ||
|
Oops, something went wrong.