Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rynowak committed Nov 5, 2021
1 parent 7f39c85 commit 695fcce
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 153 deletions.
92 changes: 79 additions & 13 deletions src/Bicep.Core.IntegrationTests/ModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Bicep.Core.UnitTests.Utils;
using Bicep.Core.Workspaces;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -496,7 +497,7 @@ public void Module_with_resource_type_output_can_be_evaluated()
result.Template.Should().HaveValueAtPath("$.outputs.name", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(reference(resourceId('Microsoft.Resources/deployments', 'test'), '2020-06-01').outputs.storage.value, '2019-06-01', 'full').name]"),
["value"] = new JValue("[last(split(reference(resourceId('Microsoft.Resources/deployments', 'test'), '2020-06-01').outputs.storage.value, '/'))]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.type", new JObject()
{
Expand All @@ -516,35 +517,100 @@ public void Module_with_resource_type_output_can_be_evaluated()
}

[TestMethod]
public void Module_with_unknown_resourcetype_as_parameter_and_output_gets_diagnostics()
public void Module_array_with_resource_type_output_can_be_evaluated()
{
var result = CompilationHelper.Compile(
("main.bicep", @"
module mod './module.bicep' = [for (item, i) in []: {
name: 'test-${i}'
}]
output id string = mod[0].outputs.storage.id
output name string = mod[0].outputs.storage.name
output type string = mod[0].outputs.storage.type
output apiVersion string = mod[0].outputs.storage.apiVersion
output accessTier string = mod[0].outputs.storage.properties.accessTier
"),
("module.bicep", @"
resource storage 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
name: 'test'
}
output storage resource = storage
"));
result.Should().NotHaveAnyDiagnostics();

result.Template.Should().HaveValueAtPath("$.outputs.id", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(resourceId('Microsoft.Resources/deployments', format('test-{0}', 0)), '2020-06-01').outputs.storage.value]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.name", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[last(split(reference(resourceId('Microsoft.Resources/deployments', format('test-{0}', 0)), '2020-06-01').outputs.storage.value, '/'))]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.type", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("Microsoft.Storage/storageAccounts"),
});
result.Template.Should().HaveValueAtPath("$.outputs.apiVersion", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("2019-06-01"),
});
result.Template.Should().HaveValueAtPath("$.outputs.accessTier", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(reference(resourceId('Microsoft.Resources/deployments', format('test-{0}', 0)), '2020-06-01').outputs.storage.value, '2019-06-01').accessTier]"),
});
}

[TestMethod]
public void Module_with_unknown_resourcetype_as_parameter_and_output_has_diagnostics()
{
// Note that we get diagnostics inside 'main.bicep' without trying to use the parameter
// or output.
var result = CompilationHelper.Compile(
("main.bicep", @"
module mod './module.bicep' = {
name: 'test'
params: {
p: 'something'
}
}
output foo resource = mod.outputs.storage
"),
("module.bicep", @"
param p resource 'Some.Fake/Type@2019-06-01'
resource fake 'Another.Fake/Type@2019-06-01' existing = {
resource fake 'Another.Fake/Type@2019-06-01' = {
name: 'test'
properties: {
value: p.properties.value
}
}
output storage resource 'Another.Fake/Type@2019-06-01' = fake
"));
var diagnosticsMap = result.Compilation.GetAllDiagnosticsByBicepFile().ToDictionary(kvp => kvp.Key.FileUri.AbsolutePath, kvp => kvp.Value);;
diagnosticsMap["/path/to/main.bicep"].Should().HaveDiagnostics(new []
var diagnosticsMap = result.Compilation.GetAllDiagnosticsByBicepFile().ToDictionary(kvp => kvp.Key.FileUri.AbsolutePath, kvp => kvp.Value);
using (new AssertionScope())
{
("BCP036", DiagnosticLevel.Error, "The property \"p\" expected a value of type \"Microsoft.Sql/servers\" but the provided value is of type \"Microsoft.Storage/storageAccounts@2019-06-01\"."),
});
diagnosticsMap["/path/to/module.bicep"].Should().HaveDiagnostics(new []
{
("BCP036", DiagnosticLevel.Error, "The property \"p\" expected a value of type \"Microsoft.Sql/servers\" but the provided value is of type \"Microsoft.Storage/storageAccounts@2019-06-01\"."),
});
diagnosticsMap["/path/to/module.bicep"].Should().HaveDiagnostics(new []
{
("BCP081", DiagnosticLevel.Warning, "Resource type \"Some.Fake/Type@2019-06-01\" does not have types available."),
("BCP081", DiagnosticLevel.Warning, "Resource type \"Another.Fake/Type@2019-06-01\" does not have types available."),
("BCP081", DiagnosticLevel.Warning, "Resource type \"Another.Fake/Type@2019-06-01\" does not have types available."),
});
diagnosticsMap["/path/to/main.bicep"].Should().HaveDiagnostics(new []
{
("BCP228", DiagnosticLevel.Warning, "The referenced module uses resource type \"Some.Fake/Type@2019-06-01\" which does not have types available."),
("BCP228", DiagnosticLevel.Warning, "The referenced module uses resource type \"Another.Fake/Type@2019-06-01\" which does not have types available."),
("BCP036", DiagnosticLevel.Error, "The property \"p\" expected a value of type \"Some.Fake/Type\" but the provided value is of type \"'something'\"."),
});
}
}

[TestMethod]
Expand Down
107 changes: 68 additions & 39 deletions src/Bicep.Core.IntegrationTests/OutputsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,62 +108,91 @@ public void Output_can_have_specified_resource_type()
}

[TestMethod]
public void Output_cannot_use_extensibility_resource_type()
public void Output_can_have_decorators()
{
var result = CompilationHelper.Compile(GetExtensibilityCompilationContext(), @"
import stg from storage {
connectionString: 'asdf'
var result = CompilationHelper.Compile(@"
resource resource 'Microsoft.Storage/storageAccounts@2019-06-01' = {
name: 'test'
location: 'eastus'
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
identity: {
type: 'SystemAssigned'
}
properties:{
accessTier: 'Cool'
}
}
resource container 'stg:container' = {
name: 'myblob'
}
output out resource = container
@description('cool beans')
output out resource 'Microsoft.Storage/storageAccounts@2019-06-01' = resource
");
result.Should().HaveDiagnostics(new []
result.Should().NotHaveAnyDiagnostics();

result.Template.Should().HaveValueAtPath("$.outputs.out", new JObject()
{
("BCP226", DiagnosticLevel.Error, "The type \"container\" cannot be used as an output type. Extensibility types are currently not supported as parameters or outputs."),
["type"] = "string",
["value"] = "[resourceId('Microsoft.Storage/storageAccounts', 'test')]",
["metadata"] = new JObject()
{
["resourceType"] = new JValue("Microsoft.Storage/storageAccounts@2019-06-01"),
["description"] = new JValue("cool beans"),
},
});
}

[TestMethod]
public void Output_with_resource_type_can_have_properties_evaluated()
public void Output_can_have_warnings_for_missing_type()
{
var result = CompilationHelper.Compile(@"
param p resource 'Microsoft.Storage/storageAccounts@2019-06-01'
output id string = p.id
output name string = p.name
output type string = p.type
output apiVersion string = p.apiVersion
output accessTier string = p.properties.accessTier
resource resource 'Some.Fake/Type@2019-06-01' = {
name: 'test'
}
output out resource 'Some.Fake/Type@2019-06-01' = resource
");
result.Should().NotHaveAnyDiagnostics();

result.Template.Should().HaveValueAtPath("$.outputs.id", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[parameters('p')]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.name", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(parameters('p'), '2019-06-01', 'full').name]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.type", new JObject()
result.Should().HaveDiagnostics(new []
{
["type"] = new JValue("string"),
["value"] = new JValue("Microsoft.Storage/storageAccounts"),
// There are two warnings because there are two places in code to correct the missing type.
("BCP081", DiagnosticLevel.Warning, "Resource type \"Some.Fake/Type@2019-06-01\" does not have types available."),
("BCP081", DiagnosticLevel.Warning, "Resource type \"Some.Fake/Type@2019-06-01\" does not have types available."),
});
result.Template.Should().HaveValueAtPath("$.outputs.apiVersion", new JObject()
}

[TestMethod]
public void Output_can_have_warnings_for_missing_type_but_we_dont_duplicate_them_when_type_is_inferred()
{
// As a special case we don't show a warning on the output when the type is inferred
// the user only has one location in code to correct.
var result = CompilationHelper.Compile(@"
resource resource 'Some.Fake/Type@2019-06-01' = {
name: 'test'
}
output out resource = resource
");
result.Should().HaveDiagnostics(new []
{
["type"] = new JValue("string"),
["value"] = new JValue("2019-06-01"),
("BCP081", DiagnosticLevel.Warning, "Resource type \"Some.Fake/Type@2019-06-01\" does not have types available."),
});
result.Template.Should().HaveValueAtPath("$.outputs.accessTier", new JObject()
}

[TestMethod]
public void Output_cannot_use_extensibility_resource_type()
{
var result = CompilationHelper.Compile(GetExtensibilityCompilationContext(), @"
import stg from storage {
connectionString: 'asdf'
}
resource container 'stg:container' = {
name: 'myblob'
}
output out resource = container
");
result.Should().HaveDiagnostics(new []
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(parameters('p'), '2019-06-01').accessTier]"),
("BCP226", DiagnosticLevel.Error, "The type \"container\" cannot be used as an output type. Extensibility types are currently not supported as parameters or outputs."),
});
}
}
Expand Down
51 changes: 50 additions & 1 deletion src/Bicep.Core.IntegrationTests/ParametersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,41 @@ param p resource 'Microsoft.Storage/storageAccounts@2019-06-01'
});
}

[TestMethod]
public void Parameter_with_resource_type_can_have_decorators()
{
var result = CompilationHelper.Compile(@"
@description('cool')
param p resource 'Microsoft.Storage/storageAccounts@2019-06-01'
resource resource 'Microsoft.Storage/storageAccounts@2019-06-01' = {
name: 'test'
location: 'eastus'
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
identity: {
type: 'SystemAssigned'
}
properties:{
accessTier: p.properties.accessTier
}
}
");
result.Should().NotHaveAnyDiagnostics();

result.Template.Should().HaveValueAtPath("$.parameters.p", new JObject()
{
["type"] = new JValue("string"),
["metadata"] = new JObject()
{
["resourceType"] = new JValue("Microsoft.Storage/storageAccounts@2019-06-01"),
["description"] = new JValue("cool"),
},
});
}

[TestMethod]
public void Parameter_with_resource_type_can_have_properties_evaluated()
{
Expand All @@ -98,7 +133,7 @@ param p resource 'Microsoft.Storage/storageAccounts@2019-06-01'
result.Template.Should().HaveValueAtPath("$.outputs.name", new JObject()
{
["type"] = new JValue("string"),
["value"] = new JValue("[reference(parameters('p'), '2019-06-01', 'full').name]"),
["value"] = new JValue("[last(split(parameters('p'), '/'))]"),
});
result.Template.Should().HaveValueAtPath("$.outputs.type", new JObject()
{
Expand All @@ -117,6 +152,20 @@ param p resource 'Microsoft.Storage/storageAccounts@2019-06-01'
});
}

[TestMethod]
public void Parameter_can_have_warnings_for_missing_type()
{
var result = CompilationHelper.Compile(@"
param p resource 'Some.Fake/Type@2019-06-01'
output id string = p.id
");
result.Should().HaveDiagnostics(new []
{
("BCP081", DiagnosticLevel.Warning, "Resource type \"Some.Fake/Type@2019-06-01\" does not have types available."),
});
}

[TestMethod]
public void Parameter_cannot_use_extensibility_resource_type()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ param invalidResourceType resource 'Some/fake'
param unknownResourceType resource 'Some/fake@2020-01-01'
//@[6:25) [no-unused-params (Warning)] Parameter "unknownResourceType" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-params)) |unknownResourceType|
//@[35:57) [BCP081 (Warning)] Resource type "Some/fake@2020-01-01" does not have types available. (CodeDescription: none) |'Some/fake@2020-01-01'|
param invalidDefaultValueObject resource 'Microsoft.Storage/storageAccounts@2019-06-01' = {}
//@[6:31) [no-unused-params (Warning)] Parameter "invalidDefaultValueObject" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-params)) |invalidDefaultValueObject|
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,12 @@ public ErrorDiagnostic UnknownModuleReferenceScheme(string badScheme, ImmutableA
TextSpan,
"BCP227",
$"The parameter \"{parameterName}\" cannot be used as a resource scope or parent. Resources passed as parameters cannot be used as a scope or parent of a resource.");

public Diagnostic ModuleParamOrOutputResourceTypeUnavailable(ResourceTypeReference resourceTypeReference) => new(
TextSpan,
DiagnosticLevel.Warning,
"BCP228",
$"The referenced module uses resource type \"{resourceTypeReference.FormatName()}\" which does not have types available.");
}

public static DiagnosticBuilderInternal ForPosition(TextSpan span)
Expand Down
Loading

0 comments on commit 695fcce

Please sign in to comment.