Exemplo n.º 1
0
        public void Type_validation_runs_on_compilation_successfully(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
        {
            var customTypes = new[] {
                TestTypeHelper.CreateCustomResourceType("My.Rp/myType", "2020-01-01", validationFlags),
            };
            var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: { }
}
";

            var model = GetSemanticModelForTest(program, customTypes);

            model.GetAllDiagnostics().Should().BeEmpty();
        }
        public void VerifyParameterTypeCompletionWithPrecedingComment()
        {
            var grouping    = SyntaxTreeGroupingFactory.CreateFromText("/*test*/param foo ");
            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping);
            var provider    = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server));

            var offset = grouping.EntryPoint.ProgramSyntax.Declarations.OfType <ParameterDeclarationSyntax>().Single().Type.Span.Position;

            var completions = provider.GetFilteredCompletions(compilation, BicepCompletionContext.Create(compilation, offset));
            var declarationTypeCompletions = completions.Where(c => c.Kind == CompletionItemKind.Class).ToList();

            AssertExpectedDeclarationTypeCompletions(declarationTypeCompletions);

            completions.Where(c => c.Kind == CompletionItemKind.Snippet).Should().SatisfyRespectively(
                c =>
            {
                c.Label.Should().Be("secureObject");
                c.Kind.Should().Be(CompletionItemKind.Snippet);
                c.InsertTextFormat.Should().Be(InsertTextFormat.Snippet);
                c.TextEdit !.TextEdit !.NewText.Should().Be("object");
                c.TextEdit.TextEdit.Range.Start.Line.Should().Be(0);
                c.TextEdit.TextEdit.Range.Start.Character.Should().Be(18);
                c.Detail.Should().Be("Secure object");
                c.AdditionalTextEdits !.Count().Should().Be(1);
                c.AdditionalTextEdits !.ElementAt(0).NewText.Should().Be("@secure()\n");
                c.AdditionalTextEdits !.ElementAt(0).Range.Start.Line.Should().Be(0);
                c.AdditionalTextEdits !.ElementAt(0).Range.Start.Character.Should().Be(8);
                c.AdditionalTextEdits !.ElementAt(0).Range.End.Line.Should().Be(0);
                c.AdditionalTextEdits !.ElementAt(0).Range.End.Character.Should().Be(8);
            },
                c =>
            {
                c.Label.Should().Be("secureString");
                c.Kind.Should().Be(CompletionItemKind.Snippet);
                c.InsertTextFormat.Should().Be(InsertTextFormat.Snippet);
                c.TextEdit !.TextEdit !.NewText.Should().Be("string");
                c.TextEdit.TextEdit.Range.Start.Line.Should().Be(0);
                c.TextEdit.TextEdit.Range.Start.Character.Should().Be(18);
                c.Detail.Should().Be("Secure string");
                c.AdditionalTextEdits !.Count().Should().Be(1);
                c.AdditionalTextEdits !.ElementAt(0).NewText.Should().Be("@secure()\n");
                c.AdditionalTextEdits !.ElementAt(0).Range.Start.Line.Should().Be(0);
                c.AdditionalTextEdits !.ElementAt(0).Range.Start.Character.Should().Be(8);
                c.AdditionalTextEdits !.ElementAt(0).Range.End.Line.Should().Be(0);
                c.AdditionalTextEdits !.ElementAt(0).Range.End.Character.Should().Be(8);
            });
        }
        public void Create_ShouldReturnValidCompilation()
        {
            var provider = new BicepCompilationProvider(TestTypeHelper.CreateEmptyProvider(), CreateEmptyFileResolver());

            var fileUri    = DocumentUri.Parse($"/{DataSets.Parameters_LF.Name}.bicep");
            var syntaxTree = SyntaxTree.Create(fileUri.ToUri(), DataSets.Parameters_LF.Bicep);
            var workspace  = new Workspace();

            workspace.UpsertSyntaxTrees(syntaxTree.AsEnumerable());
            var context = provider.Create(workspace, fileUri);

            context.Compilation.Should().NotBeNull();
            // TOOD: remove Where when the support of modifiers is dropped.
            context.Compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Where(d => d.Code != "BCP161").Should().BeEmpty();
            context.LineStarts.Should().NotBeEmpty();
            context.LineStarts[0].Should().Be(0);
        }
        public void NonDeclarationContextShouldIncludeDeclaredSymbols()
        {
            var grouping    = SyntaxTreeGroupingFactory.CreateFromText(@"
param p string
var v = 
resource r 'Microsoft.Foo/foos@2020-09-01' = {
  name: 'foo'
}
output o int = 42
");
            var offset      = grouping.EntryPoint.ProgramSyntax.Declarations.OfType <VariableDeclarationSyntax>().Single().Value.Span.Position;
            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), grouping);

            var provider    = new BicepCompletionProvider(new FileResolver(), new SnippetsProvider(), new TelemetryProvider(Server));
            var context     = BicepCompletionContext.Create(compilation, offset);
            var completions = provider.GetFilteredCompletions(compilation, context).ToList();

            AssertExpectedFunctions(completions, expectParamDefaultFunctions: false);

            // outputs can't be referenced so they should not show up in completions
            completions.Where(c => c.Kind == SymbolKind.Output.ToCompletionItemKind()).Should().BeEmpty();

            // the variable won't appear in completions because we are not suggesting cycles
            completions.Where(c => c.Kind == SymbolKind.Variable.ToCompletionItemKind()).Should().BeEmpty();

            const string expectedResource   = "r";
            var          resourceCompletion = completions.Single(c => c.Kind == SymbolKind.Resource.ToCompletionItemKind());

            resourceCompletion.Label.Should().Be(expectedResource);
            resourceCompletion.Kind.Should().Be(CompletionItemKind.Interface);
            resourceCompletion.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
            resourceCompletion.TextEdit !.TextEdit !.NewText.Should().Be(expectedResource);
            resourceCompletion.CommitCharacters.Should().BeEquivalentTo(new[] { ":", });
            resourceCompletion.Detail.Should().Be(expectedResource);

            const string expectedParam   = "p";
            var          paramCompletion = completions.Single(c => c.Kind == SymbolKind.Parameter.ToCompletionItemKind());

            paramCompletion.Label.Should().Be(expectedParam);
            paramCompletion.Kind.Should().Be(CompletionItemKind.Field);
            paramCompletion.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
            paramCompletion.TextEdit !.TextEdit !.NewText.Should().Be(expectedParam);
            paramCompletion.CommitCharacters.Should().BeNull();
            paramCompletion.Detail.Should().Be(expectedParam);
        }
Exemplo n.º 5
0
        public void Errors_are_raised_for_existing_resources_at_invalid_scopes()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader    = TestTypeHelper.CreateAzResourceTypeLoaderWithTypes(new [] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new TypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                }, null))
            });

            // explicitly pass an invalid scope
            var(_, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: subscription()
}
"));

            diags.Should().HaveDiagnostics(new[] {
Exemplo n.º 6
0
        private static IEnumerable <string> GetAllDiagnostics(string bicepFilePath)
        {
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
            var compilation        = new Compilation(TestTypeHelper.CreateEmptyProvider(), syntaxTreeGrouping);

            var output = new List <string>();

            foreach (var(syntaxTree, diagnostics) in compilation.GetAllDiagnosticsBySyntaxTree())
            {
                foreach (var diagnostic in diagnostics)
                {
                    var(line, character) = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, diagnostic.Span.Position);
                    output.Add($"{syntaxTree.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}");
                }
            }

            return(output);
        }
        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
}");
        }
Exemplo n.º 8
0
        public void NestedResources_symbols_are_bound()
        {
            var program = @"
resource parent 'My.RP/parentType@2020-01-01' = {
  name: 'parent'
  properties: {
    size: 'large'
  }

  resource child 'childType' = {
    name: 'child'
    properties: {
      style: 'very cool'
    }
  }

  resource sibling 'childType@2020-01-02' = {
    name: 'sibling'
    properties: {
      style: child.properties.style
      size: parent.properties.size
    }
  }
}
";

            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SyntaxTreeGroupingFactory.CreateFromText(program));
            var model       = compilation.GetEntrypointSemanticModel();

            model.GetAllDiagnostics().Should().BeEmpty();

            var expected = new []
            {
                new { name = "child", type = "My.RP/parentType/childType@2020-01-01", },
                new { name = "parent", type = "My.RP/parentType@2020-01-01", },
                new { name = "sibling", type = "My.RP/parentType/childType@2020-01-02", },
            };

            model.Root.GetAllResourceDeclarations()
            .Select(s => new { name = s.Name, type = (s.Type as ResourceType)?.TypeReference.FormatName(), })
            .OrderBy(n => n.name)
            .Should().BeEquivalentTo(expected);
        }
Exemplo n.º 9
0
        public void NestedResources_symbols_are_bound()
        {
            var program = @"
resource parent 'My.RP/parentType@2020-01-01' = {
  name: 'parent'
  properties: {
    size: 'large'
  }

  resource child 'childType' = {
    name: 'child'
    properties: {
      style: 'very cool'
    }
  }

  resource sibling 'childType@2020-01-02' = {
    name: 'sibling'
    properties: {
      style: child.properties.style
      size: parent.properties.size
    }
  }
}
";

            var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration, BicepTestConstants.LinterAnalyzer);
            var model       = compilation.GetEntrypointSemanticModel();

            model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty();

            var expected = new[]
            {
                new { name = "child", type = "My.RP/parentType/childType@2020-01-01", },
                new { name = "parent", type = "My.RP/parentType@2020-01-01", },
                new { name = "sibling", type = "My.RP/parentType/childType@2020-01-02", },
            };

            model.DeclaredResources.Select(x => x.Symbol)
            .Select(s => new { name = s.Name, type = (s.Type as ResourceType)?.TypeReference.FormatName(), })
            .OrderBy(n => n.name)
            .Should().BeEquivalentTo(expected);
        }
Exemplo n.º 10
0
        public void Type_validation_narrowing_on_union_types(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
        {
            var customTypes = new[] {
                TestTypeHelper.CreateCustomResourceType("My.Rp/myType", "2020-01-01", validationFlags,
                                                        new TypeProperty("stringOrInt", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("unspecifiedStringOrInt", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("abcOrDef", TypeHelper.CreateTypeUnion(new StringLiteralType("abc"), new StringLiteralType("def")), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("unspecifiedAbcOrDef", TypeHelper.CreateTypeUnion(new StringLiteralType("abc"), new StringLiteralType("def")), TypePropertyFlags.AllowImplicitNull)),
                TestTypeHelper.CreateCustomResourceType("My.Rp/myDependentType", "2020-01-01", validationFlags,
                                                        new TypeProperty("stringOnly", LanguageConstants.String, TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("abcOnly", new StringLiteralType("abc"), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("abcOnlyUnNarrowed", new StringLiteralType("abc"), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("stringOrIntUnNarrowed", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int), TypePropertyFlags.AllowImplicitNull),
                                                        new TypeProperty("abcOrDefUnNarrowed", TypeHelper.CreateTypeUnion(new StringLiteralType("abc"), new StringLiteralType("def"), new StringLiteralType("ghi")), TypePropertyFlags.AllowImplicitNull)),
            };
            var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    stringOrInt: 'abc'
    abcOrDef: 'abc'
  }
}

resource myDependentRes 'My.Rp/myDependentType@2020-01-01' = {
  name: 'steve'
  properties: {
    stringOnly: myRes.properties.stringOrInt // should be allowed
    abcOnly: myRes.properties.abcOrDef
    abcOnlyUnNarrowed: myRes.properties.unspecifiedAbcOrDef
    stringOrIntUnNarrowed: myRes.properties.unspecifiedStringOrInt
    abcOrDefUnNarrowed: myRes.properties.abcOrDef
  }
}
";

            var model = GetSemanticModelForTest(program, customTypes);

            model.GetAllDiagnostics().Should().SatisfyRespectively(
                x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"abcOnlyUnNarrowed\" expected a value of type \"'abc' | null\" but the provided value is of type \"'abc' | 'def'\". If this is an inaccuracy in the documentation, please report it to the Bicep Team.")
                );
        }
Exemplo n.º 11
0
        public void ShouldConvertExpressionsCorrectly(string text, string expected)
        {
            var programText = $"var test = {text}";
            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(programText, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration);

            var programSyntax             = compilation.SourceFileGrouping.EntryPoint.ProgramSyntax;
            var variableDeclarationSyntax = programSyntax.Children.OfType <VariableDeclarationSyntax>().First();

            var converter = new ExpressionConverter(new EmitterContext(compilation.GetEntrypointSemanticModel(), BicepTestConstants.EmitterSettings));
            var converted = converter.ConvertExpression(variableDeclarationSyntax.Value);

            var serializer = new ExpressionSerializer(new ExpressionSerializerSettings
            {
                IncludeOuterSquareBrackets = true, SingleStringHandling = ExpressionSerializerSingleStringHandling.SerializeAsString
            });

            var actual = serializer.SerializeExpression(converted);

            actual.Should().Be(expected);
        }
Exemplo n.º 12
0
        public void Create_ShouldReturnValidCompilation()
        {
            var fileUri      = DocumentUri.Parse($"/{DataSets.Parameters_LF.Name}.bicep");
            var fileResolver = CreateFileResolver(fileUri.ToUri(), DataSets.Parameters_LF.Bicep);
            var dispatcher   = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver));

            var provider = new BicepCompilationProvider(TestTypeHelper.CreateEmptyProvider(), fileResolver, dispatcher);

            var sourceFile = SourceFileFactory.CreateSourceFile(fileUri.ToUri(), DataSets.Parameters_LF.Bicep);
            var workspace  = new Workspace();

            workspace.UpsertSourceFile(sourceFile);
            var context = provider.Create(workspace, fileUri, ImmutableDictionary <ISourceFile, ISemanticModel> .Empty);

            context.Compilation.Should().NotBeNull();
            // TODO: remove Where when the support of modifiers is dropped.
            context.Compilation.GetEntrypointSemanticModel().GetAllDiagnostics(new ConfigHelper().GetDisabledLinterConfig()).Should().BeEmpty();
            context.LineStarts.Should().NotBeEmpty();
            context.LineStarts[0].Should().Be(0);
        }
Exemplo n.º 13
0
        protected static IEnumerable <string> GetAllDiagnostics(string bicepFilePath)
        {
            var dispatcher         = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver));
            var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
            var compilation        = new Compilation(TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping);

            var output = new List <string>();

            foreach (var(bicepFile, diagnostics) in compilation.GetAllDiagnosticsByBicepFile())
            {
                foreach (var diagnostic in diagnostics)
                {
                    var(line, character) = TextCoordinateConverter.GetPosition(bicepFile.LineStarts, diagnostic.Span.Position);
                    var codeDescription = diagnostic.Uri == null ? string.Empty : $" [{diagnostic.Uri.AbsoluteUri}]";
                    output.Add($"{bicepFile.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}{codeDescription}");
                }
            }

            return(output);
        }
Exemplo n.º 14
0
        public void VerifyDisableNextLineDiagnosticDirectivesCache_WithMultipleDisableNextLineDiagnosticDirectivesInBicepFile()
        {
            string bicepFileContents = @"#disable-next-line no-unused-params
param storageAccount string = 'testStorageAccount'
var vmProperties = {
  diagnosticsProfile: {
    bootDiagnostics: {
      enabled: 123
      storageUri: true
      unknownProp: 'asdf'
    }
  }
  evictionPolicy: 'Deallocate'
}
resource vm 'Microsoft.Compute/virtualMachines@2020-12-01' = {
  name: 'vm'
  location: 'West US'
#disable-next-line BCP036 BCP037
  properties: vmProperties
}


";
            var    compilation       = new Compilation(TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(bicepFileContents, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration, BicepTestConstants.LinterAnalyzer);
            var    bicepFile         = compilation.GetEntrypointSemanticModel().SourceFile;

            var disabledDiagnosticsCache = bicepFile.DisabledDiagnosticsCache;

            var disableNextLineDirectiveEndPositionAndCodes = disabledDiagnosticsCache.TryGetDisabledNextLineDirective(0);

            disableNextLineDirectiveEndPositionAndCodes.Should().NotBeNull();
            disableNextLineDirectiveEndPositionAndCodes !.endPosition.Should().Be(35);
            disableNextLineDirectiveEndPositionAndCodes.diagnosticCodes.Should().Contain("no-unused-params");

            disableNextLineDirectiveEndPositionAndCodes = disabledDiagnosticsCache.TryGetDisabledNextLineDirective(15);

            disableNextLineDirectiveEndPositionAndCodes.Should().NotBeNull();
            disableNextLineDirectiveEndPositionAndCodes !.endPosition.Should().Be(396);
            disableNextLineDirectiveEndPositionAndCodes.diagnosticCodes.Should().Contain("BCP036");
            disableNextLineDirectiveEndPositionAndCodes.diagnosticCodes.Should().Contain("BCP037");
        }
Exemplo n.º 15
0
        public void Existing_resources_can_be_referenced_at_other_scopes()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader    = TestTypeHelper.CreateAzResourceTypeLoaderWithTypes(new [] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new TypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                    new TypeProperty("kind", LanguageConstants.String, TypePropertyFlags.ReadOnly, "kind property"),
                }, null))
            });

            // explicitly pass a valid scope
            var(template, _, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: resourceGroup()
}

output resourceARef string = resourceA.kind
"));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.outputs['resourceARef'].value", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01', 'full').kind]");
            }

            // use a valid targetScope without setting the scope property
            (template, _, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
targetScope = 'resourceGroup'

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

output resourceARef string = resourceA.kind
"));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.outputs['resourceARef'].value", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01', 'full').kind]");
            }
        }
Exemplo n.º 16
0
        public void Create_ShouldReturnValidCompilation()
        {
            var fileUri      = DocumentUri.Parse($"/{DataSets.Parameters_LF.Name}.bicep");
            var fileResolver = CreateFileResolver(fileUri.ToUri(), DataSets.Parameters_LF.Bicep);
            var dispatcher   = new ModuleDispatcher(new DefaultModuleRegistryProvider(fileResolver, BicepTestConstants.ClientFactory, BicepTestConstants.TemplateSpecRepositoryFactory, BicepTestConstants.Features));

            var provider = new BicepCompilationProvider(BicepTestConstants.Features, TestTypeHelper.CreateWithAzTypes(), fileResolver, dispatcher);

            var sourceFile = SourceFileFactory.CreateSourceFile(fileUri.ToUri(), DataSets.Parameters_LF.Bicep);
            var workspace  = new Workspace();

            workspace.UpsertSourceFile(sourceFile);
            var configuration = BicepTestConstants.BuiltInConfigurationWithAnalyzersDisabled;
            var context       = provider.Create(workspace, fileUri, ImmutableDictionary <ISourceFile, ISemanticModel> .Empty, configuration, new LinterAnalyzer(configuration));

            context.Compilation.Should().NotBeNull();
            // TODO: remove Where when the support of modifiers is dropped.
            context.Compilation.GetEntrypointSemanticModel().GetAllDiagnostics().Should().BeEmpty();
            context.LineStarts.Should().NotBeEmpty();
            context.LineStarts[0].Should().Be(0);
        }
Exemplo n.º 17
0
        public static async Task <ILanguageClient> StartServerWithClientConnectionAsync(TestContext testContext, Action <LanguageClientOptions> onClientOptions, IResourceTypeProvider?resourceTypeProvider = null, IFileResolver?fileResolver = null)
        {
            resourceTypeProvider ??= TestTypeHelper.CreateEmptyProvider();
            fileResolver ??= new InMemoryFileResolver(new Dictionary <Uri, string>());

            var clientPipe = new Pipe();
            var serverPipe = new Pipe();

            var server = new Server(
                serverPipe.Reader,
                clientPipe.Writer,
                new Server.CreationOptions
            {
                ResourceTypeProvider = resourceTypeProvider,
                FileResolver         = fileResolver,
                SnippetsProvider     = SnippetsProvider
            });
            var _ = server.RunAsync(CancellationToken.None); // do not wait on this async method, or you'll be waiting a long time!

            var client = LanguageClient.PreInit(options =>
            {
                options
                .WithInput(clientPipe.Reader)
                .WithOutput(serverPipe.Writer)
                .OnInitialize((client, request, cancellationToken) => { testContext.WriteLine("Language client initializing."); return(Task.CompletedTask); })
                .OnInitialized((client, request, response, cancellationToken) => { testContext.WriteLine("Language client initialized."); return(Task.CompletedTask); })
                .OnStarted((client, cancellationToken) => { testContext.WriteLine("Language client started."); return(Task.CompletedTask); })
                .OnLogTrace(@params => testContext.WriteLine($"TRACE: {@params.Message} VERBOSE: {@params.Verbose}"))
                .OnLogMessage(@params => testContext.WriteLine($"{@params.Type}: {@params.Message}"));

                onClientOptions(options);
            });
            await client.Initialize(CancellationToken.None);

            testContext.WriteLine("LanguageClient initialize finished.");

            return(client);
        }
Exemplo n.º 18
0
        public void NestedResources_resource_can_contain_property_called_resource()
        {
            var program = @"
resource parent 'My.RP/parentType@2020-01-01' = {
  name: 'parent'
  properties: {
    size: 'large'
  }
  resource: 'yes please'

  resource child 'childType' = {
    name: 'child'
    properties: {
      style: 'very cool'
    }
  }
}
";

            var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration, BicepTestConstants.LinterAnalyzer);
            var model       = compilation.GetEntrypointSemanticModel();

            // The property "resource" is not allowed ...
            model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().HaveCount(1);
            model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Single().Should().HaveCodeAndSeverity("BCP037", DiagnosticLevel.Error);

            var expected = new[]
            {
                new { name = "child", type = "My.RP/parentType/childType@2020-01-01", },
                new { name = "parent", type = "My.RP/parentType@2020-01-01", },
            };

            model.DeclaredResources.Select(x => x.Symbol)
            .Select(s => new { name = s.Name, type = (s.Type as ResourceType)?.TypeReference.FormatName(), })
            .OrderBy(n => n.name)
            .Should().BeEquivalentTo(expected);
        }
Exemplo n.º 19
0
        public void VerifySnippetTemplatesAreErrorFree(CompletionData completionData)
        {
            string pathPrefix = $"Completions/SnippetTemplates/{completionData.Prefix}";

            var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix(TestContext, typeof(SnippetTemplatesTests).Assembly, pathPrefix);

            var mainUri       = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, "main.bicep"));
            var bicepContents = completionData.SnippetText;
            var files         = new Dictionary <Uri, string>
            {
                [mainUri] = bicepContents,
            };

            // overrides for certain snippets which have contextual dependencies (e.g. external files)
            switch (completionData.Prefix)
            {
            case "module":
                var paramUri = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, "param.bicep"));
                files.Add(paramUri, "param myParam string = 'test'");
                break;

            case "res-logic-app-from-file":
                var requiredFile = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, "REQUIRED"));
                files.Add(requiredFile, @"{""definition"":{""$schema"":""https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#"",""contentVersion"":""1.0.0.0"",""outputs"":{}}}");
                return;
            }

            var compilation   = new Compilation(TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateForFiles(files, mainUri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInConfiguration);
            var semanticModel = compilation.GetEntrypointSemanticModel();

            if (semanticModel.HasErrors())
            {
                var errors = semanticModel.GetAllDiagnostics().Where(x => x.Level == DiagnosticLevel.Error);
                var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(bicepContents, "\n", errors, diag => OutputHelper.GetDiagLoggingString(bicepContents, outputDirectory, diag));
                Assert.Fail("Template with prefix {0} contains errors. Please fix following errors:\n {1}", completionData.Prefix, sourceTextWithDiags);
            }
        }
Exemplo n.º 20
0
        public void NestedResources_resource_can_contain_property_called_resource()
        {
            var program = @"
resource parent 'My.RP/parentType@2020-01-01' = {
  name: 'parent'
  properties: {
    size: 'large'
  }
  resource: 'yes please'

  resource child 'childType' = {
    name: 'child'
    properties: {
      style: 'very cool'
    }
  }
}
";

            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SyntaxTreeGroupingFactory.CreateFromText(program));
            var model       = compilation.GetEntrypointSemanticModel();

            // The property "resource" is not allowed ...
            model.GetAllDiagnostics().Should().HaveCount(1);
            model.GetAllDiagnostics().Single().Should().HaveCodeAndSeverity("BCP038", DiagnosticLevel.Error);

            var expected = new []
            {
                new { name = "child", type = "My.RP/parentType/childType@2020-01-01", },
                new { name = "parent", type = "My.RP/parentType@2020-01-01", },
            };

            model.Root.GetAllResourceDeclarations()
            .Select(s => new { name = s.Name, type = (s.Type as ResourceType)?.TypeReference.FormatName(), })
            .OrderBy(n => n.name)
            .Should().BeEquivalentTo(expected);
        }
Exemplo n.º 21
0
        public void Decompiler_handles_strings_with_newlines(string newline, string escapedNewline)
        {
            var template = @"{
    ""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"",
    ""contentVersion"": ""1.0.0.0"",
    ""parameters"": {},
    ""variables"": {
        ""multilineString"": ""multi
        line
        string""
    },
    ""resources"": [],
    ""outputs"": {}
}";

            // replace newlines with the style passed in
            template = string.Join(newline, Regex.Split(template, "\r?\n"));

            var fileUri      = new Uri("file:///path/to/main.json");
            var fileResolver = new InMemoryFileResolver(new Dictionary <Uri, string>
            {
                [fileUri] = template,
            });;

            var(entryPointUri, filesToSave) = TemplateDecompiler.DecompileFileWithModules(TestTypeHelper.CreateEmptyProvider(), fileResolver, fileUri);

            // this behavior is actually controlled by newtonsoft's deserializer, but we should assert it anyway to avoid regressions.
            filesToSave[entryPointUri].Should().Contain($"var multilineString = 'multi{escapedNewline}        line{escapedNewline}        string'");
        }
Exemplo n.º 22
0
 private static Program CreateProgram(TextWriter outputWriter, TextWriter errorWriter)
 {
     return(new Program(TestTypeHelper.CreateEmptyProvider(), outputWriter, errorWriter, BicepTestConstants.DevAssemblyFileVersion));
 }
Exemplo n.º 23
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 = TestTypeHelper.CreateCustomResourceType("My.Rp/resA", "2020-01-01", TypeSymbolValidationFlags.WarnOnTypeMismatch,
                                                                         new TypeProperty("lowercaseobj", new ObjectType("lowercaseobj", TypeSymbolValidationFlags.Default, new [] {
                new TypeProperty("lowercasestr", LanguageConstants.String)
            }, null)));
            var typeProvider = TestTypeHelper.CreateProviderWithTypes(typeDefinition.AsEnumerable());

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

            var newProgramSyntax   = rewriter.Rewrite(compilation.SourceFileGrouping.EntryPoint.ProgramSyntax);
            var firstPassBicepFile = PrintHelper.PrintAndCheckForParseErrors(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.SourceFileGrouping.EntryPoint.ProgramSyntax);
            PrintHelper.PrintAndCheckForParseErrors(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
}");
        }
Exemplo n.º 24
0
        public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow()
        {
            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SyntaxTreeGroupingFactory.CreateFromText("parameter "));

            compilation.GetEntrypointSemanticModel().GetParseDiagnostics();
        }
Exemplo n.º 25
0
        public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow()
        {
            var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("parameter ", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration);

            compilation.GetEntrypointSemanticModel().GetParseDiagnostics();
        }
Exemplo n.º 26
0
        public void NestedResources_valid_resource_references()
        {
            var program = @"
resource parent 'My.RP/parentType@2020-01-01' = {
  name: 'parent'
  properties: {
    size: 'large'
  }

  resource child 'childType' = {
    name: 'child'
    properties: {
      style: 'very cool'
    }

    resource grandchild 'grandchildType' = {
      name: 'grandchild'
      properties: {
        temperature: 'ice-cold'
      }
    }
  }

  resource sibling 'childType@2020-01-02' = {
    name: 'sibling'
    properties: {
      style: parent::child.properties.style
      size: parent.properties.size
      temperatureC: child::grandchild.properties.temperature
      temperatureF: parent::child::grandchild.properties.temperature
    }
  }
}

output fromChild string = parent::child.properties.style
output fromGrandchild string = parent::child::grandchild.properties.style
";

            var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText(program, BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration, BicepTestConstants.LinterAnalyzer);
            var model       = compilation.GetEntrypointSemanticModel();

            model.GetAllDiagnostics().ExcludingLinterDiagnostics().ExcludingMissingTypes().Should().BeEmpty();

            var parent     = model.DeclaredResources.Select(x => x.Symbol).Single(r => r.Name == "parent");
            var references = model.FindReferences(parent);

            references.Should().HaveCount(6);

            var child = model.DeclaredResources.Select(x => x.Symbol).Single(r => r.Name == "child");

            references = model.FindReferences(child);
            references.Should().HaveCount(6);

            var grandchild = model.DeclaredResources.Select(x => x.Symbol).Single(r => r.Name == "grandchild");

            references = model.FindReferences(grandchild);
            references.Should().HaveCount(4);

            var sibling = model.DeclaredResources.Select(x => x.Symbol).Single(r => r.Name == "sibling");

            references = model.FindReferences(sibling);
            references.Should().HaveCount(1);

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

            using var outputStream = new MemoryStream();
            emitter.Emit(outputStream);

            outputStream.Seek(0L, SeekOrigin.Begin);
            var text = Encoding.UTF8.GetString(outputStream.GetBuffer());
        }
Exemplo n.º 27
0
        public void Decompiler_handles_banned_function_replacement(string expression, string type, string expectedValue)
        {
            var template = @"{
    ""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"",
    ""contentVersion"": ""1.0.0.0"",
    ""parameters"": {},
    ""variables"": {
        ""a"": true,
        ""b"": false,
        ""c"": true
    },
    ""resources"": [],
    ""outputs"": {
        ""calculated"": {
            ""type"": """ + type + @""",
            ""value"": ""[" + expression + @"]""
        }
    }
}";

            var fileUri      = new Uri("file:///path/to/main.json");
            var fileResolver = new InMemoryFileResolver(new Dictionary <Uri, string>
            {
                [fileUri] = template,
            });

            var(entryPointUri, filesToSave) = TemplateDecompiler.DecompileFileWithModules(TestTypeHelper.CreateEmptyProvider(), fileResolver, fileUri);

            filesToSave[entryPointUri].Should().Contain($"output calculated {type} = ({expectedValue})");
        }
Exemplo n.º 28
0
        public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
        {
            var customTypes = new[] {
                TestTypeHelper.CreateCustomResourceType("My.Rp/myType", "2020-01-01", validationFlags,
                                                        new TypeProperty("myDisc1", new DiscriminatedObjectType("myDisc1", validationFlags, "discKey", new [] {
                    new ObjectType("choiceA", validationFlags, new [] {
                        new TypeProperty("discKey", new StringLiteralType("choiceA"), TypePropertyFlags.Required),
                        new TypeProperty("valueA", LanguageConstants.String, TypePropertyFlags.Required),
                    }, null),
                    new ObjectType("choiceB", validationFlags, new [] {
                        new TypeProperty("discKey", new StringLiteralType("choiceB"), TypePropertyFlags.Required),
                        new TypeProperty("valueB", LanguageConstants.String, TypePropertyFlags.Required),
                    }, null),
                }
                                                                                                                ))),
            };

            {
                // missing discriminator key
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      valueA: 'abc'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP078", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" requires a value of type \"'choiceA' | 'choiceB'\", but none was supplied.")
                    );
            }

            {
                // incorrect discriminator key case
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      DiscKey: 'choiceA'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP078", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" requires a value of type \"'choiceA' | 'choiceB'\", but none was supplied."),
                    x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"DiscKey\" is not allowed on objects of type \"'choiceA' | 'choiceB'\". Did you mean \"discKey\"?")
                    );
            }

            {
                // incorrect discriminator key
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'foo'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'foo'\". If this is an inaccuracy in the documentation, please report it to the Bicep Team.")
                    );
            }

            {
                // incorrect discriminator key with suggestion
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'choiceC'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP088", DiagnosticLevel.Warning).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'choiceC'\". Did you mean \"'choiceA'\"?")
                    );
            }

            {
                // discriminator key supplied, required value omitted
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'choiceA'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"valueA\". If this is an inaccuracy in the documentation, please report it to the Bicep Team.")
                    );
            }

            {
                // discriminator key supplied, case of required property is incorrect
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'choiceA'
      ValueA: 'hello'
    }
  }
}
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"valueA\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                    x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"ValueA\" is not allowed on objects of type \"choiceA\". Did you mean \"valueA\"?")
                    );
            }

            {
                // all good, incorrect property access
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'choiceA'
      valueA: 'hello'
    }
  }
}

output valueA string = myRes.properties.myDisc1.valueA
output valueB string = myRes.properties.myDisc1.valuuuueB
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"choiceA\" does not contain property \"valuuuueB\". Available properties include \"discKey\", \"valueA\".")
                    );
            }

            {
                // all good, incorrect property access with suggestion
                var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  properties: {
    myDisc1: {
      discKey: 'choiceA'
      valueA: 'hello'
    }
  }
}

output valueA string = myRes.properties.myDisc1.valueA
output valueB string = myRes.properties.myDisc1.valueB
";

                var model = GetSemanticModelForTest(program, customTypes);
                model.GetAllDiagnostics().Should().SatisfyRespectively(
                    x => x.Should().HaveCodeAndSeverity("BCP083", expectedDiagnosticLevel).And.HaveMessage("The type \"choiceA\" does not contain property \"valueB\". Did you mean \"valueA\"?")
                    );
            }
        }
Exemplo n.º 29
0
        public void Type_validation_runs_on_compilation_common_failures(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
        {
            var customTypes = new[] {
                TestTypeHelper.CreateCustomResourceTypeWithTopLevelProperties("My.Rp/myType", "2020-01-01", validationFlags,
                                                                              new [] { new TypeProperty("readOnlyTopLevelProp", LanguageConstants.String, TypePropertyFlags.ReadOnly) },
                                                                              new TypeProperty("readOnlyProp", LanguageConstants.String, TypePropertyFlags.ReadOnly),
                                                                              new TypeProperty("writeOnlyProp", LanguageConstants.String, TypePropertyFlags.WriteOnly | TypePropertyFlags.AllowImplicitNull),
                                                                              new TypeProperty("requiredProp", LanguageConstants.String, TypePropertyFlags.Required),
                                                                              new TypeProperty("additionalProps", new ObjectType(
                                                                                                   "additionalProps",
                                                                                                   validationFlags,
                                                                                                   new [] {
                    new TypeProperty("propA", LanguageConstants.String, TypePropertyFlags.Required),
                    new TypeProperty("propB", LanguageConstants.String, TypePropertyFlags.AllowImplicitNull),
                },
                                                                                                   LanguageConstants.Int
                                                                                                   )),
                                                                              new TypeProperty("nestedObj", new ObjectType(
                                                                                                   "nestedObj",
                                                                                                   validationFlags,
                                                                                                   new [] {
                    new TypeProperty("readOnlyNestedProp", LanguageConstants.String, TypePropertyFlags.ReadOnly),
                    new TypeProperty("writeOnlyNestedProp", LanguageConstants.String, TypePropertyFlags.WriteOnly | TypePropertyFlags.AllowImplicitNull),
                    new TypeProperty("requiredNestedProp", LanguageConstants.String, TypePropertyFlags.Required),
                },
                                                                                                   null
                                                                                                   ))),
            };
            var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
  name: 'steve'
  id: '/subscriptions/guid/resourceGroups/rg/My.Rp/myType/steve'
  type: 'My.Rp/myType'
  apiVersion: '2020-01-01'
  readOnlyTopLevelProp: 'abcd'
  properties: {
    readOnlyProp: 123
    writeOnlyProp: 456
    additionalProps: {
      propB: 123
    }
    nestedObj: {
      readOnlyNestedProp: 123
      writeOnlyNestedProp: 456
    }
  }
}

output writeOnlyOutput string = myRes.properties.writeOnlyProp
output writeOnlyOutput2 string = myRes.properties.nestedObj.writeOnlyProp
output missingOutput string = myRes.properties.missingOutput
output missingOutput2 string = myRes.properties.nestedObj.missingOutput
output incorrectTypeOutput int = myRes.properties.readOnlyProp
output incorrectTypeOutput2 int = myRes.properties.nestedObj.readOnlyProp
";

            var model = GetSemanticModelForTest(program, customTypes);

            model.GetAllDiagnostics().Should().SatisfyRespectively(
                x => x.Should().HaveCodeAndSeverity("BCP073", expectedDiagnosticLevel).And.HaveMessage("The property \"id\" is read-only. Expressions cannot be assigned to read-only properties."),
                x => x.Should().HaveCodeAndSeverity("BCP073", expectedDiagnosticLevel).And.HaveMessage("The property \"type\" is read-only. Expressions cannot be assigned to read-only properties."),
                x => x.Should().HaveCodeAndSeverity("BCP073", expectedDiagnosticLevel).And.HaveMessage("The property \"apiVersion\" is read-only. Expressions cannot be assigned to read-only properties."),
                x => x.Should().HaveCodeAndSeverity("BCP073", DiagnosticLevel.Warning).And.HaveMessage("The property \"readOnlyTopLevelProp\" is read-only. Expressions cannot be assigned to read-only properties. If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"requiredProp\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP073", DiagnosticLevel.Warning).And.HaveMessage("The property \"readOnlyProp\" is read-only. Expressions cannot be assigned to read-only properties. If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"writeOnlyProp\" expected a value of type \"null | string\" but the provided value is of type \"int\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"propA\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"propB\" expected a value of type \"null | string\" but the provided value is of type \"int\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"requiredNestedProp\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP073", DiagnosticLevel.Warning).And.HaveMessage("The property \"readOnlyNestedProp\" is read-only. Expressions cannot be assigned to read-only properties. If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"writeOnlyNestedProp\" expected a value of type \"null | string\" but the provided value is of type \"int\". If this is an inaccuracy in the documentation, please report it to the Bicep Team."),
                x => x.Should().HaveCodeAndSeverity("BCP077", expectedDiagnosticLevel).And.HaveMessage("The property \"writeOnlyProp\" on type \"properties\" is write-only. Write-only properties cannot be accessed."),
                x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"nestedObj\" does not contain property \"writeOnlyProp\". Available properties include \"readOnlyNestedProp\", \"requiredNestedProp\"."),
                x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"properties\" does not contain property \"missingOutput\". Available properties include \"additionalProps\", \"nestedObj\", \"readOnlyProp\", \"requiredProp\"."),
                x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"nestedObj\" does not contain property \"missingOutput\". Available properties include \"readOnlyNestedProp\", \"requiredNestedProp\"."),
                x => x.Should().HaveCodeAndSeverity("BCP026", DiagnosticLevel.Error).And.HaveMessage("The output expects a value of type \"int\" but the provided value is of type \"string\"."),
                x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"nestedObj\" does not contain property \"readOnlyProp\". Available properties include \"readOnlyNestedProp\", \"requiredNestedProp\".")
                );
        }
Exemplo n.º 30
0
 private static SemanticModel GetSemanticModelForTest(string programText, IEnumerable <ResourceTypeComponents> definedTypes)
 => GetSemanticModelForTest(programText, TestTypeHelper.CreateProviderWithTypes(definedTypes));