Skip to content

Commit

Permalink
added option to include errors in tweek-api values result (#1132)
Browse files Browse the repository at this point in the history
* split files

* return all error from jpad calculations

* add option to propagate errors to tweek response

* add e2e-integration .dockerignore file

* add unit test

* move smoke tests to integration tests

* propagate error tests

* added EnsureSuccess method to TweekValuesResult

* rename propagateErrors -> includeErrors

* split to files
  • Loading branch information
nataly87s committed Apr 7, 2019
1 parent 3c1952a commit 9abc93c
Show file tree
Hide file tree
Showing 25 changed files with 534 additions and 386 deletions.
96 changes: 69 additions & 27 deletions core/Engine/Tweek.Engine.Tests/EngineTests.cs
Expand Up @@ -41,7 +41,7 @@ public EngineTests(TestDriverFixture fixture)

async Task Run(Func<ITweek, IContextDriver, Task> test)
{
var scope = driver.SetTestEnviornment(contexts, paths, rules);
var scope = driver.SetTestEnvironment(contexts, paths, rules);
await scope.Run(test);
}

Expand All @@ -58,13 +58,13 @@ public async Task CalculateSingleValue()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("_", NoIdentities, context);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", NoIdentities, context);
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/somepath", NoIdentities, context);
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

Expand All @@ -78,7 +78,7 @@ public async Task CalculateMultipleValues()

await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", NoIdentities, context);
var val = (await tweek.GetContextAndCalculate("abc/_", NoIdentities, context)).Data;
Assert.Equal(3, val.Count);
Assert.Equal("SomeValue",val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue",val["abc/otherpath"].Value.AsString());
Expand All @@ -96,7 +96,7 @@ public async Task CalculateMultiplePathQueries()

await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate(new List<ConfigurationPath>{"abc/_", "def/_"}, NoIdentities, context);
var val = (await tweek.GetContextAndCalculate(new List<ConfigurationPath>{"abc/_", "def/_"}, NoIdentities, context)).Data;
Assert.Equal(4, val.Count);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/otherpath"].Value.AsString());
Expand All @@ -115,7 +115,7 @@ public async Task CalculateMultiplePathQueriesWithOverlap()

await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate(new List<ConfigurationPath> { "abc/_", "abc/nested/_" }, NoIdentities, context);
var val = (await tweek.GetContextAndCalculate(new List<ConfigurationPath> { "abc/_", "abc/nested/_" }, NoIdentities, context)).Data;
Assert.Equal(3, val.Count);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val["abc/otherpath"].Value.AsString());
Expand All @@ -142,13 +142,13 @@ public async Task CalculateFilterByMatcher()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal(0, val.Count);
Assert.Equal(0, val.Data.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal(0, val.Count);
Assert.Equal(0, val.Data.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "3") }, context);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

Expand All @@ -171,13 +171,13 @@ public async Task CalculateFilterByMatcherWithMultiIdentities()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal(0, val.Count);
Assert.Equal(0, val.Data.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("user", "1") }, context);
Assert.Equal(0, val.Count);
Assert.Equal(0, val.Data.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1"), new Identity("user", "1") }, context);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

Expand All @@ -196,7 +196,7 @@ public async Task MultipleRules()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal( "SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal( "SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

Expand All @@ -222,7 +222,7 @@ public async Task MultipleRulesWithFallback()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

Expand All @@ -248,12 +248,14 @@ public async Task CalculateWithMultiVariant()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { }, context);
Assert.Equal(0, val.Count);
Assert.Equal(0, val.Data.Count);
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1")}, context);
Assert.True(val["abc/somepath"].Value.AsString() == "true" || val["abc/somepath"].Value.AsString() == "false");
Assert.True(val.Data["abc/somepath"].Value.AsString() == "true" || val.Data["abc/somepath"].Value.AsString() == "false");
await Task.WhenAll(Enumerable.Range(0, 10).Select(async x =>
{
Assert.Equal((await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context))["abc/somepath"].Value, val["abc/somepath"].Value);
var expected = val.Data["abc/somepath"].Value;
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> {new Identity("device", "1")}, context);
Assert.Equal(val.Data["abc/somepath"].Value, expected);
}));
});
}
Expand All @@ -276,7 +278,7 @@ public async Task ContextKeysShouldBeCaseInsensitive()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("true", val["abc/somepath"].Value.AsString());
Assert.Equal("true", val.Data["abc/somepath"].Value.AsString());
});
}

Expand Down Expand Up @@ -306,10 +308,10 @@ public async Task RuleUsingTimeBasedOperators()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("true", val["abc/somepath"].Value.AsString());
Assert.Equal("true", val.Data["abc/somepath"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal("false", val["abc/somepath"].Value.AsString());
Assert.Equal("false", val.Data["abc/somepath"].Value.AsString());
});
}
Expand All @@ -334,13 +336,13 @@ public async Task CalculateWithFixedValue()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
Assert.Equal("FixedValue", val["abc/somepath"].Value.AsString());
Assert.Equal("FixedValue", val.Data["abc/somepath"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
Assert.Equal("RuleBasedValue", val["abc/somepath"].Value.AsString());
Assert.Equal("RuleBasedValue", val.Data["abc/somepath"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "3") }, context);
Assert.Equal("FixedValue", val["abc/somepath"].Value.AsString());
Assert.Equal("FixedValue", val.Data["abc/somepath"].Value.AsString());
});
}
Expand Down Expand Up @@ -371,11 +373,11 @@ public async Task CalculateWithRecursiveMatcher()

await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context);
var val = (await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "1") }, context)).Data;
Assert.Equal(1, val.Count);
Assert.Equal("true", val["abc/dep_path1"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context);
val = (await tweek.GetContextAndCalculate("abc/_", new HashSet<Identity> { new Identity("device", "2") }, context)).Data;
Assert.Equal(3, val.Count);
Assert.Equal("true", val["abc/dep_path1"].Value.AsString());
Assert.Equal("true", val["abc/dep_path2"].Value.AsString());
Expand All @@ -399,9 +401,49 @@ public async Task EmptyFixedKeyIsIgnoredInScan()
await Run(async (tweek, context) =>
{
var val = await tweek.GetContextAndCalculate("_", new HashSet<Identity> { new Identity("device", "1")}, context);
Assert.Equal("SomeValue", val["abc/somepath"].Value.AsString());
Assert.Equal("SomeValue", val.Data["abc/somepath"].Value.AsString());
});
}

[Fact]
public async Task BadKeyShouldReturnError()
{
contexts = ContextCreator.Create("device", "1", Tuple.Create("SomeDeviceProp", JsonValue.NewNumber(3)));

paths = new[] { "abc/bad_path", "abc/good_path" };
rules = new Dictionary<string, RuleDefinition>
{
["abc/bad_path"] = JPadGenerator.New()
.AddSingleVariantRule(JsonConvert.SerializeObject(new MatcherData {["device.SomeDeviceProp"] = new string[]{}}), "BadValue")
.Generate(),
["abc/good_path"] = JPadGenerator.New().AddSingleVariantRule("{}", "SomeValue").Generate()
};

await Run(async (tweek, context) =>
{
var identities = new HashSet<Identity> {new Identity("device", "1")};
var val = await tweek.GetContextAndCalculate("_", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.False(val.Data.ContainsKey("abc/bad_path"));
Assert.Equal("SomeValue", val.Data["abc/good_path"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/_", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.False(val.Data.ContainsKey("abc/bad_path"));
Assert.Equal( "SomeValue", val.Data["abc/good_path"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/good_path", identities, context);
Assert.Equal(0, val.Errors.Count);
Assert.Equal( "SomeValue", val.Data["abc/good_path"].Value.AsString());
val = await tweek.GetContextAndCalculate("abc/bad_path", identities, context);
Assert.Equal(1, val.Errors.Count);
Assert.True(val.Errors.ContainsKey("abc/bad_path"));
Assert.Equal(0, val.Data.Count);
});
}
}
}
2 changes: 1 addition & 1 deletion core/Engine/Tweek.Engine.Tests/TestDrivers/ITestDriver.cs
Expand Up @@ -9,6 +9,6 @@ namespace Tweek.Engine.Tests.TestDrivers
public interface ITestDriver
{
IContextDriver Context { get; }
TestScope SetTestEnviornment(Dictionary<Identity, Dictionary<string, JsonValue>> contexts, string[] keys, Dictionary<string,RuleDefinition> rules);
TestScope SetTestEnvironment(Dictionary<Identity, Dictionary<string, JsonValue>> contexts, string[] keys, Dictionary<string,RuleDefinition> rules);
}
}
Expand Up @@ -81,7 +81,7 @@ private async Task InsertContextRows(Dictionary<Identity, Dictionary<string, Jso

private async Task Flush() => dictionary.Clear();

public TestScope SetTestEnviornment(Dictionary<Identity, Dictionary<string, JsonValue>> contexts, string[] keys,
public TestScope SetTestEnvironment(Dictionary<Identity, Dictionary<string, JsonValue>> contexts, string[] keys,
Dictionary<string, RuleDefinition> rules)
{
return new TestScope(rules: new InMemoryRulesRepository(rules), context: Context,
Expand Down
25 changes: 0 additions & 25 deletions core/Engine/Tweek.Engine/FallbackRule.cs

This file was deleted.

15 changes: 15 additions & 0 deletions core/Engine/Tweek.Engine/ITweek.cs
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using Tweek.Engine.Core.Context;
using Tweek.Engine.DataTypes;
using IdentityHashSet = System.Collections.Generic.HashSet<Tweek.Engine.DataTypes.Identity>;

namespace Tweek.Engine
{
public interface ITweek
{
TweekValuesResult Calculate(
ICollection<ConfigurationPath> pathQuery,
IdentityHashSet identities, GetLoadedContextByIdentityType context,
ConfigurationPath[] includeFixedPaths = null);
}
}
15 changes: 15 additions & 0 deletions core/Engine/Tweek.Engine/Tweek.cs
@@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Tweek.Engine.Drivers.Rules;
using Tweek.Engine.Rules.Creation;

namespace Tweek.Engine
{
public static class Tweek
{
public static async Task<ITweek> Create(IRulesRepository rulesRepository, GetRuleParser parserResolver)
{
var rulesLoader = await RulesLoader.Factory(rulesRepository, parserResolver);
return new TweekRunner(rulesLoader);
}
}
}

0 comments on commit 9abc93c

Please sign in to comment.