public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics()
        {
            var documentUri   = DocumentUri.From("/template.bicep");
            var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();

            var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(options =>
            {
                options.OnPublishDiagnostics(diags => {
                    diagsReceived.SetResult(diags);
                });
            });

            // open document
            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @"
param myParam string = 2
resource myRes 'invalidFormat' = {

}
randomToken
", 1));

            var response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().SatisfyRespectively(
                d => {
                d.Range.Should().HaveRange((1, 23), (1, 24));
                d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((2, 15), (2, 30));
                d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((5, 0), (5, 11));
                d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error);
            }
                );

            // change document
            diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();
            client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @"
param myParam string = 'fixed!'
resource myRes 'invalidFormat' = {

}
randomToken
", 2));

            response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().SatisfyRespectively(
                d => {
                d.Range.Should().HaveRange((2, 15), (2, 30));
                d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((5, 0), (5, 11));
                d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error);
            }
                );

            // close document
            diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();
            client.TextDocument.DidCloseTextDocument(TextDocumentParamHelper.CreateDidCloseTextDocumentParams(documentUri, 3));

            response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().BeEmpty();
        }
示例#2
0
        public async Task RequestDeploymentGraphShouldReturnDeploymentGraph()
        {
            var diagnosticsListener = new MultipleMessageListener <PublishDiagnosticsParams>();
            var fileSystemDict      = new Dictionary <Uri, string>();

            using var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync(
                      this.TestContext,
                      options => options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)),
                      new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create(), FileResolver: new InMemoryFileResolver(fileSystemDict)));

            var client = helper.Client;

            var mainUri = DocumentUri.FromFileSystemPath("/main.bicep");

            fileSystemDict[mainUri.ToUri()] = @"
resource res1 'Test.Rp/basicTests@2020-01-01' = {
  name: 'res1'
}

resource res2 'Test.Rp/readWriteTests@2020-01-01' = {
  name: 'res2'
  properites: {
    readwrite: mod1.outputs.output1
  }
}

resource unknownRes = {
}

module mod1 './modules/module1.bicep' = {
  name: 'mod1'
}

module mod2 './modules/module2.bicep' = {
  name: 'mod2'
}

module nonExistingMod './path/to/nonExistingModule.bicep' = {
}
";

            var module1Uri = DocumentUri.FromFileSystemPath("/modules/module1.bicep");

            fileSystemDict[module1Uri.ToUri()] = @"
resource res3 'Test.Rp/basicTests@2020-01-01' = {
  name: 'res3'
}

output output1 int = 123
";

            var module2Uri = DocumentUri.FromFileSystemPath("/modules/module2.bicep");

            fileSystemDict[module2Uri.ToUri()] = @"
resource res4 'Test.Rp/basicTests@2020-01-01' = {
  name: 'res4'
}

module nestedMod './nestedModules/nestedModule.bicep' = [for x in []: {
  name: 'nestedMod'
  dependsOn: [
    res4
  ]
}]
";

            var nestedModuleUri = DocumentUri.FromFileSystemPath("/modules/nestedModules/nestedModule.bicep");

            fileSystemDict[nestedModuleUri.ToUri()] = @"
resource res5 'Test.Rp/basicTests@2020-01-01' = {
  name: 'res5'
}
";

            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1));
            await diagnosticsListener.WaitNext();

            var deploymentGraph = await client.SendRequest(new BicepDeploymentGraphParams(new TextDocumentIdentifier(mainUri)), default);

            deploymentGraph.Should().NotBeNull();
            deploymentGraph !.Nodes.Should().Equal(
                new BicepDeploymentGraphNode("mod1", "<module>", false, CreateTextRange(15, 0, 17, 1), true, false, Path.GetFullPath(mainUri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("mod1::res3", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(module1Uri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("mod2", "<module>", false, CreateTextRange(19, 0, 21, 1), true, false, Path.GetFullPath(mainUri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("mod2::nestedMod", "<module>", true, CreateTextRange(5, 0, 10, 2), true, false, Path.GetFullPath(module2Uri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("mod2::nestedMod::res5", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(nestedModuleUri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("mod2::res4", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(module2Uri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("nonExistingMod", "<module>", false, CreateTextRange(23, 0, 24, 1), false, true, Path.GetFullPath(mainUri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("res1", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(mainUri.GetFileSystemPath())),
                new BicepDeploymentGraphNode("res2", "Test.Rp/readWriteTests", false, CreateTextRange(5, 0, 10, 1), false, true, Path.GetFullPath(mainUri.GetFileSystemPath())));
            deploymentGraph !.Edges.Should().Equal(
                new BicepDeploymentGraphEdge("mod2::nestedMod", "mod2::res4"),
                new BicepDeploymentGraphEdge("res2", "mod1"));
            deploymentGraph !.ErrorCount.Should().Be(6);
        }
示例#3
0
        public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics()
        {
            var documentUri   = DocumentUri.From("/template.bicep");
            var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();

            var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(this.TestContext, options =>
            {
                options.OnPublishDiagnostics(diags => {
                    diagsReceived.SetResult(diags);
                });
            });

            // open document
            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @"
param myParam string = 2
resource myRes 'invalidFormat' = {

}
randomToken
", 1));

            var response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().SatisfyRespectively(
                d => {
                d.Range.Should().HaveRange((1, 6), (1, 13));
                // note documentation pretty printing moves Uri to code for output
                d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning);
            },
                d => {
                d.Range.Should().HaveRange((1, 23), (1, 24));
                d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((2, 15), (2, 30));
                d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((5, 0), (5, 11));
                d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error);
            }
                );

            // change document
            diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();
            client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @"
param myParam string = 'fixed!'
resource myRes 'invalidFormat' = {

}
randomToken
", 2));

            response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().SatisfyRespectively(
                d => {
                d.Range.Should().HaveRange((1, 6), (1, 13));
                // documentation provided with linter sets code to uri for pretty link print outs
                d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning);
            },
                d => {
                d.Range.Should().HaveRange((2, 15), (2, 30));
                d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error);
            },
                d => {
                d.Range.Should().HaveRange((5, 0), (5, 11));
                d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error);
            }
                );

            // close document
            diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();
            client.TextDocument.DidCloseTextDocument(TextDocumentParamHelper.CreateDidCloseTextDocumentParams(documentUri, 3));

            response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task);

            response.Diagnostics.Should().BeEmpty();
        }
示例#4
0
        public async Task BicepConfigFileDeletion_ShouldRefreshCompilation()
        {
            var(diagsListener, testOutputPath) = GetTestConfig();
            using var helper = await StartServerWithClientConnectionAsync(diagsListener);

            var client = helper.Client;

            var bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""info""
        }
      }
    }
  }
}";

            var bicepFileContents = @"param storageAccountName string = 'test'";

            var mainUri        = SaveFile("main.bicep", bicepFileContents, testOutputPath);
            var bicepConfigUri = SaveFile("bicepconfig.json", bicepConfigFileContents, testOutputPath);

            // open the main document and verify diagnostics
            {
                client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, bicepFileContents, 1));

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             mainUri,
                                             DiagnosticSeverity.Information,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }

            // Delete bicepconfig.json and verify diagnostics are based off of default bicepconfig.json
            {
                File.Delete(bicepConfigUri.GetFileSystemPath());

                client.Workspace.DidChangeWatchedFiles(new DidChangeWatchedFilesParams
                {
                    Changes = new Container <FileEvent>(new FileEvent
                    {
                        Type = FileChangeType.Deleted,
                        Uri  = bicepConfigUri,
                    })
                });

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             mainUri,
                                             DiagnosticSeverity.Warning,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }
        }
示例#5
0
        public async Task RequestDocumentSymbol_should_return_full_symbol_list()
        {
            var documentUri   = DocumentUri.From("/template.bicep");
            var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>();

            var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(options =>
            {
                options.OnPublishDiagnostics(diags => {
                    diagsReceived.SetResult(diags);
                });
            });

            // client opens the document
            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @"
param myParam string = 'test'
resource myRes 'myRp/provider@2019-01-01' = {
  name = 'test'
  }
  output myOutput string = 'myOutput'
", 0));

            // client requests symbols
            var symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams
            {
                TextDocument = new TextDocumentIdentifier
                {
                    Uri = documentUri,
                },
            });

            symbols.Should().SatisfyRespectively(
                x => {
                x.DocumentSymbol.Name.Should().Be("myParam");
                x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field);
                x.DocumentSymbol.Range.Should().HaveRange((1, 0), (1, 29));
            },
                x => {
                x.DocumentSymbol.Name.Should().Be("myRes");
                x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object);
                x.DocumentSymbol.Range.Should().HaveRange((2, 0), (4, 3));
            },
                x => {
                x.DocumentSymbol.Name.Should().Be("myOutput");
                x.DocumentSymbol.Kind.Should().Be(SymbolKind.Interface);
                x.DocumentSymbol.Range.Should().HaveRange((5, 2), (5, 37));
            }
                );

            // client deletes the output and renames the resource
            client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @"
param myParam string = 'test'
resource myRenamedRes 'myRp/provider@2019-01-01' = {
  name = 'test'
  }
", 1));

            // client requests symbols
            symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams
            {
                TextDocument = new TextDocumentIdentifier
                {
                    Uri = documentUri,
                },
            });

            symbols.Should().SatisfyRespectively(
                x => {
                x.DocumentSymbol.Name.Should().Be("myParam");
                x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field);
                x.DocumentSymbol.Range.Should().HaveRange((1, 0), (1, 29));
            },
                x => {
                x.DocumentSymbol.Name.Should().Be("myRenamedRes");
                x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object);
                x.DocumentSymbol.Range.Should().HaveRange((2, 0), (4, 3));
            }
                );
        }
示例#6
0
        public async Task WithMultipleConfigFiles_ShouldUseConfigSettingsFromRelevantDirectory()
        {
            var(diagsListener, parentDirectoryPath) = GetTestConfig();
            using var helper = await StartServerWithClientConnectionAsync(diagsListener);

            var client = helper.Client;

            var childDirectoryPath = Path.Combine(parentDirectoryPath, "child");

            string bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""info""
        }
      }
    }
  }
}";

            SaveFile("bicepconfig.json", bicepConfigFileContents, childDirectoryPath);

            string bicepFileContents = @"param storageAccountName string = 'test'";
            var    documentUriOfFileInChildDirectory = SaveFile("main.bicep", bicepFileContents, childDirectoryPath);
            var    uriOfFileInChildDirectory         = documentUriOfFileInChildDirectory.ToUri();

            // open the main document and verify diagnostics
            {
                client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(uriOfFileInChildDirectory, bicepFileContents, 1));

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             uriOfFileInChildDirectory,
                                             DiagnosticSeverity.Information,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }

            // add bicepconfig.json to parent directory and verify diagnostics
            {
                var documentUriOfFileInParentDirectory = SaveFile("main.bicep", bicepFileContents, parentDirectoryPath);
                var uriOfFileInParentDirectory         = documentUriOfFileInParentDirectory.ToUri();

                bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""warning""
        }
      }
    }
  }
}";
                var newBicepConfigUri = SaveFile("bicepconfig.json", bicepConfigFileContents, parentDirectoryPath);

                client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(uriOfFileInParentDirectory, bicepFileContents, 1));

                client.Workspace.DidChangeWatchedFiles(new DidChangeWatchedFilesParams
                {
                    Changes = new Container <FileEvent>(new FileEvent
                    {
                        Type = FileChangeType.Created,
                        Uri  = newBicepConfigUri,
                    })
                });

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             uriOfFileInParentDirectory,
                                             DiagnosticSeverity.Warning,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }
        }
示例#7
0
        public async Task FixingErrorsInInvalidBicepConfigFileAndSaving_ShouldRefreshCompilation()
        {
            var(diagsListener, testOutputPath) = GetTestConfig();
            using var helper = await StartServerWithClientConnectionAsync(diagsListener);

            var client = helper.Client;

            var bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
";
            var bicepFileContents       = @"param storageAccountName string = 'test'";

            var bicepConfigUri = SaveFile("bicepconfig.json", bicepConfigFileContents, testOutputPath);
            var mainUri        = SaveFile("main.bicep", bicepFileContents, testOutputPath);

            // open the main document and verify diagnostics
            {
                client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, bicepFileContents, 1));

                await VerifyDiagnosticsAsync(diagsListener,
                                             $"Failed to parse the contents of the Bicep configuration file \"{bicepConfigUri.GetFileSystemPath()}\" as valid JSON",
                                             mainUri,
                                             DiagnosticSeverity.Error,
                                             new Position(0, 0),
                                             new Position(1, 0),
                                             "Invalid Bicep Configuration");
            }

            // update bicepconfig.json and verify diagnostics
            {
                File.WriteAllText(bicepConfigUri.GetFileSystemPath(), @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""warning""
        }
      }
    }
  }
}");
                client.Workspace.DidChangeWatchedFiles(new DidChangeWatchedFilesParams
                {
                    Changes = new Container <FileEvent>(new FileEvent
                    {
                        Type = FileChangeType.Changed,
                        Uri  = bicepConfigUri,
                    })
                });

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             mainUri,
                                             DiagnosticSeverity.Warning,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }
        }
示例#8
0
        public async Task BicepFileOpen_ShouldFireTelemetryEvent()
        {
            var testOutputPath = Path.Combine(TestContext.ResultsDirectory, Guid.NewGuid().ToString());

            var bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""warning""
        },
        ""no-unused-vars"": {
          ""level"": ""info""
        },
        ""no-loc-expr-outside-params"": {
            ""level"": ""none""
        }
      }
    }
  }
}";

            FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigFileContents, testOutputPath);

            var fileSystemDict = new Dictionary <Uri, string>();

            var mainBicepFileContents = @"param appInsightsName string = 'testAppInsightsName'

var deployGroups = true

resource applicationInsights 'Microsoft.Insights/components@2015-05-01' = {
  name: appInsightsName
  location: resourceGroup().location
  kind: 'web'
  properties: {
    Application_Type: 'web'
  }
  resource favorites 'favorites@2015-05-01'{
    name: 'testName'
  }
}

module apimGroups 'groups.bicep' = if (deployGroups) {
  name: 'apimGroups'
}

param location string = 'testLocation'

#disable-next-line";
            var mainBicepFilePath     = FileHelper.SaveResultFile(TestContext, "main.bicep", mainBicepFileContents, testOutputPath);
            var mainUri = DocumentUri.FromFileSystemPath(mainBicepFilePath);

            fileSystemDict[mainUri.ToUri()] = mainBicepFileContents;

            var referencedBicepFileContents = @"resource parentAPIM 'Microsoft.ApiManagement/service@2019-01-01' existing = {
  name: 'testApimInstanceName'
}

resource apimGroup 'Microsoft.ApiManagement/service/groups@2020-06-01-preview' = {
  name: 'apiManagement/groups'
}

param storageAccount string = 'testStorageAccount'
param location string = 'testLocation'

var useDefaultSettings = true";
            var referencedBicepFilePath     = FileHelper.SaveResultFile(TestContext, "groups.bicep", referencedBicepFileContents, testOutputPath);
            var moduleUri = DocumentUri.FromFileSystemPath(referencedBicepFilePath);

            fileSystemDict[moduleUri.ToUri()] = referencedBicepFileContents;

            var telemetryEventsListener = new MultipleMessageListener <BicepTelemetryEvent>();

            using var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync(
                      TestContext,
                      options =>
            {
                options.OnTelemetryEvent <BicepTelemetryEvent>(telemetry => telemetryEventsListener.AddMessage(telemetry));
            },
                      creationOptions : new Server.CreationOptions(FileResolver: new InMemoryFileResolver(fileSystemDict)));

            var client = helper.Client;

            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1));

            var bicepTelemetryEvent = await telemetryEventsListener.WaitNext();

            IDictionary <string, string> properties = new Dictionary <string, string>
            {
                { "enabled", "true" },
                { "simplify-interpolation", "warning" },
                { "no-unused-vars", "info" },
                { "no-hardcoded-env-urls", "warning" },
                { "no-unused-params", "warning" },
                { "prefer-interpolation", "warning" },
                { "protect-commandtoexecute-secrets", "warning" },
                { "no-unnecessary-dependson", "warning" },
                { "adminusername-should-not-be-literal", "warning" },
                { "use-stable-vm-image", "warning" },
                { "secure-parameter-default", "warning" },
                { "outputs-should-not-contain-secrets", "warning" },
                { "explicit-values-for-loc-params", "warning" },
                { "no-loc-expr-outside-params", "none" },
                { "no-hardcoded-location", "warning" }
            };

            bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.LinterRuleStateOnBicepFileOpen);
            bicepTelemetryEvent.Properties.Should().Contain(properties);

            properties = new Dictionary <string, string>
            {
                { "modules", "1" },
                { "parameters", "2" },
                { "resources", "2" },
                { "variables", "1" },
                { "fileSizeInBytes", "488" },
                { "lineCount", "23" },
                {
                    // #disable-next-line
                    //   => Expected at least one diagnostic code at this location. Valid format is "#disable-next-line diagnosticCode1 diagnosticCode2 ..."bicep(BCP226)
                    // resource favorites 'favorites@2015-05-01'{
                    //   => Expected the "=" character at this location.
                    "errors", "2"
                },
                {
                    // param location string = 'testLocation'
                    //   => Parameter "location" is declared but never used.
                    // location: resourceGroup().location
                    //   => Use a parameter here instead of 'resourceGroup().location'. 'resourceGroup().location' and 'deployment().location' should only be used as a default value for parameters.
                    "warnings",
                    "2"
                },
                { "modulesInReferencedFiles", "0" },
                { "parentResourcesInReferencedFiles", "2" },
                { "parametersInReferencedFiles", "2" },
                { "variablesInReferencedFiles", "1" },
                { "lineCountOfReferencedFiles", "12" }
            };

            bicepTelemetryEvent = await telemetryEventsListener.WaitNext();

            bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.BicepFileOpen);
            bicepTelemetryEvent.Properties.Should().Contain(properties);
        }
示例#9
0
        public async Task VerifyDisableNextLineCodeActionInvocationFiresTelemetryEvent()
        {
            var bicepFileContents = @"param storageAccount string = 'testStorageAccount'";
            var bicepFilePath     = FileHelper.SaveResultFile(TestContext, "main.bicep", bicepFileContents);
            var documentUri       = DocumentUri.FromFileSystemPath(bicepFilePath);
            var uri = documentUri.ToUri();

            var files = new Dictionary <Uri, string>
            {
                [uri] = bicepFileContents,
            };

            var compilation = new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, SourceFileGroupingFactory.CreateForFiles(files, uri, BicepTestConstants.FileResolver, BicepTestConstants.BuiltInConfiguration), BicepTestConstants.BuiltInConfiguration, BicepTestConstants.LinterAnalyzer);
            var diagnostics = compilation.GetEntrypointSemanticModel().GetAllDiagnostics();

            var telemetryEventsListener = new MultipleMessageListener <BicepTelemetryEvent>();

            using var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync(
                      TestContext,
                      options =>
            {
                options.OnTelemetryEvent <BicepTelemetryEvent>(telemetry => telemetryEventsListener.AddMessage(telemetry));
            },
                      new Server.CreationOptions(NamespaceProvider: BicepTestConstants.NamespaceProvider, FileResolver: new InMemoryFileResolver(files)));

            var client = helper.Client;

            client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, files[uri], 1));

            var bicepTelemetryEvent = await telemetryEventsListener.WaitNext();

            bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.LinterRuleStateOnBicepFileOpen);

            bicepTelemetryEvent = await telemetryEventsListener.WaitNext();

            bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.BicepFileOpen);

            var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts;

            var codeActions = await client.RequestCodeAction(new CodeActionParams
            {
                TextDocument = new TextDocumentIdentifier(documentUri),
                Range        = diagnostics.First().ToRange(lineStarts)
            });

            var disableNextLineCodeAction = codeActions.First(x => x.CodeAction !.Title == "Disable no-unused-params for this line").CodeAction;

            _ = await client !.ResolveCodeAction(disableNextLineCodeAction !);

            Command?command   = disableNextLineCodeAction !.Command;
            JArray? arguments = command !.Arguments;

            await client.Workspace.ExecuteCommand(command);

            bicepTelemetryEvent = await telemetryEventsListener.WaitNext();

            IDictionary <string, string> properties = new Dictionary <string, string>
            {
                { "code", "no-unused-params" }
            };

            bicepTelemetryEvent.EventName.Should().Be(TelemetryConstants.EventNames.DisableNextLineDiagnostics);
            bicepTelemetryEvent.Properties.Should().Equal(properties);
        }
示例#10
0
        public async Task WithBicepConfigInCurrentDirectory_WhenNewBicepConfigFileIsAddedToParentDirectory_ShouldUseOldConfigSettings()
        {
            (ILanguageClient client, MultipleMessageListener <PublishDiagnosticsParams> diagsListener, string parentDirectoryPath) = await StartServerWithClientConnectionAsync();

            var childDirectoryPath = Path.Combine(parentDirectoryPath, "child");
            var bicepFileContents  = @"param storageAccountName string = 'test'";
            var mainUri            = SaveFile("main.bicep", bicepFileContents, childDirectoryPath);

            var bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""info""
        }
      }
    }
  }
}";

            SaveFile("bicepconfig.json", bicepConfigFileContents, childDirectoryPath);

            // open the main document and verify diagnostics
            {
                client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, bicepFileContents, 1));

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             mainUri,
                                             DiagnosticSeverity.Information,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }

            // add bicepconfig.json to parent directory and verify diagnostics
            {
                bicepConfigFileContents = @"{
  ""analyzers"": {
    ""core"": {
      ""verbose"": false,
      ""enabled"": true,
      ""rules"": {
        ""no-unused-params"": {
          ""level"": ""warning""
        }
      }
    }
  }
}";
                var newBicepConfigUri = SaveFile("bicepconfig.json", bicepConfigFileContents, parentDirectoryPath);

                client.Workspace.DidChangeWatchedFiles(new DidChangeWatchedFilesParams
                {
                    Changes = new Container <FileEvent>(new FileEvent
                    {
                        Type = FileChangeType.Created,
                        Uri  = newBicepConfigUri,
                    })
                });

                await VerifyDiagnosticsAsync(diagsListener,
                                             @"Parameter ""storageAccountName"" is declared but never used.",
                                             mainUri,
                                             DiagnosticSeverity.Information,
                                             new Position(0, 6),
                                             new Position(0, 24),
                                             "https://aka.ms/bicep/linter/no-unused-params");
            }
        }