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); }
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[] {
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 }"); }
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); }
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); }
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.") ); }
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); }
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); }
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); }
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"); }
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]"); } }
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); }
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); }
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); }
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); } }
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); }
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'"); }
private static Program CreateProgram(TextWriter outputWriter, TextWriter errorWriter) { return(new Program(TestTypeHelper.CreateEmptyProvider(), outputWriter, errorWriter, BicepTestConstants.DevAssemblyFileVersion)); }
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 }"); }
public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow() { var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SyntaxTreeGroupingFactory.CreateFromText("parameter ")); compilation.GetEntrypointSemanticModel().GetParseDiagnostics(); }
public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow() { var compilation = new Compilation(TestTypeHelper.CreateEmptyProvider(), SourceFileGroupingFactory.CreateFromText("parameter ", BicepTestConstants.FileResolver), BicepTestConstants.BuiltInConfiguration); compilation.GetEntrypointSemanticModel().GetParseDiagnostics(); }
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()); }
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})"); }
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\"?") ); } }
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\".") ); }
private static SemanticModel GetSemanticModelForTest(string programText, IEnumerable <ResourceTypeComponents> definedTypes) => GetSemanticModelForTest(programText, TestTypeHelper.CreateProviderWithTypes(definedTypes));