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(); }
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); }
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(); }
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"); } }
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)); } ); }
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"); } }
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"); } }
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); }
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); }
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"); } }