diff --git a/Packages.props b/Packages.props index 576642f1..ea0ac32b 100644 --- a/Packages.props +++ b/Packages.props @@ -20,6 +20,7 @@ + diff --git a/bin/nuget/Microsoft.SqlServer.DacFx.Projects.161.8056.0-alpha.nupkg b/bin/nuget/Microsoft.SqlServer.DacFx.Projects.161.8056.0-alpha.nupkg new file mode 100644 index 00000000..467c2e00 Binary files /dev/null and b/bin/nuget/Microsoft.SqlServer.DacFx.Projects.161.8056.0-alpha.nupkg differ diff --git a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs index cbd556db..aa46a8d9 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs @@ -41,6 +41,7 @@ using Microsoft.SqlTools.ServiceLayer.AzureBlob; using Microsoft.SqlTools.ServiceLayer.ExecutionPlan; using Microsoft.SqlTools.ServiceLayer.ObjectManagement; using System.IO; +using Microsoft.SqlTools.ServiceLayer.SqlProjects; namespace Microsoft.SqlTools.ServiceLayer { @@ -179,6 +180,9 @@ namespace Microsoft.SqlTools.ServiceLayer ObjectManagementService.Instance.InitializeService(serviceHost); serviceProvider.RegisterSingleService(ObjectManagementService.Instance); + SqlProjectsService.Instance.InitializeService(serviceHost); + serviceProvider.RegisterSingleService(SqlProjectsService.Instance); + serviceHost.InitializeRequestHandlers(); } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj index bee3cbc5..e9eca682 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj +++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj @@ -50,6 +50,7 @@ + diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/CloseSqlProject.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/CloseSqlProject.cs new file mode 100644 index 00000000..2b08003d --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/CloseSqlProject.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class CloseSqlProjectRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/closeProject"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/NewSqlProject.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/NewSqlProject.cs new file mode 100644 index 00000000..069e3c7f --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/NewSqlProject.cs @@ -0,0 +1,39 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlServer.Dac.Projects; +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + /// + /// Parameters for creating a new SQL Project + /// + public class NewSqlProjectParams : SqlProjectParams + { + /// + /// Type of SQL Project: SDK-style or Legacy + /// + public ProjectType SqlProjectType { get; set; } + + /// + /// Database schema provider for the project, in the format + /// "Microsoft.Data.Tools.Schema.Sql.SqlXYZDatabaseSchemaProvider". + /// Case sensitive. + /// + public string? DatabaseSchemaProvider { get; set; } + + /// + /// Version of the Microsoft.Build.Sql SDK for the project, if overriding the default + /// + public string? BuildSdkVersion { get; set; } + } + + public class NewSqlProjectRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/newProject"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/OpenSqlProject.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/OpenSqlProject.cs new file mode 100644 index 00000000..33c77aea --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/Projects/OpenSqlProject.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class OpenSqlProjectRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/openProject"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/AddSqlObjectScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/AddSqlObjectScript.cs new file mode 100644 index 00000000..5f6ea9df --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/AddSqlObjectScript.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class AddSqlObjectScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/addSqlObjectScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/DeleteSqlObjectScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/DeleteSqlObjectScript.cs new file mode 100644 index 00000000..409e0422 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/DeleteSqlObjectScript.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; + +using Microsoft.SqlTools.ServiceLayer.Utility; +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class DeleteSqlObjectScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/deleteSqlObjectScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/ExcludeSqlObjectScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/ExcludeSqlObjectScript.cs new file mode 100644 index 00000000..d75aa75d --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjects/ExcludeSqlObjectScript.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class ExcludeSqlObjectScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlprojects/excludeSqlObjectScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlProjectParams.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlProjectParams.cs new file mode 100644 index 00000000..22a60e2d --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlProjectParams.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + /// + /// Parameters for a generic SQL Project operation + /// + public class SqlProjectParams : GeneralRequestDetails + { + /// + /// Absolute path of the project, including .sqlproj + /// + public string ProjectUri { get; set; } + } + + /// + /// Parameters for a SQL Project operation that targets a script + /// + public class SqlProjectScriptParams : SqlProjectParams + { + /// + /// Path of the script, including .sql, relative to the .sqlproj + /// + public string Path { get; set; } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs new file mode 100644 index 00000000..4e6b661c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs @@ -0,0 +1,140 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using Microsoft.SqlServer.Dac.Projects; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.ServiceLayer.Hosting; +using Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects +{ + /// + /// Main class for SqlProjects service + /// + class SqlProjectsService + { + private static readonly Lazy instance = new Lazy(() => new SqlProjectsService()); + + /// + /// Gets the singleton instance object + /// + public static SqlProjectsService Instance => instance.Value; + + private Lazy> projects = new Lazy>(() => new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); + + /// + /// that maps Project URI to Project + /// + public ConcurrentDictionary Projects => projects.Value; + + /// + /// Initializes the service instance + /// + /// + public void InitializeService(ServiceHost serviceHost) + { + // Project-level functions + serviceHost.SetRequestHandler(OpenSqlProjectRequest.Type, HandleOpenSqlProjectRequest, isParallelProcessingSupported: true); + serviceHost.SetRequestHandler(CloseSqlProjectRequest.Type, HandleCloseSqlProjectRequest, isParallelProcessingSupported: true); + serviceHost.SetRequestHandler(NewSqlProjectRequest.Type, HandleNewSqlProjectRequest, isParallelProcessingSupported: true); + + // SQL object script calls + serviceHost.SetRequestHandler(AddSqlObjectScriptRequest.Type, HandleAddSqlObjectScriptRequest, isParallelProcessingSupported: false); + serviceHost.SetRequestHandler(DeleteSqlObjectScriptRequest.Type, HandleDeleteSqlObjectScriptRequest, isParallelProcessingSupported: false); + serviceHost.SetRequestHandler(ExcludeSqlObjectScriptRequest.Type, HandleExcludeSqlObjectScriptRequest, isParallelProcessingSupported: false); + } + + #region Handlers + + #region Project-level functions + + internal async Task HandleOpenSqlProjectRequest(SqlProjectParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri), requestContext); + } + + internal async Task HandleCloseSqlProjectRequest(SqlProjectParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => Projects.TryRemove(requestParams.ProjectUri, out _), requestContext); + } + + internal async Task HandleNewSqlProjectRequest(NewSqlProjectParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(async () => + { + await SqlProject.CreateProjectAsync(requestParams.ProjectUri, requestParams.SqlProjectType, requestParams.DatabaseSchemaProvider); + GetProject(requestParams.ProjectUri); // load into the cache + + }, requestContext); + } + + #endregion + + #region Sql object script calls + + internal async Task HandleAddSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Add(new SqlObjectScript(requestParams.Path)), requestContext); + } + + internal async Task HandleDeleteSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Delete(requestParams.Path), requestContext); + } + + internal async Task HandleExcludeSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Exclude(requestParams.Path), requestContext); + } + + #endregion + + #endregion + + #region Helper methods + + private async Task RunWithErrorHandling(Action action, RequestContext requestContext) + { + await RunWithErrorHandling(async () => await Task.Run(action), requestContext); + } + + private async Task RunWithErrorHandling(Func action, RequestContext requestContext) + { + try + { + await action(); + + await requestContext.SendResult(new ResultStatus() + { + Success = true, + ErrorMessage = null + }); + } + catch (Exception ex) + { + await requestContext.SendResult(new ResultStatus() + { + Success = false, + ErrorMessage = ex.Message + }); + } + } + + private SqlProject GetProject(string projectUri) + { + if (!Projects.ContainsKey(projectUri)) + { + Projects[projectUri] = new SqlProject(projectUri); + } + + return Projects[projectUri]; + } + + #endregion + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs new file mode 100644 index 00000000..00cad9a2 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs @@ -0,0 +1,182 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.IO; +using System.Threading.Tasks; +using Microsoft.SqlServer.Dac.Projects; +using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.SqlProjects; +using Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts; +using Microsoft.SqlTools.ServiceLayer.Test.Common.RequestContextMocking; +using Microsoft.SqlTools.ServiceLayer.Utility; +using NUnit.Framework; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects +{ + public class SqlProjectsServiceTests : TestBase + { + [Test] + public async Task TestErrorDuringExecution() + { + SqlProjectsService service = new(); + string projectUri = await service.CreateSqlProject(); // validates result.Success == true + + // Validate that result indicates failure when there's an exception + MockRequest requestMock = new(); + await service.HandleNewSqlProjectRequest(new NewSqlProjectParams() + { + ProjectUri = projectUri, + SqlProjectType = ProjectType.SdkStyle + + }, requestMock.Object); + + Assert.IsFalse(requestMock.Result.Success); + Assert.IsTrue(requestMock.Result.ErrorMessage!.Contains("Cannot create a new SQL project")); + } + + [Test] + public async Task TestOpenCloseProject() + { + // Setup + string sdkProjectUri = TestContext.CurrentContext.GetTestProjectPath(nameof(TestOpenCloseProject) + "Sdk"); + string legacyProjectUri = TestContext.CurrentContext.GetTestProjectPath(nameof(TestOpenCloseProject) + "Legacy"); + + if (File.Exists(sdkProjectUri)) File.Delete(sdkProjectUri); + if (File.Exists(legacyProjectUri)) File.Delete(legacyProjectUri); + + SqlProjectsService service = new(); + + Assert.AreEqual(0, service.Projects.Count); + + // Validate creating SDK-style project + MockRequest requestMock = new(); + await service.HandleNewSqlProjectRequest(new NewSqlProjectParams() + { + ProjectUri = sdkProjectUri, + SqlProjectType = ProjectType.SdkStyle + + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(1, service.Projects.Count); + Assert.IsTrue(service.Projects.ContainsKey(sdkProjectUri)); + Assert.AreEqual(service.Projects[sdkProjectUri].SqlProjStyle, ProjectType.SdkStyle); + + // Validate creating Legacy-style project + requestMock = new(); + await service.HandleNewSqlProjectRequest(new NewSqlProjectParams() + { + ProjectUri = legacyProjectUri, + SqlProjectType = ProjectType.LegacyStyle + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(2, service.Projects.Count); + Assert.IsTrue(service.Projects.ContainsKey(legacyProjectUri)); + Assert.AreEqual(service.Projects[legacyProjectUri].SqlProjStyle, ProjectType.LegacyStyle); + + // Validate closing a project + requestMock = new(); + await service.HandleCloseSqlProjectRequest(new SqlProjectParams() { ProjectUri = sdkProjectUri }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(1, service.Projects.Count); + Assert.IsTrue(!service.Projects.ContainsKey(sdkProjectUri)); + + // Validate opening a project + requestMock = new(); + await service.HandleOpenSqlProjectRequest(new SqlProjectParams() { ProjectUri = sdkProjectUri }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(2, service.Projects.Count); + Assert.IsTrue(service.Projects.ContainsKey(sdkProjectUri)); + } + + [Test] + public async Task TestSqlObjectScriptAddDeleteExclude() + { + // Setup + SqlProjectsService service = new(); + string projectUri = await service.CreateSqlProject(); + Assert.AreEqual(0, service.Projects[projectUri].SqlObjectScripts.Count); + + // Validate adding a SQL object script + MockRequest requestMock = new(); + string scriptRelativePath = "MyTable.sql"; + string scriptFullPath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); + await File.WriteAllTextAsync(scriptFullPath, "CREATE TABLE [MyTable] ([Id] INT)"); + + await service.HandleAddSqlObjectScriptRequest(new SqlProjectScriptParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(1, service.Projects[projectUri].SqlObjectScripts.Count); + Assert.IsTrue(service.Projects[projectUri].SqlObjectScripts.Contains(scriptRelativePath)); + + // Validate excluding a SQL object script + requestMock = new(); + await service.HandleExcludeSqlObjectScriptRequest(new SqlProjectScriptParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(0, service.Projects[projectUri].SqlObjectScripts.Count); + Assert.IsTrue(File.Exists(scriptFullPath)); + + // Re-add to set up for Delete + requestMock = new(); + await service.HandleAddSqlObjectScriptRequest(new SqlProjectScriptParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(1, service.Projects[projectUri].SqlObjectScripts.Count); + + // Validate deleting a SQL object script + requestMock = new(); + await service.HandleDeleteSqlObjectScriptRequest(new SqlProjectScriptParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + Assert.AreEqual(0, service.Projects[projectUri].SqlObjectScripts.Count); + Assert.IsFalse(File.Exists(scriptFullPath)); + } + } + + internal static class SqlProjectsExtensions + { + /// + /// Uses the service to create a new SQL project + /// + /// + /// + public async static Task CreateSqlProject(this SqlProjectsService service) + { + string projectUri = TestContext.CurrentContext.GetTestProjectPath(); + + MockRequest requestMock = new(); + await service.HandleNewSqlProjectRequest(new NewSqlProjectParams() + { + ProjectUri = projectUri, + SqlProjectType = ProjectType.SdkStyle + + }, requestMock.Object); + + Assert.IsTrue(requestMock.Result.Success); + + return projectUri; + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestBase.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestBase.cs new file mode 100644 index 00000000..a4b51a5a --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestBase.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.IO; +using NUnit.Framework; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility +{ + [TestFixture] + public abstract class TestBase + { + static TestBase() + { + RunTimestamp = DateTime.Now.ToString("yyyyMMdd-HHmmssffff"); + } + + public static string RunTimestamp + { + get; + private set; + } + + public static string TestRunFolder => Path.Join(TestContext.CurrentContext.WorkDirectory, "SqlToolsServiceTestRuns", $"Run{RunTimestamp}"); + + + [OneTimeSetUp] + public void SetUp() + { + if (!Directory.Exists(TestRunFolder)) + { + Directory.CreateDirectory(TestRunFolder); + } + } + + [OneTimeTearDown] + public void TearDown() + { + if (Directory.Exists(TestRunFolder)) + { + Directory.Delete(TestRunFolder, recursive: true); + } + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestContextHelpers.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestContextHelpers.cs new file mode 100644 index 00000000..85e8845c --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/TestContextHelpers.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.IO; +using NUnit.Framework; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility +{ + public static class TestContextHelpers + { + private static string TestName => TestContext.CurrentContext.Test.Name; + + public static string GetTestWorkingFolder(this TestContext context) => Path.Join(TestBase.TestRunFolder, TestName); + + public static string GetTestProjectPath(this TestContext context, string? projectName = null) => Path.Join(context.GetTestWorkingFolder(), $"{projectName ?? TestName}.sqlproj"); + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/RequestContextMocking/RequestContextMocks.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/RequestContextMocking/RequestContextMocks.cs index 68f2bfad..ea102224 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/RequestContextMocking/RequestContextMocks.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/RequestContextMocking/RequestContextMocks.cs @@ -13,7 +13,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common.RequestContextMocking { public static class RequestContextMocks { - public static Mock> Create(Action resultCallback) { var requestContext = new Mock>(); @@ -61,4 +60,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common.RequestContextMocking return mock; } } + + public class MockRequest + { + private T? result; + public T Result => result ?? throw new InvalidOperationException("No result has been sent for the request"); + + public Mock> Mock; + public RequestContext Object => Mock.Object; + + public MockRequest() + { + Mock = RequestContextMocks.Create(actual => result = actual); + } + } }