Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parent property implementation #1800

Merged
merged 20 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cc56e22
MVP for parent property
anthony-c-martin Mar 9, 2021
2ad9838
Use name as-is for nested/parent resource property access
anthony-c-martin Mar 9, 2021
8577272
Add some assertions for parent/nested resources & scopes
anthony-c-martin Mar 10, 2021
73aef17
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 11, 2021
e73f3c0
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 11, 2021
20081f3
Add some scope validations
anthony-c-martin Mar 11, 2021
bfac1ae
Inherit scope from oldest ancestor
anthony-c-martin Mar 11, 2021
008dead
Fix dependsOn generation for resource access syntax
anthony-c-martin Mar 11, 2021
aba5af0
Update test baselines
Mar 11, 2021
b023eb2
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 12, 2021
bcc737b
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 12, 2021
9b1084c
Make parent writable; fix tests
anthony-c-martin Mar 12, 2021
3b4b760
Add validation for literal resource names
anthony-c-martin Mar 12, 2021
a613559
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 12, 2021
61f16bb
Add loop tests
anthony-c-martin Mar 12, 2021
efe088e
Loops implementation
anthony-c-martin Mar 13, 2021
c81ad77
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 13, 2021
54466fd
Add handling & tests for parent property cycles
anthony-c-martin Mar 16, 2021
709a41f
Merge remote-tracking branch 'origin/main' into antmarti/parent_property
anthony-c-martin Mar 16, 2021
016c843
Reword error messages
anthony-c-martin Mar 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 54 additions & 30 deletions src/Bicep.Core.IntegrationTests/NestedResourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Bicep.Core.TypeSystem;
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Diagnostics;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using FluentAssertions.Execution;
Expand Down Expand Up @@ -415,10 +416,10 @@ public void NestedResources_ancestors_are_detected()
model.ResourceAncestors.GetAncestors(parent).Should().BeEmpty();

var child = model.Root.GetAllResourceDeclarations().Single(r => r.Name == "child");
model.ResourceAncestors.GetAncestors(child).Should().Equal(new []{ parent, });
model.ResourceAncestors.GetAncestors(child).Select(x => x.Resource).Should().Equal(new []{ parent, });

var grandchild = model.Root.GetAllResourceDeclarations().Single(r => r.Name == "grandchild");
model.ResourceAncestors.GetAncestors(grandchild).Should().Equal(new []{ parent, child, }); // order matters
model.ResourceAncestors.GetAncestors(grandchild).Select(x => x.Resource).Should().Equal(new []{ parent, child, }); // order matters
}

[TestMethod]
Expand Down Expand Up @@ -464,22 +465,22 @@ public void NestedResources_scopes_isolate_names()
model.ResourceAncestors.GetAncestors(parent).Should().BeEmpty();

var child = model.Root.GetAllResourceDeclarations().Single(r => r.Name == "child");
model.ResourceAncestors.GetAncestors(child).Should().Equal(new []{ parent, });
model.ResourceAncestors.GetAncestors(child).Select(x => x.Resource).Should().Equal(new []{ parent, });

var childGrandChild = (ResourceSymbol)model.GetSymbolInfo(child.DeclaringResource.GetBody().Resources.Single())!;
model.ResourceAncestors.GetAncestors(childGrandChild).Should().Equal(new []{ parent, child, });
model.ResourceAncestors.GetAncestors(childGrandChild).Select(x => x.Resource).Should().Equal(new []{ parent, child, });

var sibling = model.Root.GetAllResourceDeclarations().Single(r => r.Name == "sibling");
model.ResourceAncestors.GetAncestors(child).Should().Equal(new []{ parent, });
model.ResourceAncestors.GetAncestors(child).Select(x => x.Resource).Should().Equal(new []{ parent, });

var siblingGrandChild = (ResourceSymbol)model.GetSymbolInfo(sibling.DeclaringResource.GetBody().Resources.Single())!;
model.ResourceAncestors.GetAncestors(siblingGrandChild).Should().Equal(new []{ parent, sibling, });
model.ResourceAncestors.GetAncestors(siblingGrandChild).Select(x => x.Resource).Should().Equal(new []{ parent, sibling, });
}

[TestMethod] // Should turn into positive test when support is added.
public void NestedResources_cannot_appear_inside_loops()
{
var program = @"
var (template, diags, _) = CompilationHelper.Compile(@"
var items = [
'a'
'b'
Expand All @@ -495,19 +496,21 @@ public void NestedResources_cannot_appear_inside_loops()
}
}
}]
";
");

var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxTreeGroupingFactory.CreateFromText(program));
var model = compilation.GetEntrypointSemanticModel();
compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().HaveDiagnostics(new[] {
("BCP160", DiagnosticLevel.Error, "A nested resource cannot appear inside of a resource with a for-expression."),
});
using (new AssertionScope())
{
template.Should().NotHaveValue();
diags.ExcludingMissingTypes().Should().HaveDiagnostics(new[] {
("BCP160", DiagnosticLevel.Error, "A nested resource cannot appear inside of a resource with a for-expression."),
});
}
}

[TestMethod]
public void NestedResources_can_have_loops()
{
var program = @"
var (template, diags, _) = CompilationHelper.Compile(@"
var items = [
'a'
'b'
Expand All @@ -525,18 +528,13 @@ public void NestedResources_can_have_loops()
}

output loopy string = parent::child[0].name
";

var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxTreeGroupingFactory.CreateFromText(program));
var model = compilation.GetEntrypointSemanticModel();
compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty();

var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), BicepTestConstants.DevAssemblyFileVersion);
using var outputStream = new MemoryStream();
emitter.Emit(outputStream);
");

outputStream.Seek(0L, SeekOrigin.Begin);
var text = Encoding.UTF8.GetString(outputStream.GetBuffer());
using (new AssertionScope())
{
diags.ExcludingMissingTypes().Should().BeEmpty();
template.Should().NotBeNull();
}
}

[TestMethod]
Expand Down Expand Up @@ -671,7 +669,7 @@ public void Nested_resource_works_with_extension_resources()

using (new AssertionScope())
{
diags.Where(x => x.Code != "BCP081").Should().BeEmpty();
diags.ExcludingMissingTypes().Should().BeEmpty();

// res1
template.Should().HaveValueAtPath("$.resources[2].name", "res1");
Expand Down Expand Up @@ -716,7 +714,7 @@ public void Nested_resource_works_with_existing_resources()

using (new AssertionScope())
{
diags.Where(x => x.Code != "BCP081").Should().BeEmpty();
diags.ExcludingMissingTypes().Should().BeEmpty();

// res1::child1
template.Should().HaveValueAtPath("$.resources[0].name", "[format('{0}/{1}', 'res1', 'child1')]");
Expand Down Expand Up @@ -752,7 +750,7 @@ public void Nested_resource_formats_references_correctly_for_existing_resources(

using (new AssertionScope())
{
diags.Where(x => x.Code != "BCP081").Should().BeEmpty();
diags.ExcludingMissingTypes().Should().BeEmpty();

template.Should().NotHaveValueAtPath("$.resources[0]");

Expand Down Expand Up @@ -780,7 +778,7 @@ public void Nested_resource_blocks_existing_parents_at_different_scopes()
using (new AssertionScope())
{
template.Should().NotHaveValue();
diags.Where(x => x.Code != "BCP081").Should().HaveDiagnostics(new[] {
diags.ExcludingMissingTypes().Should().HaveDiagnostics(new[] {
("BCP165", DiagnosticLevel.Error, "Cannot deploy a resource with ancestor under a different scope. Resource \"res1\" has the \"scope\" property set."),
});
}
Expand All @@ -807,11 +805,37 @@ public void Nested_resource_blocks_scope_on_child_resources()
using (new AssertionScope())
{
template.Should().NotHaveValue();
diags.Where(x => x.Code != "BCP081").Should().HaveDiagnostics(new[] {
diags.ExcludingMissingTypes().Should().HaveDiagnostics(new[] {
("BCP164", DiagnosticLevel.Error, "The \"scope\" property is unsupported for a resource with a parent resource. This resource has \"res2\" declared as its parent."),
});
}
}

[TestMethod]
public void Nested_resource_detects_invalid_child_resource_literal_names()
{
var (template, diags, _) = CompilationHelper.Compile(@"
resource res1 'Microsoft.Rp1/resource1@2020-06-01' = {
name: 'res1'

resource res2 'child' = {
name: 'res1/res2'
}

resource res3 'child' = {
name: '${res1.name}/res2'
}
}
");

using (new AssertionScope())
{
template.Should().NotHaveValue();
diags.ExcludingMissingTypes().Should().HaveDiagnostics(new[] {
("BCP170", DiagnosticLevel.Error, "Expected resource name to not contain any \"/\" characters. Child resources with a parent resource reference (via the parent property or via nesting) must not contain a fully-qualified name."),
("BCP170", DiagnosticLevel.Error, "Expected resource name to not contain any \"/\" characters. Child resources with a parent resource reference (via the parent property or via nesting) must not contain a fully-qualified name."),
});
}
}
}
}
Loading