diff --git a/src/Bicep.Core.UnitTests/BicepTestConstants.cs b/src/Bicep.Core.UnitTests/BicepTestConstants.cs index 41a3a494ada..3007c1ab8ae 100644 --- a/src/Bicep.Core.UnitTests/BicepTestConstants.cs +++ b/src/Bicep.Core.UnitTests/BicepTestConstants.cs @@ -95,6 +95,9 @@ public static IModuleDispatcher CreateModuleDispatcher(IServiceProvider services public static readonly IModuleRestoreScheduler ModuleRestoreScheduler = CreateMockModuleRestoreScheduler(); + public static RootConfiguration GetConfiguration(string contents) + => RootConfiguration.Bind(IConfigurationManager.BuiltInConfigurationElement.Merge(JsonElementFactory.CreateElement(contents))); + public static RootConfiguration CreateMockConfiguration(Dictionary? customConfigurationData = null, string? configFilePath = null) { var configurationData = new Dictionary diff --git a/src/Bicep.Core/Configuration/IConfigurationManager.cs b/src/Bicep.Core/Configuration/IConfigurationManager.cs index e7be5581dd8..cda8e3fe7bf 100644 --- a/src/Bicep.Core/Configuration/IConfigurationManager.cs +++ b/src/Bicep.Core/Configuration/IConfigurationManager.cs @@ -28,7 +28,7 @@ public interface IConfigurationManager public static IConfigurationManager WithStaticConfiguration(RootConfiguration configuration) => new ConstantConfigurationManager(configuration); - protected static readonly JsonElement BuiltInConfigurationElement = GetBuiltInConfigurationElement(); + static readonly JsonElement BuiltInConfigurationElement = GetBuiltInConfigurationElement(); private static readonly Lazy BuiltInConfigurationLazy = new(() => RootConfiguration.Bind(BuiltInConfigurationElement)); diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index f2ee1cc0096..ec378f277aa 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -4128,19 +4128,9 @@ public async Task ModuleRegistryReferenceCompletions_GetCompletionsForFolderInsi [DataRow("using 'br/public:|", BicepSourceFileKind.ParamsFile)] public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string inputWithCursors, BicepSourceFileKind kind) { - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var (text, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); - - var fileName = kind switch - { - BicepSourceFileKind.BicepFile => "main.bicep", - BicepSourceFileKind.ParamsFile => "main.bicepparam", - _ => throw new InvalidOperationException(), - }; - var mainBicepFilePath = FileHelper.SaveResultFile(TestContext, fileName, text, testOutputPath); - var mainUri = DocumentUri.FromFileSystemPath(mainBicepFilePath); - - FileHelper.SaveResultFile(TestContext, "groups.bicep", string.Empty, Path.Combine(testOutputPath, "br")); + var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; + var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); + var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}"); var settingsProvider = StrictMock.Of(); settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); @@ -4151,11 +4141,10 @@ public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string i using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( TestContext, services => services - .AddSingleton(publicRegistryModuleMetadataProvider.Object) - .AddSingleton(settingsProvider.Object) - .WithFileResolver(new FileResolver(new LocalFileSystem()))); + .AddSingleton(publicRegistryModuleMetadataProvider.Object) + .AddSingleton(settingsProvider.Object)); - var file = await new ServerRequestHelper(TestContext, helper).OpenFile(mainUri.ToUriEncoded(), text); + var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); var completions = await file.RequestCompletion(cursor); completions.Count().Should().Be(2); @@ -4174,32 +4163,25 @@ public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string i [DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)] public async Task ModuleRegistryReferenceCompletions_GetVersionCompletions(string inputWithCursors, BicepSourceFileKind kind) { - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var (text, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); - - var fileName = kind switch - { - BicepSourceFileKind.BicepFile => "main.bicep", - BicepSourceFileKind.ParamsFile => "main.bicepparam", - _ => throw new InvalidOperationException(), - }; - var mainBicepFilePath = FileHelper.SaveResultFile(TestContext, fileName, text, testOutputPath); - var mainUri = DocumentUri.FromFileSystemPath(mainBicepFilePath); + var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; + var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); + var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}"); var settingsProvider = StrictMock.Of(); settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); var publicRegistryModuleMetadataProvider = StrictMock.Of(); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerapp", "d1", "contoso.com/help1")]); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]); publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]); using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( TestContext, services => services - .AddSingleton(publicRegistryModuleMetadataProvider.Object) - .AddSingleton(settingsProvider.Object) - .WithFileResolver(new FileResolver(new LocalFileSystem()))); + .AddSingleton(publicRegistryModuleMetadataProvider.Object) + .AddSingleton(settingsProvider.Object)); - var file = await new ServerRequestHelper(TestContext, helper).OpenFile(mainUri.ToUriEncoded(), text); + var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); var completions = await file.RequestCompletion(cursor); completions.Count().Should().Be(2); @@ -4207,6 +4189,41 @@ public async Task ModuleRegistryReferenceCompletions_GetVersionCompletions(strin completions.Should().Contain(x => x.Label == "1.0.2" && x.SortText == "0000" && x.Kind == CompletionItemKind.Snippet && x.Detail == "d1" && x.Documentation!.MarkupContent!.Value == "[View Documentation](contoso.com/help1)"); } + [TestMethod] + [DataRow("module test 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/public:foo|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/public:foo|", BicepSourceFileKind.BicepFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br/public:foo|'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br/public:foo|", BicepSourceFileKind.ParamsFile)] + public async Task Public_registry_completions_support_prefix_matching(string text, BicepSourceFileKind kind) + { + var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; + var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(text, '|'); + var fileUri = new Uri($"file:///{Guid.NewGuid():D}/{TestContext.TestName}/main.{extension}"); + + var settingsProvider = StrictMock.Of(); + settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); + + var publicRegistryModuleMetadataProvider = StrictMock.Of(); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("foo/bar", "d1", "contoso.com/help1"), new("food/bar", "d2", "contoso.com/help2"), new("bar/bar", "d2", "contoso.com/help2")]); + + using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( + TestContext, + services => services + .AddSingleton(publicRegistryModuleMetadataProvider.Object) + .AddSingleton(settingsProvider.Object)); + + var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); + var completions = await file.RequestCompletion(cursor); + + completions.Count().Should().Be(2); + completions.Should().Contain(x => x.Label == "foo/bar"); + completions.Should().Contain(x => x.Label == "food/bar"); + } + [DataTestMethod] [DataRow("var arr1 = [|]")] [DataRow("param arr array = [|]")] diff --git a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs index 20ffc665da8..ebd16911014 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompilationManagerTests.cs @@ -724,11 +724,8 @@ public void GetLinterStateTelemetryOnBicepFileOpen_ShouldReturnTelemetryEvent() } } }"; - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); - var configurationManager = new ConfigurationManager(fileExplorer); - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var rootConfiguration = GetRootConfiguration(testOutputPath, bicepConfigFileContents, configurationManager); + var rootConfiguration = BicepTestConstants.GetConfiguration(bicepConfigFileContents); var telemetryEvent = compilationManager.GetLinterStateTelemetryOnBicepFileOpen(rootConfiguration); @@ -777,11 +774,7 @@ public void GetLinterStateTelemetryOnBicepFileOpen_WithOverallLinterStateDisable } } }"; - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); - var configurationManager = new ConfigurationManager(fileExplorer); - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - - var rootConfiguration = GetRootConfiguration(testOutputPath, bicepConfigFileContents, configurationManager); + var rootConfiguration = BicepTestConstants.GetConfiguration(bicepConfigFileContents); var telemetryEvent = compilationManager.GetLinterStateTelemetryOnBicepFileOpen(rootConfiguration); @@ -801,11 +794,7 @@ public void GetLinterStateTelemetryOnBicepFileOpen_WithNoContents_ShouldUseDefau var compilationManager = CreateBicepCompilationManager(); var bicepConfigFileContents = @"{}"; - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); - var configurationManager = new ConfigurationManager(fileExplorer); - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - - var rootConfiguration = GetRootConfiguration(testOutputPath, bicepConfigFileContents, configurationManager); + var rootConfiguration = BicepTestConstants.GetConfiguration(bicepConfigFileContents); var telemetryEvent = compilationManager.GetLinterStateTelemetryOnBicepFileOpen(rootConfiguration); @@ -914,14 +903,6 @@ param intParam int telemetryEvent.Properties.Should().Contain(properties); } - private RootConfiguration GetRootConfiguration(string testOutputPath, string bicepConfigContents, ConfigurationManager configurationManager) - { - var bicepConfigFilePath = FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigContents, testOutputPath); - var bicepConfigUri = DocumentUri.FromFileSystemPath(bicepConfigFilePath); - - return configurationManager.GetConfiguration(bicepConfigUri.ToUriEncoded()); - } - private BicepCompilationManager CreateBicepCompilationManager() { PublishDiagnosticsParams? receivedParams = null; diff --git a/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs index f2bb86b6fef..7454a064042 100644 --- a/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs @@ -2,9 +2,11 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions.TestingHelpers; using System.Runtime.CompilerServices; using Bicep.Core.Registry.PublicRegistry; using Bicep.Core.UnitTests; +using Bicep.Core.UnitTests.FileSystem; using Bicep.Core.UnitTests.Mock; using Bicep.Core.UnitTests.Utils; using Bicep.IO.FileSystem; @@ -44,10 +46,10 @@ public class ModuleReferenceCompletionProviderTests [DataRow("module test |", 12)] public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCompletionContext_ReturnsCompletionItems(string inputWithCursors, int expectedEnd) { - var completionContext = GetBicepCompletionContext(inputWithCursors, null, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - BicepTestConstants.BuiltInOnlyConfigurationManager, + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -123,11 +125,11 @@ public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCo } } }"; - var completionContext = GetBicepCompletionContext("module test '|'", bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test '|'", bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -192,10 +194,10 @@ public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCo [TestMethod] public async Task GetFilteredCompletions_WithInvalidTextInCompletionContext_ReturnsEmptyListOfCompletionItems() { - var completionContext = GetBicepCompletionContext("module test 'br:/|'", null, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test 'br:/|'"); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - BicepTestConstants.BuiltInOnlyConfigurationManager, + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -212,13 +214,13 @@ public async Task GetFilteredCompletions_WithInvalidTextInCompletionContext_Retu public async Task GetFilteredCompletions_WithInvalidCompletionContext_ReturnsEmptyList(string inputWithCursors) { var publicRegistryModuleMetadataProvider = StrictMock.Of(); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.1", null, null), new("1.0.2", null, null)]); - var completionContext = GetBicepCompletionContext(inputWithCursors, null, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider.Object, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -245,11 +247,10 @@ public async Task GetFilteredCompletions_WithAliasCompletionContext_ReturnsCompl } } }"; - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -318,15 +319,14 @@ public async Task GetFilteredCompletions_WithACRCompletionSettingSetToFalse_Retu } } }"; - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var settingsProviderMock = StrictMock.Of(); settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -367,15 +367,6 @@ public async Task GetFilteredCompletions_WithACRCompletionSettingSetToFalse_Retu [DataRow("module test 'br:|")] public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_ReturnsACRCompletionItemsUsingResourceGraphClient(string inputWithCursors) { - var (bicepFileContents, cursors) = ParserHelper.GetFileWithCursors(inputWithCursors, '|'); - - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var bicepFilePath = FileHelper.SaveResultFile(TestContext, "input.bicep", bicepFileContents, testOutputPath); - var documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); - var bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); - var compilation = bicepCompilationManager.GetCompilation(documentUri)!.Compilation; - var completionContext = BicepCompletionContext.Create(BicepTestConstants.Features, compilation, cursors[0]); - var bicepConfigFileContents = @"{ ""moduleAliases"": { ""br"": { @@ -389,7 +380,7 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_Retu } } }"; - FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigFileContents, testOutputPath); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var settingsProviderMock = StrictMock.Of(); settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(true); @@ -397,10 +388,9 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_Retu var azureContainerRegistriesProvider = StrictMock.Of(); azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), CancellationToken.None)).Returns(new List { "testacr3.azurecr.io", "testacr4.azurecr.io" }.ToAsyncEnumerable()); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -442,7 +432,7 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_Retu public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_AndNoAccessibleRegistries_ReturnsNoACRCompletions( string inputWithCursors) { - var completionContext = GetBicepCompletionContext(inputWithCursors, null, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var settingsProviderMock = StrictMock.Of(); settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(true); @@ -450,10 +440,9 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_AndN var azureContainerRegistriesProvider = StrictMock.Of(); azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), CancellationToken.None)).Returns(new List().ToAsyncEnumerable()); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -487,7 +476,7 @@ public async Task GetFilteredCompletions_WithPublicMcrModuleRegistryCompletionCo var publicRegistryModuleMetadataProvider = StrictMock.Of(); publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-cntrapp1", null, null), new("app/dapr-cntrapp2", "description2", "contoso.com/help2")]); - var completionContext = GetBicepCompletionContext(inputWithCursors, null, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, BicepTestConstants.BuiltInOnlyConfigurationManager, @@ -552,11 +541,10 @@ public async Task GetFilteredCompletions_WithPathCompletionContext_ReturnsComple } } }"; - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -604,13 +592,13 @@ public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_Returns } }"; var publicRegistryModuleMetadataProvider = StrictMock.Of(); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", null, null), new("1.0.1", "d2", "contoso.com/help%20page.html")]); - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider.Object, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -647,13 +635,13 @@ public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_Returns public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_AndNoMatchingModuleName_ReturnsEmptyListOfCompletionItems() { var publicRegistryModuleMetadataProvider = StrictMock.Of(); + publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerappapp")).Returns([]); - var completionContext = GetBicepCompletionContext("module test 'br/public:app/dapr-containerappapp:|'", null, out DocumentUri documentUri); - var fileSystemfileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test 'br/public:app/dapr-containerappapp:|'"); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileSystemfileExplorer), + configMgr, publicRegistryModuleMetadataProvider.Object, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -687,11 +675,10 @@ public async Task GetFilteredCompletions_WithPublicAliasOverriddenInBicepConfigA } } }"; - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -738,11 +725,10 @@ public async Task GetFilteredCompletions_WithAliasForMCRInBicepConfigAndModulePa var publicRegistryModuleMetadataProvider = StrictMock.Of(); publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerappapp", "dapr description", "contoso.com/help")]); - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider.Object, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -793,7 +779,7 @@ public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(str } } }"; - var completionContext = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents, out DocumentUri documentUri); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var publicRegistryModuleMetadataProvider = StrictMock.Of(); publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-cntrapp1", "description1", null), new("app/dapr-cntrapp2", null, "contoso.com/help2")]); @@ -801,10 +787,9 @@ public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(str var telemetryProvider = StrictMock.Of(); telemetryProvider.Setup(x => x.PostEvent(It.IsAny())); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider.Object, settingsProvider, telemetryProvider.Object); @@ -819,14 +804,7 @@ public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(str [TestMethod] public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_AndIsCanceled_EnumerationShouldBeCanceled() { - var (bicepFileContents, cursors) = ParserHelper.GetFileWithCursors("module test 'br:|'", '|'); - - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var bicepFilePath = FileHelper.SaveResultFile(TestContext, "input.bicep", bicepFileContents, testOutputPath); - var documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); - var bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); - var compilation = bicepCompilationManager.GetCompilation(documentUri)!.Compilation; - var completionContext = BicepCompletionContext.Create(BicepTestConstants.Features, compilation, cursors[0]); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test 'br:|'"); var settingsProviderMock = StrictMock.Of(); settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(true); @@ -853,10 +831,9 @@ async IAsyncEnumerable GetUris([EnumeratorCancellation] CancellationToke azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), It.IsAny())) .Returns((Uri uri, CancellationToken ct) => GetUris(ct)); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, - new ConfigurationManager(fileExplorer), + configMgr, publicRegistryModuleMetadataProvider, settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); @@ -868,24 +845,30 @@ async IAsyncEnumerable GetUris([EnumeratorCancellation] CancellationToke secondItemReturned.Should().BeFalse(); } - private BicepCompletionContext GetBicepCompletionContext( + private (BicepCompletionContext, ConfigurationManager, DocumentUri) GetBicepCompletionContext( string inputWithCursors, - string? bicepConfigFileContents, - out DocumentUri documentUri) + string? bicepConfigFileContents = null) { + var documentUri = DocumentUri.FromFileSystemPath("/path/to/main.bicep"); var (bicepFileContents, cursors) = ParserHelper.GetFileWithCursors(inputWithCursors, '|'); - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - var bicepFilePath = FileHelper.SaveResultFile(TestContext, "input.bicep", bicepFileContents, testOutputPath); - documentUri = DocumentUri.FromFileSystemPath(bicepFilePath); - var bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); - var compilation = bicepCompilationManager.GetCompilation(documentUri)!.Compilation; + + var files = new Dictionary + { + ["/path/to/main.bicep"] = bicepFileContents, + }; if (bicepConfigFileContents is not null) { - FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigFileContents, testOutputPath); + files["/path/to/bicepconfig.json"] = bicepConfigFileContents; } - return BicepCompletionContext.Create(BicepTestConstants.Features, compilation, cursors[0]); + var bicepCompilationManager = BicepCompilationManagerHelper.CreateCompilationManager(documentUri, bicepFileContents, true); + var compilation = bicepCompilationManager.GetCompilation(documentUri)!.Compilation; + + return ( + BicepCompletionContext.Create(BicepTestConstants.Features, compilation, cursors[0]), + new ConfigurationManager(new FileSystemFileExplorer(new MockFileSystem(files))), + documentUri); } } } diff --git a/src/Bicep.LangServer.UnitTests/Configuration/ConfigurationHelperTests.cs b/src/Bicep.LangServer.UnitTests/Configuration/ConfigurationHelperTests.cs index 5aeff23418b..0db7b4c3990 100644 --- a/src/Bicep.LangServer.UnitTests/Configuration/ConfigurationHelperTests.cs +++ b/src/Bicep.LangServer.UnitTests/Configuration/ConfigurationHelperTests.cs @@ -35,34 +35,5 @@ public void IsBicepConfigFile_WithDocumentUriCorrespondingToBicepConfigFile_Shou ConfigurationHelper.IsBicepConfigFile(documentUri).Should().Be(true); } - - [TestMethod] - public void TryGetConfiguration_WithValidDocumentUri_ShouldReturnRootConfiguration() - { - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - - var bicepConfigFileContents = @"{ - ""analyzers"": { - ""core"": { - ""verbose"": false, - ""enabled"": true, - ""rules"": { - ""no-unused-params"": { - ""level"": ""info"" - } - } - } - } -}"; - - var bicepConfigFilePath = FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigFileContents, testOutputPath); - var bicepConfigUri = DocumentUri.FromFileSystemPath(bicepConfigFilePath).ToUriEncoded(); - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); - - bool result = ConfigurationHelper.TryGetConfiguration(new ConfigurationManager(fileExplorer), bicepConfigUri, out RootConfiguration? rootConfiguration); - - result.Should().Be(true); - rootConfiguration.Should().NotBeNull(); - } } } diff --git a/src/Bicep.LangServer.UnitTests/Telemetry/TelemetryHelperTests.cs b/src/Bicep.LangServer.UnitTests/Telemetry/TelemetryHelperTests.cs index e8a5d688dad..7549630a82d 100644 --- a/src/Bicep.LangServer.UnitTests/Telemetry/TelemetryHelperTests.cs +++ b/src/Bicep.LangServer.UnitTests/Telemetry/TelemetryHelperTests.cs @@ -2,18 +2,19 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions.TestingHelpers; using Bicep.Core.Analyzers.Linter; using Bicep.Core.Configuration; +using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Utils; -using Bicep.IO.FileSystem; using Bicep.LanguageServer.Telemetry; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; -using LocalFileSystem = System.IO.Abstractions.FileSystem; namespace Bicep.LangServer.UnitTests.Telemetry { + [TestClass] public class TelemetryHelperTests { [NotNull] @@ -513,23 +514,6 @@ public void GetTelemetryEventsForBicepConfigChange_VerifyDefaultRuleSettingsAreU } private (RootConfiguration, RootConfiguration) GetPreviousAndCurrentRootConfiguration(string prevBicepConfigContents, string curBicepConfigContents) - { - var fileExplorer = new FileSystemFileExplorer(new LocalFileSystem()); - var configurationManager = new ConfigurationManager(fileExplorer); - var testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); - - var prevConfiguration = GetRootConfiguration(testOutputPath, prevBicepConfigContents, configurationManager); - var curConfiguration = GetRootConfiguration(testOutputPath, curBicepConfigContents, configurationManager); - - return (prevConfiguration, curConfiguration); - } - - private RootConfiguration GetRootConfiguration(string testOutputPath, string bicepConfigContents, ConfigurationManager configurationManager) - { - var bicepConfigFilePath = FileHelper.SaveResultFile(TestContext, "bicepconfig.json", bicepConfigContents, testOutputPath); - var bicepConfigUri = DocumentUri.FromFileSystemPath(bicepConfigFilePath); - - return configurationManager.GetConfiguration(bicepConfigUri.ToUriEncoded()); - } + => (BicepTestConstants.GetConfiguration(prevBicepConfigContents), BicepTestConstants.GetConfiguration(curBicepConfigContents)); } } diff --git a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs index 38881c2883d..f09b127de6a 100644 --- a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs @@ -297,22 +297,11 @@ private IEnumerable GetOciModulePathCompletions(BicepCompletionC return []; } - if (replacementText == "'br/public:'" || - replacementText == $"'br:{PublicMCRRegistry}/bicep/'" || - replacementText == "'br/public:" || - replacementText == $"'br:{PublicMCRRegistry}/bicep/") - { - return GetPublicModuleCompletions(replacementText, context, sourceFileUri); - } - else - { - List completions = new(); - - completions.AddRange(GetACRPartialPathCompletionsFromBicepConfig(replacementText, context, sourceFileUri)); - completions.AddRange(GetMCRPathCompletionFromBicepConfig(replacementText, context, sourceFileUri)); - - return completions; - } + return [ + .. GetPublicModuleCompletions(replacementText, context), + .. GetACRPartialPathCompletionsFromBicepConfig(replacementText, context, sourceFileUri), + .. GetMCRPathCompletionFromBicepConfig(replacementText, context, sourceFileUri), + ]; } @@ -503,16 +492,30 @@ private IEnumerable GetACRPartialPathCompletionsFromBicepConfig( // br/public: // or // br:mcr.microsoft.com/bicep/: - private IEnumerable GetPublicModuleCompletions(string replacementText, BicepCompletionContext context, Uri sourceUri) + private IEnumerable GetPublicModuleCompletions(string replacementText, BicepCompletionContext context) { - List completions = new(); + var (prefix, suffix) = replacementText switch { + {} x when x.StartsWith("'br/public:", StringComparison.Ordinal) => ("'br/public:", x["'br/public:".Length..].TrimEnd('\'')), + {} x when x.StartsWith($"'br:{PublicMCRRegistry}/bicep/", StringComparison.Ordinal) => ($"'br:{PublicMCRRegistry}/bicep/", x[$"'br:{PublicMCRRegistry}/bicep/".Length..].TrimEnd('\'')), + _ => (null, null), + }; - var replacementTextWithTrimmedEnd = replacementText.TrimEnd('\''); + if (prefix is null || suffix is null) + { + return []; + } + + List completions = new(); var modules = publicRegistryModuleMetadataProvider.GetModulesMetadata(); foreach (var (moduleName, description, documentationUri) in modules) { - var insertText = $"{replacementTextWithTrimmedEnd}{moduleName}:$0'"; + if (!moduleName.StartsWith(suffix, StringComparison.Ordinal)) + { + continue; + } + + var insertText = $"{prefix}{moduleName}:$0'"; var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, moduleName) .WithSnippetEdit(context.ReplacementRange, insertText) diff --git a/src/Bicep.LangServer/Configuration/ConfigurationHelper.cs b/src/Bicep.LangServer/Configuration/ConfigurationHelper.cs index 8e1dcc69d83..8169937e02d 100644 --- a/src/Bicep.LangServer/Configuration/ConfigurationHelper.cs +++ b/src/Bicep.LangServer/Configuration/ConfigurationHelper.cs @@ -23,20 +23,5 @@ public static bool IsBicepConfigFile(DocumentUri documentUri) return false; } } - - public static bool TryGetConfiguration(IConfigurationManager configurationManager, DocumentUri documentUri, [NotNullWhen(true)] out RootConfiguration? rootConfiguration) - { - try - { - rootConfiguration = configurationManager.GetConfiguration(documentUri.ToUriEncoded()); - return true; - } - catch (Exception e) - { - rootConfiguration = null; - Trace.WriteLine($"Encountered issue while getting configuration: {e.Message}"); - return false; - } - } } }