コード例 #1
0
        public void If_ResLocationIs_AnyOtherStringLiteral_ShouldFail_AndOfferToCreateNewParameter()
        {
            var result = CompilationHelper.Compile(@"
                resource appInsightsComponents 'Microsoft.Insights/components@2020-02-02-preview' = {
                  name: 'name'
                  location: 'non-global'
                  kind: 'web'
                  properties: {
                    Application_Type: 'web'
                  }
                }"
                                                   );

            CodeFix x = result.Diagnostics.First().Should().BeAssignableTo <IFixable>()
                        .Which.Fixes.Single();

            x.Should().BeEquivalentTo(
                new
            {
                Description  = "Create new parameter 'location' with default value 'non-global'",
                Replacements = new[] {
                    // Replacement 1: change 'non-global' to 'location'
                    new { Text = "location" },
                    // Replacement 1: add new location param
                    new { Text = "@description('Specifies the location for resources.')\nparam location string = 'non-global'\n\n" }
                }
            });
        }
コード例 #2
0
        public void If_NameLocationAlreadyInUse_ShouldChooseAnotherNameForFix()
        {
            var result = CompilationHelper.Compile(@"
                var location = 'fee fie'
                param location2 string
                resource location3 'Microsoft.Insights/components@2020-02-02-preview' = {
                  name: 'name'
                  location: 'non-global'
                  kind: 'web'
                  properties: {
                    Application_Type: 'web'
                  }
                }
                output location4 string = '${location}${location2}'
                ");

            CodeFix x = result.Diagnostics.First().Should().BeAssignableTo <IFixable>()
                        .Which.Fixes.Single();

            x.Should().BeEquivalentTo(
                new
            {
                Description  = "Create new parameter 'location5' with default value 'non-global'",
                Replacements = new[] {
                    // Replacement 1: change 'non-global' to 'location5'
                    new { Text = "location5" },
                    // Replacement 1: add new location param
                    new { Text = "@description('Specifies the location for resources.')\nparam location5 string = 'non-global'\n\n" }
                }
            });
        }
コード例 #3
0
        public void Emitter_should_generate_correct_extension_scope_property_and_correct_dependsOn()
        {
            var(json, diags) = CompilationHelper.Compile(@"
resource resourceA 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceA'
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  scope: resourceA
  name: 'resourceB'
}

resource resourceC 'My.Rp/myResource@2020-01-01' = {
  scope: resourceB
  name: 'resourceC'
}");

            json.Should().NotBeNull();
            var template = JObject.Parse(json !);

            using (new AssertionScope())
            {
                template.SelectToken("$.resources[?(@.name == 'resourceB')].scope") !.ToString().Should().Be("[format('My.Rp/myResource/{0}', 'resourceA')]");
                template.SelectToken("$.resources[?(@.name == 'resourceB')].dependsOn[0]") !.ToString().Should().Be("[resourceId('My.Rp/myResource', 'resourceA')]");

                template.SelectToken("$.resources[?(@.name == 'resourceC')].scope") !.ToString().Should().Be("[extensionResourceId(format('My.Rp/myResource/{0}', 'resourceA'), 'My.Rp/myResource', 'resourceB')]");
                template.SelectToken("$.resources[?(@.name == 'resourceC')].dependsOn[0]") !.ToString().Should().Be("[extensionResourceId(format('My.Rp/myResource/{0}', 'resourceA'), 'My.Rp/myResource', 'resourceB')]");
            }
        }
コード例 #4
0
        public void List_wildcard_function_on_resource_references()
        {
            var result = CompilationHelper.Compile(@"
resource stg 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: 'testacc'
  location: 'West US'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

#disable-next-line outputs-should-not-contain-secrets
output pkStandard string = listKeys(stg.id, stg.apiVersion).keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethod string = stg.listKeys().keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethodVersionOverride string = stg.listKeys('2021-01-01').keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethodPayload string = stg.listKeys(stg.apiVersion, {
  key1: 'val1'
})
");

            result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
            result.Template.Should().HaveValueAtPath("$.outputs['pkStandard'].value", "[listKeys(resourceId('Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethod'].value", "[listKeys(resourceId('Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethodVersionOverride'].value", "[listKeys(resourceId('Microsoft.Storage/storageAccounts', 'testacc'), '2021-01-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethodPayload'].value", "[listKeys(resourceId('Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01', createObject('key1', 'val1'))]");
        }
コード例 #5
0
ファイル: ErrorBuilderTests.cs プロジェクト: husains/bicep
        private void ExpectDiagnosticWithFixedText(string text, string expectedText)
        {
            var result = CompilationHelper.Compile(text);

            result.Diagnostics.Should().HaveCount(1);

            FixableDiagnostic diagnostic = (FixableDiagnostic)result.Diagnostics.Single();

            diagnostic.Code.Should().Be("BCP035");
            diagnostic.Fixes.Should().HaveCount(1);

            var fix = diagnostic.Fixes.Single();

            fix.Replacements.Should().HaveCount(1);

            var replacement = fix.Replacements.Single();

            var actualText = text.Remove(replacement.Span.Position, replacement.Span.Length);

            actualText = actualText.Insert(replacement.Span.Position, replacement.Text);

            // Normalize line endings
            expectedText = expectedText.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
            actualText   = actualText.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);

            actualText.Should().Be(expectedText);
        }
コード例 #6
0
        public void List_wildcard_function_on_cross_scope_resource_references()
        {
            var result = CompilationHelper.Compile(@"
resource stg 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
  scope: resourceGroup('other')
  name: 'testacc'
}

#disable-next-line outputs-should-not-contain-secrets
output pkStandard string = listKeys(stg.id, stg.apiVersion).keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethod string = stg.listKeys().keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethodVersionOverride string = stg.listKeys('2021-01-01').keys[0].value
#disable-next-line outputs-should-not-contain-secrets
output pkMethodPayload string = stg.listKeys(stg.apiVersion, {
  key1: 'val1'
})
");

            result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
            result.Template.Should().HaveValueAtPath("$.outputs['pkStandard'].value", "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'other'), 'Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethod'].value", "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'other'), 'Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethodVersionOverride'].value", "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'other'), 'Microsoft.Storage/storageAccounts', 'testacc'), '2021-01-01').keys[0].value]");
            result.Template.Should().HaveValueAtPath("$.outputs['pkMethodPayload'].value", "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'other'), 'Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01', createObject('key1', 'val1'))]");
        }
コード例 #7
0
        private void RunWithDiagnosticAnnotations(string bicepText, Func <IDiagnostic, bool> filterFunc, OnCompileErrors onCompileErrors, Action <IEnumerable <IDiagnostic> > assertAction)
        {
            using (AssertionScope scope = new AssertionScope())
            {
                scope.AddReportable("bicep code", bicepText);

                var result = CompilationHelper.Compile(bicepText);
                result.Should().NotHaveDiagnosticsWithCodes(new[] { LinterAnalyzer.LinterRuleInternalError }, "There should never be linter LinterRuleInternalError errors");

                if (onCompileErrors == OnCompileErrors.Fail)
                {
                    var compileErrors = result.Diagnostics.Where(d => d.Level == DiagnosticLevel.Error);
                    DiagnosticAssertions.DoWithDiagnosticAnnotations(
                        result.Compilation.SourceFileGrouping.EntryPoint,
                        compileErrors,
                        diags => diags.Should().HaveCount(0));
                }

                IDiagnostic[] diagnosticsMatchingCode = result.Diagnostics.Where(filterFunc).ToArray();
                DiagnosticAssertions.DoWithDiagnosticAnnotations(
                    result.Compilation.SourceFileGrouping.EntryPoint,
                    result.Diagnostics.Where(filterFunc),
                    assertAction);
            }
        }
コード例 #8
0
        public void Casing_issues_are_corrected()
        {
            var bicepFile = @"
resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    lowerCaseProp: 'abc'
    camelcaseprop: 'def'
    'lowerCaseQuoted=+.Prop': 'ghi'
    'camelcasequoted=+.prop': 'jkl'
    lowerCaseEnumProp: 'MyEnum'
    pascalCaseEnumProp: 'myenum'
    lowerCaseEnumUnionProp: 'MyEnum'
    pascalCaseEnumUnionProp: 'myenum'
  }
}

output myObj object = {
  lowerCaseProp: resA.properties.lowerCaseProp
  camelcaseprop: resA.properties.camelcaseprop
}
";

            var typeDefinition = ResourceTypeProviderHelper.CreateCustomResourceType("My.Rp/resA", "2020-01-01", TypeSymbolValidationFlags.WarnOnTypeMismatch,
                                                                                     new TypeProperty("lowercaseprop", LanguageConstants.String),
                                                                                     new TypeProperty("camelCaseProp", LanguageConstants.String),
                                                                                     new TypeProperty("lowercasequoted=+.prop", LanguageConstants.String),
                                                                                     new TypeProperty("camelCaseQuoted=+.Prop", LanguageConstants.String),
                                                                                     new TypeProperty("lowerCaseEnumProp", new StringLiteralType("myenum")),
                                                                                     new TypeProperty("pascalCaseEnumProp", new StringLiteralType("MyEnum")),
                                                                                     new TypeProperty("lowerCaseEnumUnionProp", UnionType.Create(new StringLiteralType("myenum"), new StringLiteralType("blahblah"))),
                                                                                     new TypeProperty("pascalCaseEnumUnionProp", UnionType.Create(new StringLiteralType("MyEnum"), new StringLiteralType("BlahBlah"))));
            var typeProvider = ResourceTypeProviderHelper.CreateMockTypeProvider(typeDefinition.AsEnumerable());

            var(_, _, compilation) = CompilationHelper.Compile(typeProvider, ("main.bicep", bicepFile));
            var rewriter = new TypeCasingFixerRewriter(compilation.GetEntrypointSemanticModel());

            var newProgramSyntax = rewriter.Rewrite(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax);

            PrintHelper.PrettyPrint(newProgramSyntax).Should().Be(
                @"resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    lowercaseprop: 'abc'
    camelCaseProp: 'def'
    'lowercasequoted=+.prop': 'ghi'
    'camelCaseQuoted=+.Prop': 'jkl'
    lowerCaseEnumProp: 'myenum'
    pascalCaseEnumProp: 'MyEnum'
    lowerCaseEnumUnionProp: 'myenum'
    pascalCaseEnumUnionProp: 'MyEnum'
  }
}

output myObj object = {
  lowerCaseProp: resA.properties.lowercaseprop
  camelcaseprop: resA.properties.camelCaseProp
}");
        }
コード例 #9
0
        protected IDiagnostic[] GetDiagnostics(string ruleCode, string text)
        {
            var compilationResult  = CompilationHelper.Compile(text);
            var internalRuleErrors = compilationResult.Diagnostics.Where(d => d.Code == LinterAnalyzer.FailedRuleCode).ToArray();

            internalRuleErrors.Count().Should().Be(0, "There should never be linter FailedRuleCode errors");

            return(compilationResult.Diagnostics.OfType <IDiagnostic>().Where(d => d.Code == ruleCode).ToArray());
        }
コード例 #10
0
        public void AssigningResourceToVariable_ShouldNotGenerateVariables_ChainedCondition()
        {
            var result = CompilationHelper.Compile(@"
param mode int = 0

resource storage1_1 'Microsoft.Storage/storageAccounts@2019-06-01' = if (mode == 1) {
  name: 'test11'
  location: 'eastus'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}
resource storage1_2 'Microsoft.Storage/storageAccounts@2019-06-01' = if (mode != 1) {
  name: 'test12'
  location: 'eastus'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

var refResource = ref3
var ref1 = storage1_1
var ref2 = storage1_2
var ref3 = mode == 1 ? ref1 : ref2

resource storage2 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: 'test2'
  location: 'eastus'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    allowBlobPublicAccess: refResource.properties.allowBlobPublicAccess
  }
}
");

            result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
            using (new AssertionScope())
            {
                result.Template.Should().NotHaveValueAtPath("$.variables", "variable should not be generated");
                result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'test2')].dependsOn",
                                                         new JArray
                {
                    "[resourceId('Microsoft.Storage/storageAccounts', 'test11')]",
                    "[resourceId('Microsoft.Storage/storageAccounts', 'test12')]"
                },
                                                         "Referenced resource should be added to depends on section");
                result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'test2')].properties.allowBlobPublicAccess",
                                                         "[if(equals(parameters('mode'), 1), reference(resourceId('Microsoft.Storage/storageAccounts', 'test11'), '2019-06-01', 'full'), reference(resourceId('Microsoft.Storage/storageAccounts', 'test12'), '2019-06-01', 'full')).properties.allowBlobPublicAccess]",
                                                         "Resource access should be in-lined");
            }
        }
コード例 #11
0
ファイル: LinterRuleTestsBase.cs プロジェクト: rynowak/bicep
        private void DoWithDiagnosticAnnotations(string text, Func <IDiagnostic, bool> filterFunc, Action <IEnumerable <IDiagnostic> > assertAction)
        {
            var result = CompilationHelper.Compile(text);

            result.Should().NotHaveDiagnosticsWithCodes(new [] { LinterAnalyzer.FailedRuleCode }, "There should never be linter FailedRuleCode errors");

            DiagnosticAssertions.DoWithDiagnosticAnnotations(
                result.Compilation.SourceFileGrouping.EntryPoint,
                result.Diagnostics.Where(filterFunc),
                assertAction);
        }
コード例 #12
0
ファイル: TemplateEmitterTests.cs プロジェクト: stan-sz/bicep
        public void TemplateEmitter_should_not_dispose_text_writer()
        {
            var(_, _, compilation) = CompilationHelper.Compile(string.Empty);

            var stringBuilder = new StringBuilder();
            var stringWriter  = new StringWriter(stringBuilder);

            var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), BicepTestConstants.EmitterSettings);

            emitter.Emit(stringWriter);

            // second write should succeed if stringWriter wasn't closed
            emitter.Emit(stringWriter);
        }
コード例 #13
0
        public void If_ResLocationIs_StringLiteral_ShouldFail_WithFixes()
        {
            var result = CompilationHelper.Compile(@"
                resource storageaccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
                  name: 'name'
                  location: 'westus'
                  kind: 'StorageV2'
                  sku: {
                    name: 'Premium_LRS'
                  }
                }
            ");

            result.Diagnostics.Should().HaveDiagnostics(new[]
コード例 #14
0
        public void If_ResLocationIs_Global_CaseInsensitive_ShouldPass()
        {
            var result = CompilationHelper.Compile(@"
                resource appInsightsComponents 'Microsoft.Insights/components@2020-02-02-preview' = {
                  name: 'name'
                  location: 'GLOBAL'
                  kind: 'web'
                  properties: {
                    Application_Type: 'web'
                  }
                }"
                                                   );

            result.Diagnostics.Should().BeEmpty();
        }
コード例 #15
0
        public void If_ResLocationIs_VariableWithGlobal_ShouldPass()
        {
            var result = CompilationHelper.Compile(@"
                var location = 'Global'
                resource appInsightsComponents 'Microsoft.Insights/components@2020-02-02-preview' = {
                  name: 'name'
                  location: location
                  kind: 'web'
                  properties: {
                    Application_Type: 'web'
                  }
                }"
                                                   );

            result.Diagnostics.Should().BeEmpty();
        }
コード例 #16
0
        public void AssigningResourceToVariable_ShouldNotGenerateVariables_ChainedVariables()
        {
            var result = CompilationHelper.Compile(@"
resource storage 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: 'test'
  location: 'eastus'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

var ref4 = ref3
var ref3 = ref2
var ref2 = ref1
var ref1 = storage

resource storage2 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: 'test2'
  location: 'eastus'
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    allowBlobPublicAccess: ref4.properties.allowBlobPublicAccess
  }
}
");

            result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
            using (new AssertionScope())
            {
                result.Template.Should().NotHaveValueAtPath("$.variables", "variable should not be generated");
                result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'test2')].dependsOn",
                                                         new JArray
                {
                    "[resourceId('Microsoft.Storage/storageAccounts', 'test')]"
                },
                                                         "Referenced resource should be added to depends on section");
                result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'test2')].properties.allowBlobPublicAccess",
                                                         "[reference(resourceId('Microsoft.Storage/storageAccounts', 'test')).allowBlobPublicAccess]",
                                                         "Resource access should be in-lined");
            }
        }
コード例 #17
0
ファイル: TemplateEmitterTests.cs プロジェクト: v-tsun/bicep
        public void Multiline_strings_should_parse_correctly(string newlineSequence)
        {
            var inputFile = @"
var multiline = '''
this
  is
    a
      multiline
        string
'''
";

            var(template, _, _) = CompilationHelper.Compile(StringUtils.ReplaceNewlines(inputFile, newlineSequence));

            var expected = string.Join(newlineSequence, new [] { "this", "  is", "    a", "      multiline", "        string", "" });

            template !.SelectToken("$.variables.multiline") !.Should().DeepEqual(expected);
        }
コード例 #18
0
        private void CompileAndTest(string bicepText, string[] expectedFoundMessages)
        {
            using (var scope = new AssertionScope())
            {
                scope.AddReportable("bicepText", bicepText);

                var result        = CompilationHelper.Compile(bicepText);
                var semanticModel = result.Compilation.GetEntrypointSemanticModel();

                // Look for an output - that's what we'll use in the test
                var output = result.BicepFile.ProgramSyntax.Children.OfType <OutputDeclarationSyntax>()
                             .Should().HaveCount(1, "Each testcase should contain a single output with an expression to test")
                             .And.Subject.First();

                var secrets = FindPossibleSecretsVisitor.FindPossibleSecrets(semanticModel, output.Value);
                secrets.Select(s => s.FoundMessage).Should().BeEquivalentTo(expectedFoundMessages);
            }
        }
コード例 #19
0
        public void Readonly_properties_are_removed()
        {
            var bicepFile = @"
resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    readOnlyProp: 'abc'
    readWriteProp: 'def'
    writeOnlyProp: 'ghi'
  }
}

output myObj object = {
  readOnlyProp: resA.properties.readOnlyProp
  readWriteProp: resA.properties.readWriteProp
}
";

            var typeDefinition = TestTypeHelper.CreateCustomResourceType("My.Rp/resA", "2020-01-01", TypeSymbolValidationFlags.WarnOnTypeMismatch,
                                                                         new TypeProperty("readOnlyProp", LanguageConstants.String, TypePropertyFlags.ReadOnly),
                                                                         new TypeProperty("readWriteProp", LanguageConstants.String, TypePropertyFlags.None),
                                                                         new TypeProperty("writeOnlyProp", LanguageConstants.String, TypePropertyFlags.WriteOnly));
            var typeLoader = TestTypeHelper.CreateAzResourceTypeLoaderWithTypes(typeDefinition.AsEnumerable());

            var(_, _, compilation) = CompilationHelper.Compile(typeLoader, ("main.bicep", bicepFile));
            var rewriter = new ReadOnlyPropertyRemovalRewriter(compilation.GetEntrypointSemanticModel());

            var newProgramSyntax = rewriter.Rewrite(compilation.SourceFileGrouping.EntryPoint.ProgramSyntax);

            PrintHelper.PrintAndCheckForParseErrors(newProgramSyntax).Should().Be(
                @"resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    readWriteProp: 'def'
    writeOnlyProp: 'ghi'
  }
}

output myObj object = {
  readOnlyProp: resA.properties.readOnlyProp
  readWriteProp: resA.properties.readWriteProp
}");
        }
コード例 #20
0
        public void Only_list_methods_are_permitted()
        {
            var result = CompilationHelper.Compile(@"
resource stg 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
  name: 'testacc'
}

var allowed = {
  a: stg.list()
  b: stg.listA()
  c: stg.listTotallyMadeUpMethod()
}

var disallowed = {
  a: stg.lis()
  b: stg.lsit()
  c: stg.totallyMadeUpMethod()
}
");

            result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] {
コード例 #21
0
        public void TestRuleThrowingExceptionReturnsOneDiagnostic()
        {
            var text = @"
@secure()
param param1 string = 'val'";
            var compilationResult = CompilationHelper.Compile(text);
            var semanticModel     = compilationResult.Compilation.GetSemanticModel(compilationResult.BicepFile);

            var throwRule   = new LinterThrowsTestRule();
            var diagnostics = throwRule.Analyze(semanticModel);

            diagnostics.Should().NotBeNullOrEmpty();
            diagnostics.Should().HaveCount(1);

            var diag = diagnostics.First();

            diag.Should().NotBeNull();

            diag.Code.Should().NotBeNull();
            diag.Code.Should().Match("linter-internal-error");

            diag.Span.Should().NotBeNull();
            diag.Span.Position.Should().Be(0);
        }
コード例 #22
0
        public void Nested_casing_issues_take_multiple_passes_to_correct()
        {
            var bicepFile = @"
resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    lowerCaseObj: {
      lowerCaseStr: 'test'
    }
  }
}

output myObj object = {
  lowerCaseProp: resA.properties.lowerCaseObj.lowerCaseStr
}
";

            var typeDefinition = ResourceTypeProviderHelper.CreateCustomResourceType("My.Rp/resA", "2020-01-01", TypeSymbolValidationFlags.WarnOnTypeMismatch,
                                                                                     new TypeProperty("lowercaseobj", new NamedObjectType("lowercaseobj", TypeSymbolValidationFlags.Default, new [] {
                new TypeProperty("lowercasestr", LanguageConstants.String)
            }, null)));
            var typeProvider = ResourceTypeProviderHelper.CreateMockTypeProvider(typeDefinition.AsEnumerable());

            var(_, _, compilation) = CompilationHelper.Compile(typeProvider, ("main.bicep", bicepFile));
            var rewriter = new TypeCasingFixerRewriter(compilation.GetEntrypointSemanticModel());

            var newProgramSyntax   = rewriter.Rewrite(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax);
            var firstPassBicepFile = PrintHelper.PrettyPrint(newProgramSyntax);

            firstPassBicepFile.Should().Be(
                @"resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    lowercaseobj: {
      lowerCaseStr: 'test'
    }
  }
}

output myObj object = {
  lowerCaseProp: resA.properties.lowercaseobj.lowerCaseStr
}");

            (_, _, compilation) = CompilationHelper.Compile(typeProvider, ("main.bicep", firstPassBicepFile));
            rewriter            = new TypeCasingFixerRewriter(compilation.GetEntrypointSemanticModel());

            newProgramSyntax = rewriter.Rewrite(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax);
            PrintHelper.PrettyPrint(newProgramSyntax).Should().Be(
                @"resource resA 'My.Rp/resA@2020-01-01' = {
  name: 'resA'
  properties: {
    lowercaseobj: {
      lowercasestr: 'test'
    }
  }
}

output myObj object = {
  lowerCaseProp: resA.properties.lowercaseobj.lowercasestr
}");
        }