Beispiel #1
0
        public void Should_support_list_of_operators(string left, string actualOp, string right, bool rightIsExpr,
                                                     Operator expectedOp, Type exType = null, string errorMessage = null)
        {
            var boolStr  = rightIsExpr ? "true" : "false";
            var json     = $@"
{{
    ""left"": ""{left}"",
    ""operator"": ""{actualOp}"",
    ""right"": ""{right}"",
    ""RightSideIsExpression"": {boolStr}
}}";
            var expected = new LeafExpression()
            {
                Left     = left,
                Operator = expectedOp,
                Right    = right,
                RightSideIsExpression = rightIsExpr
            };

            if (exType != null)
            {
                Runner.RunScenario(
                    given => A_leaf_expression(json),
                    when => I_parse_json_expression_and_it_should_throw(exType, errorMessage));
            }
            else
            {
                Runner.RunScenario(
                    given => A_leaf_expression(json),
                    when => I_parse_json_expression(),
                    then => Parsed_expression_should_be(expected));
            }
        }
Beispiel #2
0
        public static Func <T, string> GetExpectation <T>(this LeafExpression leafExpression)
        {
            Func <T, string> getExpectation = leafExpression.RightSideIsExpression
                ? BuildGetStringFunc <T>(leafExpression.Right, true, leafExpression.Operator.ToString())
                : t => $"{leafExpression.Operator.ToString()} {leafExpression.Right}";

            return(getExpectation);
        }
Beispiel #3
0
        public void Constructor_ValidParameters_ConstructedCorrectly(string resourceType, string path)
        {
            var mockOperator   = new Mock <LeafExpressionOperator>().Object;
            var leafExpression = new LeafExpression(resourceType, path, mockOperator);

            Assert.AreEqual(resourceType, leafExpression.ResourceType);
            Assert.AreEqual(path, leafExpression.Path);
            Assert.AreEqual(mockOperator, leafExpression.Operator);
        }
Beispiel #4
0
        public void Evaluate_ValidScope_ReturnsResultsOfOperatorEvaluation(string resourceType, string path, bool expectedEvaluationResult, string jtoken, string absolutePathForEvaluateExpression, int timesResolveIsCalledOnOriginalObject)
        {
            // Arrange
            var ruleDefinition = new RuleDefinition
            {
                Name           = "testRule",
                Description    = "test rule",
                Recommendation = "test recommendation",
                HelpUri        = "https://helpUri"
            };

            // JObject to evaluate, in this test, this is a subset of an ARM template
            var jsonToEvaluate = JObject.Parse(jtoken);

            // Setting up the Mock JsonPathResolver to return the expected values when JToken and Resolve are called
            var mockJsonPathResolver = new Mock <IJsonPathResolver>();

            // The JToken property should return the JObject to evaluate
            mockJsonPathResolver
            .Setup(s => s.JToken)
            .Returns(jsonToEvaluate);
            // Resolve for the provided json path should return the JsonPathResolver
            mockJsonPathResolver
            .Setup(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))))
            .Returns(() => new[] { mockJsonPathResolver.Object });

            // Call a helper function to get the appropriate scope of the JObject
            // absolutePathForEvaluateExpression is null when resourceType is not defined
            // When resourceType is defined, absolutePathForEvaluateExpression represents the absolute
            // json path of the path parameter specified
            var jTokenExpectedInEvaluateExpression = GetRelevantJTokenScope(jsonToEvaluate, absolutePathForEvaluateExpression);

            // EvaluateExpression for the provided scope should return the expected evaluationResult
            var mockLeafExpressionOperator = new Mock <LeafExpressionOperator>();

            mockLeafExpressionOperator
            .Setup(o => o.EvaluateExpression(It.Is <JToken>(token => token == jTokenExpectedInEvaluateExpression)))
            .Returns(expectedEvaluationResult);

            var leafExpression = new LeafExpression(ruleDefinition, resourceType, path, mockLeafExpressionOperator.Object);

            // Act
            var results = leafExpression.Evaluate(jsonScope: mockJsonPathResolver.Object).ToList();

            // Assert
            // Verify the number of time Resolve, JToken, and EvaluateExpression were called
            mockJsonPathResolver.Verify(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))), Times.Exactly(timesResolveIsCalledOnOriginalObject));
            mockJsonPathResolver.Verify(s => s.JToken, Times.Once);

            mockLeafExpressionOperator.Verify(o => o.EvaluateExpression(It.Is <JToken>(token => token == jTokenExpectedInEvaluateExpression)), Times.Once);

            Assert.AreEqual(1, results.Count);
            Assert.AreEqual(expectedEvaluationResult, results.First().Passed);
            Assert.AreEqual(ruleDefinition, results.First().RuleDefinition);
        }
Beispiel #5
0
        public void Accept_single_filter()
        {
            var json     = $@"
{{
    ""left"": ""DeviceType"",
    ""operator"": ""Equals"",
    ""right"": ""Breaker""
}}";
            var expected = new LeafExpression()
            {
                Left     = "DeviceType",
                Operator = Operator.Equals,
                Right    = "Breaker",
                RightSideIsExpression = false
            };

            Runner.RunScenario(
                given => A_leaf_expression(json),
                when => I_parse_json_expression(),
                then => Parsed_expression_should_be(expected));
        }
Beispiel #6
0
        public static Func <T, double> GetScore <T>(this LeafExpression leafExpression)
        {
            var ctxExpression    = Expression.Parameter(typeof(T), "ctx");
            var targetExpression = ctxExpression.EvaluateExpression(
                leafExpression.Left,
                leafExpression.Operator != Operator.IsNull && leafExpression.Operator != Operator.NotIsNull);

            if (targetExpression.Type == typeof(int))
            {
                var lambda   = Expression.Lambda <Func <T, int> >(targetExpression, ctxExpression);
                var getValue = lambda.Compile();
                var expected = (int)Convert.ChangeType(leafExpression.Right, typeof(int));

                double getScore(T t)
                {
                    var actual = getValue(t);

                    switch (leafExpression.Operator)
                    {
                    case Operator.GreaterThan:
                        return(actual > expected ? 1.0 : (double)actual / expected);

                    case Operator.GreaterOrEqual:
                        return(actual >= expected ? 1.0 : (double)actual / expected);

                    case Operator.LessThan:
                        return(actual < expected ? 1.0 : (double)(actual - expected) / expected);

                    case Operator.LessOrEqual:
                        return(actual <= expected ? 1.0 : (double)(actual - expected) / expected);

                    default:
                        return(actual == expected ? 1.0 : 0.0);
                    }
                }

                return(getScore);
            }

            if (targetExpression.Type == typeof(double))
            {
                var lambda   = Expression.Lambda <Func <T, double> >(targetExpression, ctxExpression);
                var getValue = lambda.Compile();
                var expected = (double)Convert.ChangeType(leafExpression.Right, typeof(double));

                double getScore(T t)
                {
                    var actual = getValue(t);

                    switch (leafExpression.Operator)
                    {
                    case Operator.GreaterThan:
                        return(actual > expected ? 1.0 : actual / expected);

                    case Operator.GreaterOrEqual:
                        return(actual >= expected ? 1.0 : actual / expected);

                    case Operator.LessThan:
                        return(actual < expected ? 1.0 : (actual - expected) / expected);

                    case Operator.LessOrEqual:
                        return(actual <= expected ? 1.0 : (actual - expected) / expected);

                    default:
                        return(Math.Abs(actual - expected) < 0.001 ? 1.0 : 0.0);
                    }
                }

                return(getScore);
            }

            if (targetExpression.Type == typeof(decimal))
            {
                var lambda   = Expression.Lambda <Func <T, decimal> >(targetExpression, ctxExpression);
                var getValue = lambda.Compile();
                var expected = (decimal)Convert.ChangeType(leafExpression.Right, typeof(decimal));

                double getScore(T t)
                {
                    var actual = getValue(t);

                    switch (leafExpression.Operator)
                    {
                    case Operator.GreaterThan:
                        return(actual > expected ? 1.0 : (double)actual / (double)expected);

                    case Operator.GreaterOrEqual:
                        return(actual >= expected ? 1.0 : (double)actual / (double)expected);

                    case Operator.LessThan:
                        return(actual < expected ? 1.0 : (double)(actual - expected) / (double)expected);

                    case Operator.LessOrEqual:
                        return(actual <= expected ? 1.0 : (double)(actual - expected) / (double)expected);

                    default:
                        return(actual == expected ? 1.0 : 0.0);
                    }
                }

                return(getScore);
            }

            if (targetExpression.Type == typeof(string))
            {
                var lambda         = Expression.Lambda <Func <T, string> >(targetExpression, ctxExpression);
                var getValue       = lambda.Compile();
                var expectedString = leafExpression.Right;
                var expectedArray  = leafExpression.Right.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                     .Select(s => s.Trim()).ToArray();

                double getScore(T t)
                {
                    var actual = getValue(t);

                    switch (leafExpression.Operator)
                    {
                    case Operator.StartsWith:
                        if (expectedString is string expected1)
                        {
                            return(actual.StartsWith(expected1) ? 1.0 : 0.0);
                        }
                        return(0.0);

                    case Operator.NotStartsWith:
                        if (expectedString is string expected2)
                        {
                            return(!actual.StartsWith(expected2) ? 1.0 : 0.0);
                        }
                        return(0.0);

                    case Operator.Contains:
                        if (expectedString is string expected3)
                        {
                            return(actual.Contains(expected3) ? 1.0 : 0.0);
                        }
                        return(0.0);

                    case Operator.NotContains:
                        if (expectedString is string expected4)
                        {
                            return(!actual.Contains(expected4) ? 1.0 : 0.0);
                        }
                        return(0.0);

                    case Operator.In:
                        return(expectedArray.Contains(actual) ? 1.0 : 0.0);

                    case Operator.NotIn:
                        return(expectedArray.Contains(actual) ? 0.0 : 1.0);
                    }

                    if (expectedString is string expected)
                    {
                        return(actual == expected ? 1.0 : 0.0);
                    }
                    return(0.0);
                }

                return(getScore);
            }

            if (targetExpression.Type == typeof(bool))
            {
                var lambda   = Expression.Lambda <Func <T, bool> >(targetExpression, ctxExpression);
                var getValue = lambda.Compile();
                var expected = (bool)Convert.ChangeType(leafExpression.Right, typeof(bool));

                double getScore(T t)
                {
                    var actual = getValue(t);

                    return(actual == expected ? 1.0 : 0.0);
                }

                return(getScore);
            }

            if (targetExpression.Type == typeof(IEnumerable <string>))
            {
                var lambda         = Expression.Lambda <Func <T, IEnumerable <string> > >(targetExpression, ctxExpression);
                var getValue       = lambda.Compile();
                var expectedString = leafExpression.Right;
                var expectedArray  = leafExpression.Right.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                     .Select(s => s.Trim()).ToArray();

                double getScore(T t)
                {
                    var actual        = getValue(t)?.ToArray();
                    var commonCount   = actual?.Distinct().Intersect(expectedArray).Count() ?? 0;
                    var actualCount   = actual?.Count() ?? 0;
                    var expectedCount = expectedArray.Length;

                    switch (leafExpression.Operator)
                    {
                    case Operator.In:
                        return(actualCount == 0 ? 0 : (double)commonCount / actualCount);

                    case Operator.ContainsAll:
                        return(expectedCount == 0 ? 0 : (double)commonCount / expectedCount);

                    case Operator.NotIn:
                        return(actualCount == 0 ? 0 : (double)(actualCount - commonCount) / actualCount);

                    case Operator.Contains:
                        return(expectedString != null && actual?.Contains(expectedString) == true ? 1.0 : 0.0);

                    case Operator.NotContains:
                        return(expectedString == null || actual?.Contains(expectedString) == false ? 1.0 : 0.0);
                    }

                    return(actual == expectedArray ? 1.0 : 0.0);
                }

                return(getScore);
            }

            throw new NotSupportedException($"expression {leafExpression.Left} is not supported");
        }
 private static void ShouldBeEquivalent(LeafExpression actual, LeafExpression expected)
 {
     actual.Should().BeEquivalentTo(expected);
 }
Beispiel #8
0
        public void Evaluate_ValidScope_ReturnsResultsOfOperatorEvaluation(string resourceType, string path, bool expectedEvaluationResult)
        {
            // Arrange
            // JObject to evaluate, in this test, this is a subset of an ARM template
            var jsonToEvaluate        = JObject.Parse("{ \"property\": \"value\" }");
            var expectedPathEvaluated = "expectedPath";

            // Setting up the Mock JsonPathResolvers to return the expected values when JToken and Resolve are called
            var mockJsonPathResolver  = new Mock <IJsonPathResolver>();
            var mockResourcesResolved = new Mock <IJsonPathResolver>();

            // The JToken property should return the JObject to evaluate
            mockJsonPathResolver
            .Setup(s => s.JToken)
            .Returns(jsonToEvaluate);

            // Resolve for the provided json path should return a JsonPathResolver.
            // Both mocks need to be prepared to return a value.
            // We can just reuse mockJsonPathResolver in both cases.
            mockJsonPathResolver
            .Setup(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))))
            .Returns(new[] { mockJsonPathResolver.Object });
            mockResourcesResolved
            .Setup(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))))
            .Returns(new[] { mockJsonPathResolver.Object });

            // Setup the mock resolver to return a JSON path
            mockJsonPathResolver
            .Setup(s => s.Path)
            .Returns(expectedPathEvaluated);

            // ResolveResourceType for the provided resource type should return a JsonPathResolver.
            // Return the mock specifically for testing the call to ResolveResourceType.
            mockJsonPathResolver
            .Setup(s => s.ResolveResourceType(It.Is <string>(r => string.Equals(r, resourceType))))
            .Returns(new[] { mockResourcesResolved.Object });

            // EvaluateExpression for the provided scope should return the expected evaluationResult
            var mockLeafExpressionOperator = new Mock <LeafExpressionOperator>();

            mockLeafExpressionOperator
            .Setup(o => o.EvaluateExpression(It.Is <JToken>(token => token == jsonToEvaluate)))
            .Returns(expectedEvaluationResult);

            var leafExpression = new LeafExpression(resourceType, path, mockLeafExpressionOperator.Object);

            // Act
            var results = leafExpression.Evaluate(jsonScope: mockJsonPathResolver.Object).ToList();

            // Assert
            // Verify actions on resolvers.

            // If a resource type is passed, it should resolve for the resource type, and the path should be resolved on the mock resource type.
            // If no resource type is passed, it should resolve the path directly and not use the mock resource type.
            // ResolveResourceType should never be called on the mock returned from resolving resource types already.
            mockJsonPathResolver.Verify(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))), Times.Exactly(resourceType == null ? 1 : 0));
            mockJsonPathResolver.Verify(s => s.ResolveResourceType(It.Is <string>(r => string.Equals(r, resourceType))), Times.Exactly(resourceType == null ? 0 : 1));
            mockResourcesResolved.Verify(s => s.Resolve(It.Is <string>(p => string.Equals(p, path))), Times.Exactly(resourceType == null ? 0 : 1));
            mockResourcesResolved.Verify(s => s.ResolveResourceType(It.IsAny <string>()), Times.Never);

            // The original mock is returned from both mocks when calling Resolve for a path, so the JToken should always come from it.
            mockJsonPathResolver.Verify(s => s.JToken, Times.Once);
            mockResourcesResolved.Verify(s => s.JToken, Times.Never);

            mockLeafExpressionOperator.Verify(o => o.EvaluateExpression(It.Is <JToken>(token => token == jsonToEvaluate)), Times.Once);

            Assert.AreEqual(1, results.Count);
            Assert.AreEqual(expectedEvaluationResult, results.First().Passed);
            Assert.AreEqual(expectedPathEvaluated, results.First().JsonPath);
        }
Beispiel #9
0
        public void Evaluate_NullScope_ThrowsException()
        {
            var leafExpression = new LeafExpression(null, "path", new HasValueOperator(true, false));

            leafExpression.Evaluate(jsonScope: null).ToList();
        }
Beispiel #10
0
 public static Func <T, string> GetEvidence <T>(this LeafExpression leafExpression)
 {
     return(BuildGetStringFunc <T>(
                leafExpression.Left,
                leafExpression.Operator != Operator.IsNull && leafExpression.Operator != Operator.NotIsNull));
 }