Merge pull request #152 from Azure/nonik/miscfixes

Fixes for issues #137 and #141
This commit is contained in:
Nick Brown 2021-07-12 17:28:51 -07:00 коммит произвёл GitHub
Родитель dd3cae1a26 7c2fd3d18c
Коммит 1a10c0bc60
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 208 добавлений и 260 удалений

Просмотреть файл

@ -1,159 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions;
using Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Operators;
using Microsoft.Azure.Templates.Analyzer.Types;
using Microsoft.Azure.Templates.Analyzer.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using static Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests.TestUtilities;
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
{
[TestClass]
public class AnyOfExpressionTests
{
[DataTestMethod]
[DataRow(true, true, DisplayName = "AnyOf evaluates to true (true || true)")]
[DataRow(true, false, DisplayName = "AnyOf evaluates to true (true || false)")]
[DataRow(false, true, DisplayName = "AnyOf evaluates to true (false || true)")]
[DataRow(false, false, DisplayName = "AnyOf evaluates to false (false || false)")]
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "AnyOf - scoped to resourceType - evaluates to false (false || false)")]
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "AnyOf - scoped to resourceType and path - evaluates to false (false || false)")]
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturned(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
{
// Arrange
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
var mockLineResolver = new Mock<ILineNumberResolver>().Object;
// This AnyOf will have 2 expressions
var mockOperator1 = new Mock<LeafExpressionOperator>().Object;
var mockOperator2 = new Mock<LeafExpressionOperator>().Object;
var mockLeafExpression1 = new Mock<LeafExpression>(mockLineResolver, mockOperator1, new ExpressionCommonProperties { ResourceType = "ResourceProvider/resource", Path = "some.path" });
var mockLeafExpression2 = new Mock<LeafExpression>(mockLineResolver, mockOperator2, new ExpressionCommonProperties { ResourceType = "ResourceProvider/resource", Path = "some.path" });
var jsonRuleResult1 = new JsonRuleResult
{
Passed = evaluation1
};
var jsonRuleResult2 = new JsonRuleResult
{
Passed = evaluation2
};
mockJsonPathResolver
.Setup(s => s.Resolve(It.IsAny<string>()))
.Returns(new List<IJsonPathResolver> { mockJsonPathResolver.Object });
if (!string.IsNullOrEmpty(resourceType))
{
mockJsonPathResolver
.Setup(s => s.ResolveResourceType(It.Is<string>(type => type == resourceType)))
.Returns(new List<IJsonPathResolver> { mockJsonPathResolver.Object });
}
var results1 = new JsonRuleResult[] { jsonRuleResult1 };
var results2 = new JsonRuleResult[] { jsonRuleResult2 };
mockLeafExpression1
.Setup(s => s.Evaluate(mockJsonPathResolver.Object))
.Returns(new JsonRuleEvaluation(mockLeafExpression1.Object, evaluation1, results1));
mockLeafExpression2
.Setup(s => s.Evaluate(mockJsonPathResolver.Object))
.Returns(new JsonRuleEvaluation(mockLeafExpression2.Object, evaluation2, results2));
var expressionArray = new Expression[] { mockLeafExpression1.Object, mockLeafExpression2.Object };
var anyOfExpression = new AnyOfExpression(expressionArray, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
// Act
var anyOfEvaluation = anyOfExpression.Evaluate(mockJsonPathResolver.Object);
// Assert
bool expectedAnyOfEvaluation = evaluation1 || evaluation2;
Assert.AreEqual(expectedAnyOfEvaluation, anyOfEvaluation.Passed);
Assert.AreEqual(2, anyOfEvaluation.Evaluations.Count());
Assert.IsTrue(anyOfEvaluation.HasResults);
int expectedTrue = new[] { evaluation1, evaluation2 }.Count(e => e);
int expectedFalse = 2 - expectedTrue;
Assert.AreEqual(expectedTrue, anyOfEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedFalse, anyOfEvaluation.EvaluationsEvaluatedFalse.Count());
}
[DataTestMethod]
[DataRow(null, true, true, DisplayName = "First expression not evaluated, second expression passes, overall result is pass")]
[DataRow(null, false, false, DisplayName = "First expression not evaluated, second expression fails, overall result is fail")]
[DataRow(true, null, true, DisplayName = "First expression passes, second expression not evaluated, overall result is pass")]
[DataRow(null, null, true, DisplayName = "All expressions not evaluated, overall result is pass")]
public void Evaluate_SubResourceScopeNotFound_ExpectedResultIsReturned(bool? firstExpressionPass, bool? secondExpressionPass, bool overallPass)
{
string evaluatedPath = "some.evaluated.path";
string notEvaluatedPath = "path.not.evaluated";
// Arrange
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
mockJsonPathResolver
.Setup(r => r.Resolve(It.Is<string>(path => evaluatedPath.Equals(path))))
.Returns(() => new[] { mockJsonPathResolver.Object });
var mockLineResolver = new Mock<ILineNumberResolver>().Object;
// Create 2 expressions for the AnyOf.
// Whether each is evaluated or not is determined by the path passed to each.
var mockLeafExpression1 = new MockExpression(new ExpressionCommonProperties { Path = firstExpressionPass.HasValue ? evaluatedPath : notEvaluatedPath })
{
EvaluationCallback = pathResolver => firstExpressionPass.HasValue
? new JsonRuleEvaluation(null, firstExpressionPass.Value, new[] { new JsonRuleResult { Passed = firstExpressionPass.Value } })
: null
};
var mockLeafExpression2 = new MockExpression(new ExpressionCommonProperties { Path = secondExpressionPass.HasValue ? evaluatedPath : notEvaluatedPath })
{
EvaluationCallback = pathResolver => secondExpressionPass.HasValue
? new JsonRuleEvaluation(null, secondExpressionPass.Value, new[] { new JsonRuleResult { Passed = secondExpressionPass.Value } })
: null
};
var anyOfExpression = new AnyOfExpression(
new Expression[] { mockLeafExpression1, mockLeafExpression2 },
new ExpressionCommonProperties());
var expectedResults = new[] { firstExpressionPass, secondExpressionPass };
// Act
var anyOfEvaluation = anyOfExpression.Evaluate(mockJsonPathResolver.Object);
// Assert
Assert.AreEqual(overallPass, anyOfEvaluation.Passed);
Assert.AreEqual(expectedResults.Count(r => r.HasValue), anyOfEvaluation.Evaluations.Count());
Assert.AreEqual(expectedResults.Any(r => r.HasValue), anyOfEvaluation.HasResults);
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), anyOfEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), anyOfEvaluation.EvaluationsEvaluatedFalse.Count());
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Evaluate_NullScope_ThrowsException()
{
new AnyOfExpression(new Expression[0], new ExpressionCommonProperties()).Evaluate(jsonScope: null);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullExpressions_ThrowsException()
{
new AnyOfExpression(null, new ExpressionCommonProperties());
}
}
}

Просмотреть файл

@ -15,16 +15,37 @@ using static Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
{
[TestClass]
public class AllOfExpressionTests
public class StructuredExpressionTests
{
private readonly Func<bool, bool, bool> DummyOperation = (x, y) => x ^ y;
// tests functionality of AllOfExpression
[DataTestMethod]
[DataRow(true, true, DisplayName = "AllOf evaluates to true (true && true)")]
[DataRow(true, false, DisplayName = "AllOf evaluates to false (true && false)")]
[DataRow(false, true, DisplayName = "AllOf evaluates to false (false && true)")]
[DataRow(false, false, DisplayName = "AllOf evaluates to false (false && false)")]
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "AllOf - scoped to resourceType - evaluates to false (false && false)")]
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "AllOf - scoped to resourceType and path - evaluates to false (false && false)")]
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturned(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
[DataRow(true, true, DisplayName = "Evaluates to true (true && true)")]
[DataRow(true, false, DisplayName = "Evaluates to false (true && false)")]
[DataRow(false, true, DisplayName = "Evaluates to false (false && true)")]
[DataRow(false, false, DisplayName = "Evaluates to false (false && false)")]
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "Scoped to resourceType - Evaluates to false (false && false)")]
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "Scoped to resourceType and path - Evaluates to false (false && false)")]
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturnedForAndOperations(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
{
Evaluate_TwoLeafExpressions(evaluation1, evaluation2, (x, y) => x && y, resourceType, path);
}
// tests functionality of AnyOfExpression
[DataTestMethod]
[DataRow(true, true, DisplayName = "Evaluates to true (true || true)")]
[DataRow(true, false, DisplayName = "Evaluates to true (true || false)")]
[DataRow(false, true, DisplayName = "Evaluates to true (false || true)")]
[DataRow(false, false, DisplayName = "Evaluates to false (false || false)")]
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "Scoped to resourceType - Evaluates to false (false || false)")]
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "Scoped to resourceType and path - Evaluates to false (false || false)")]
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturnedForOrOperations(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
{
Evaluate_TwoLeafExpressions(evaluation1, evaluation2, (x, y) => x || y, resourceType, path);
}
private static void Evaluate_TwoLeafExpressions(bool evaluation1, bool evaluation2, Func<bool, bool, bool> operation, string resourceType = null, string path = null)
{
// Arrange
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
@ -71,24 +92,24 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
var expressionArray = new Expression[] { mockLeafExpression1.Object, mockLeafExpression2.Object };
var allOfExpression = new AllOfExpression(expressionArray, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
var structuredExpression = new StructuredExpression(expressionArray, operation, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
// Act
var allOfEvaluation = allOfExpression.Evaluate(mockJsonPathResolver.Object);
var structuredEvaluation = structuredExpression.Evaluate(mockJsonPathResolver.Object);
// Assert
bool expectedAllOfEvaluation = evaluation1 && evaluation2;
Assert.AreEqual(expectedAllOfEvaluation, allOfEvaluation.Passed);
Assert.AreEqual(2, allOfEvaluation.Evaluations.Count());
Assert.IsTrue(allOfEvaluation.HasResults);
bool expectedCompoundEvaluation = operation(evaluation1, evaluation2);
Assert.AreEqual(expectedCompoundEvaluation, structuredEvaluation.Passed);
Assert.AreEqual(2, structuredEvaluation.Evaluations.Count());
Assert.IsTrue(structuredEvaluation.HasResults);
int expectedTrue = new[] { evaluation1, evaluation2 }.Count(e => e);
int expectedFalse = 2 - expectedTrue;
Assert.AreEqual(expectedTrue, allOfEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedFalse, allOfEvaluation.EvaluationsEvaluatedFalse.Count());
Assert.AreEqual(expectedTrue, structuredEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedFalse, structuredEvaluation.EvaluationsEvaluatedFalse.Count());
foreach (var evaluation in allOfEvaluation.Evaluations)
foreach (var evaluation in structuredEvaluation.Evaluations)
{
// Assert all leaf expressions have results and no evaluations
Assert.IsTrue(evaluation.HasResults);
@ -132,36 +153,37 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
: null
};
var allOfExpression = new AllOfExpression(
var structuredExpression = new StructuredExpression(
new Expression[] { mockLeafExpression1, mockLeafExpression2 },
DummyOperation,
new ExpressionCommonProperties());
var expectedResults = new[] { firstExpressionPass, secondExpressionPass };
// Act
var allOfEvaluation = allOfExpression.Evaluate(mockJsonPathResolver.Object);
var structuredEvaluation = structuredExpression.Evaluate(mockJsonPathResolver.Object);
// Assert
Assert.AreEqual(overallPass, allOfEvaluation.Passed);
Assert.AreEqual(expectedResults.Count(r => r.HasValue), allOfEvaluation.Evaluations.Count());
Assert.AreEqual(expectedResults.Any(r => r.HasValue), allOfEvaluation.HasResults);
Assert.AreEqual(overallPass, structuredEvaluation.Passed);
Assert.AreEqual(expectedResults.Count(r => r.HasValue), structuredEvaluation.Evaluations.Count());
Assert.AreEqual(expectedResults.Any(r => r.HasValue), structuredEvaluation.HasResults);
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), allOfEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), allOfEvaluation.EvaluationsEvaluatedFalse.Count());
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), structuredEvaluation.EvaluationsEvaluatedTrue.Count());
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), structuredEvaluation.EvaluationsEvaluatedFalse.Count());
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Evaluate_NullScope_ThrowsException()
{
new AllOfExpression(new Expression[0], new ExpressionCommonProperties()).Evaluate(jsonScope: null);
new StructuredExpression(Array.Empty<Expression>(), DummyOperation, new ExpressionCommonProperties()).Evaluate(jsonScope: null);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullExpressions_ThrowsException()
{
new AllOfExpression(null, new ExpressionCommonProperties());
new StructuredExpression(null, DummyOperation, new ExpressionCommonProperties());
}
}
}

Просмотреть файл

@ -1,21 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Azure.Templates.Analyzer.Types;
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
{
/// <summary>
/// Represents an allOf expression in a JSON rule.
/// </summary>
internal class AllOfExpression : Expression
internal class AllOfExpression : StructuredExpression
{
/// <summary>
/// Gets the expressions to be evaluated.
/// </summary>
public Expression[] AllOf { get; private set; }
public Expression[] AllOf
{
get => Expressions;
private set => Expressions = value;
}
/// <summary>
/// Creates an <see cref="AllOfExpression"/>.
@ -23,37 +23,8 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
/// <param name="expressions">List of expressions to perform a logical AND against.</param>
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
public AllOfExpression(Expression[] expressions, ExpressionCommonProperties commonProperties)
: base(commonProperties)
: base(expressions, (x, y) => x && y, commonProperties)
{
this.AllOf = expressions ?? throw new ArgumentNullException(nameof(expressions));
}
/// <summary>
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>.
/// </summary>
/// <param name="jsonScope">The json to evaluate.</param>
/// <returns>A <see cref="JsonRuleEvaluation"/> with the results of the evaluation.</returns>
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
{
return EvaluateInternal(jsonScope, scope =>
{
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
bool evaluationPassed = true;
foreach (var expression in AllOf)
{
var evaluation = expression.Evaluate(scope);
// Add evaluations if scopes were found to evaluate
if (evaluation.HasResults)
{
evaluationPassed &= evaluation.Passed;
jsonRuleEvaluations.Add(evaluation);
}
}
return new JsonRuleEvaluation(this, evaluationPassed, jsonRuleEvaluations);
});
}
}
}

Просмотреть файл

@ -1,21 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Azure.Templates.Analyzer.Types;
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
{
/// <summary>
/// Represents an anyOf expression in a JSON rule.
/// Represents an AnyOf expression in a JSON rule.
/// </summary>
internal class AnyOfExpression : Expression
internal class AnyOfExpression : StructuredExpression
{
/// <summary>
/// Gets or sets the expressions to be evaluated.
/// Gets the expressions to be evaluated.
/// </summary>
public Expression[] AnyOf { get; private set; }
public Expression[] AnyOf
{
get => Expressions;
private set => Expressions = value;
}
/// <summary>
/// Creates an <see cref="AnyOfExpression"/>.
@ -23,36 +23,8 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
/// <param name="expressions">List of expressions to perform a logical OR against.</param>
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
public AnyOfExpression(Expression[] expressions, ExpressionCommonProperties commonProperties)
: base(commonProperties)
: base(expressions, (x, y) => x || y, commonProperties)
{
this.AnyOf = expressions ?? throw new ArgumentNullException(nameof(expressions));
}
/// <summary>
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>
/// </summary>
/// <param name="jsonScope">The json to evaluate.</param>
/// <returns>A <see cref="JsonRuleEvaluation"/> with zero or more results of the evaluation, depending on whether there are any/multiple resources of the given type,
/// and if the path contains any wildcards.</returns>
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
{
return EvaluateInternal(jsonScope, scope =>
{
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
foreach (var expression in AnyOf)
{
var evaluation = expression.Evaluate(scope);
// Add evaluations if scopes were found to evaluate
if (evaluation.HasResults)
{
jsonRuleEvaluations.Add(evaluation);
}
}
bool evaluationPassed = jsonRuleEvaluations.Count == 0 || jsonRuleEvaluations.Exists(e => e.Passed);
return new JsonRuleEvaluation(this, evaluationPassed, jsonRuleEvaluations);
});
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Azure.Templates.Analyzer.Types;
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
{
/// <summary>
/// Represents a structured expression in a JSON rule.
/// </summary>
internal class StructuredExpression : Expression
{
/// <summary>
/// Determines the boolean operation to use when evaluating the expressions.
/// </summary>
private readonly Func<bool, bool, bool> Operation;
/// <summary>
/// Gets or sets the expressions to be evaluated.
/// </summary>
internal Expression[] Expressions { get; set; }
/// <summary>
/// Creates a <see cref="StructuredExpression"/>.
/// </summary>
/// <param name="expressions">List of expressions to perform a logical operation against.</param>
/// <param name="operation">The boolean operation to perform to calculate the overall expression result.</param>
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
public StructuredExpression(Expression[] expressions, Func<bool,bool, bool> operation, ExpressionCommonProperties commonProperties)
: base(commonProperties)
{
Expressions = expressions ?? throw new ArgumentNullException(nameof(expressions));
Operation = operation;
}
/// <summary>
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>
/// </summary>
/// <param name="jsonScope">The json to evaluate.</param>
/// <returns>A <see cref="JsonRuleEvaluation"/> with zero or more results of the evaluation, depending on whether there are any/multiple resources of the given type,
/// and if the path contains any wildcards.</returns>
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
{
return EvaluateInternal(jsonScope, scope =>
{
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
bool? evaluationPassed = null;
foreach (var expression in Expressions)
{
var evaluation = expression.Evaluate(scope);
// Add evaluations if scopes were found to evaluate
if (evaluation.HasResults)
{
// if no value, this is the first expression evaluated so set the inital value of result
if (!evaluationPassed.HasValue)
{
evaluationPassed = evaluation.Passed;
}
// otherwise use defined operation to calculate intermediate expression result
else
{
evaluationPassed = Operation(evaluationPassed.Value, evaluation.Passed);
}
jsonRuleEvaluations.Add(evaluation);
}
}
evaluationPassed |= jsonRuleEvaluations.Count == 0;
return new JsonRuleEvaluation(this, evaluationPassed.Value, jsonRuleEvaluations);
});
}
}
}

Просмотреть файл

@ -157,7 +157,7 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor.UnitTests
}
[TestMethod]
public void ProcessResourcesAndOutputs_ValidTemplateWithExpressionInResourceProperties_ProcessResourceProperyLanguageExpressions()
public void ProcessResourcesAndOutputs_ValidTemplateWithExpressionInResourceProperties_ProcessResourcePropertyLanguageExpressions()
{
string templateJson = @"{
""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"",
@ -665,6 +665,67 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor.UnitTests
AssertDictionariesAreEqual(expectedMapping, armTemplateProcessor.ResourceMappings);
}
[TestMethod]
public void CopyResourceDependants_DependsOnSpecifiedInNestedChildResource()
{
// Arrange
string resourceJson = @"{
""type"": ""Microsoft.Sql/servers"",
""apiVersion"": ""2020-02-02-preview"",
""name"": ""sqlServerName"",
""location"": ""location"",
""properties"": {},
""resources"": [
{
""type"": ""databases"",
""apiVersion"": ""2020-02-02-preview"",
""name"": ""dataWarehouseName"",
""location"": ""location'"",
""kind"": ""v12.0,user,datawarehouse"",
""dependsOn"": [
""sqlServerName""
],
""properties"": {},
""resources"": [
{
""type"": ""transparentDataEncryption"",
""apiVersion"": ""2017-03-01-preview"",
""name"": ""current"",
""dependsOn"": [
""dataWarehouseName""
],
""properties"": {}
}
]
}
]
}";
Dictionary<string, string> expectedMapping = new Dictionary<string, string> {
{ "resources[0]", "resources[0]" },
{ "resources[0].resources[0]", "resources[0].resources[0]" },
{ "resources[0].resources[0].resources[0]", "resources[0].resources[0].resources[0]" }
};
TemplateResource templateResource = JObject.Parse(resourceJson).ToObject<TemplateResource>(SerializerSettings.SerializerWithObjectTypeSettings);
ArmTemplateProcessor armTemplateProcessor = new ArmTemplateProcessor(GenerateTemplateWithResources(new TemplateResource[] { templateResource }));
// Act
var template = armTemplateProcessor.ProcessTemplate();
// Assert
string expectedResourceArray = $@"[ {resourceJson} ]";
var expectedResourceJArray = JArray.Parse(expectedResourceArray);
(expectedResourceJArray[0] as JObject).Add("dependsOn", new JArray());
var actualResourceArray = template.ToJToken().InsensitiveToken("resources");
Assert.IsTrue(JToken.DeepEquals(expectedResourceJArray, actualResourceArray));
AssertDictionariesAreEqual(expectedMapping, armTemplateProcessor.ResourceMappings);
}
[TestMethod]
public void CopyResourceDependants_DependsOnIsChained_CopiesToCorrectResource()
{

Просмотреть файл

@ -234,7 +234,10 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor
AddResourceMapping($"{parentResource.Path}.resources[0]", templateResource.Path);
}
else
// check if resource is already a child of parent resource
else if (!parentResource.Resources.Any(res =>
res.Name.Value == templateResource.Name.Value &&
res.Type.Value == templateResource.Type.Value))
{
var childResources = parentResource.Resources;
parentResource.Resources = childResources.ConcatArray(new TemplateResource[] { templateResource });