mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Feature/restore db (#403)
* Added service handlers for restore database operations
This commit is contained in:
@@ -40,7 +40,7 @@ dotnet restore %REPOROOT%\test\Microsoft.SqlTools.ServiceLayer.TestDriver.Tests\
|
||||
dotnet build %REPOROOT%\test\Microsoft.SqlTools.ServiceLayer.TestDriver.Tests\Microsoft.SqlTools.ServiceLayer.TestDriver.Tests.csproj %DOTNETCONFIG%
|
||||
|
||||
SET TEST_SERVER=localhost
|
||||
SET SQLTOOLSSERVICE_EXE=%REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\bin\Integration\netcoreapp2.0\win7-x64\Microsoft.SqlTools.ServiceLayer.exe
|
||||
SET SQLTOOLSSERVICE_EXE=%REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\bin\Debug\netcoreapp2.0\win7-x64\MicrosoftSqlToolsServiceLayer.exe
|
||||
SET SERVICECODECOVERAGE=True
|
||||
SET CODECOVERAGETOOL="%WORKINGDIR%packages\OpenCover.4.6.684\tools\OpenCover.Console.exe"
|
||||
SET CODECOVERAGEOUTPUT=coverage.xml
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
{
|
||||
public class RestoreDatabaseServiceTests : ServiceTestBase
|
||||
{
|
||||
private ConnectionService _connectService = TestServiceProvider.Instance.ConnectionService;
|
||||
private Mock<IProtocolEndpoint> serviceHostMock;
|
||||
private DisasterRecoveryService service;
|
||||
|
||||
public RestoreDatabaseServiceTests()
|
||||
{
|
||||
serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
service = CreateService();
|
||||
service.InitializeService(serviceHostMock.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestorePlanShouldCreatedSuccessfullyForFullBackup()
|
||||
{
|
||||
string backupFileName = "FullBackup.bak";
|
||||
bool canRestore = true;
|
||||
await VerifyRestore(backupFileName, canRestore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestoreShouldExecuteSuccessfullyForFullBackup()
|
||||
{
|
||||
string backupFileName = "FullBackup.bak";
|
||||
bool canRestore = true;
|
||||
var restorePlan = await VerifyRestore(backupFileName, canRestore, true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestorePlanShouldFailForDiffBackup()
|
||||
{
|
||||
string backupFileName = "DiffBackup.bak";
|
||||
bool canRestore = false;
|
||||
await VerifyRestore(backupFileName, canRestore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestorePlanShouldFailForTransactionLogBackup()
|
||||
{
|
||||
string backupFileName = "TransactionLogBackup.bak";
|
||||
bool canRestore = false;
|
||||
await VerifyRestore(backupFileName, canRestore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RestorePlanRequestShouldReturnResponseWithDbFiles()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
|
||||
string filePath = GetBackupFilePath("FullBackup.bak");
|
||||
|
||||
RestoreParams restoreParams = new RestoreParams
|
||||
{
|
||||
BackupFilePath = filePath,
|
||||
OwnerUri = queryTempFile.FilePath
|
||||
};
|
||||
|
||||
await RunAndVerify<RestorePlanResponse>(
|
||||
test: (requestContext) => service.HandleRestorePlanRequest(restoreParams, requestContext),
|
||||
verify: ((result) =>
|
||||
{
|
||||
Assert.True(result.DbFiles.Any());
|
||||
Assert.Equal(result.DatabaseName, "BackupTestDb");
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RestoreDatabaseRequestShouldStartTheRestoreTask()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
|
||||
string filePath = GetBackupFilePath("FullBackup.bak");
|
||||
|
||||
RestoreParams restoreParams = new RestoreParams
|
||||
{
|
||||
BackupFilePath = filePath,
|
||||
OwnerUri = queryTempFile.FilePath
|
||||
};
|
||||
|
||||
await RunAndVerify<RestoreResponse>(
|
||||
test: (requestContext) => service.HandleRestoreRequest(restoreParams, requestContext),
|
||||
verify: ((result) =>
|
||||
{
|
||||
string taskId = result.TaskId;
|
||||
var task = SqlTaskManager.Instance.Tasks.FirstOrDefault(x => x.TaskId.ToString() == taskId);
|
||||
Assert.NotNull(task);
|
||||
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DropDatabase(string databaseName)
|
||||
{
|
||||
string dropDatabaseQuery = string.Format(CultureInfo.InvariantCulture,
|
||||
Scripts.DropDatabaseIfExist, databaseName);
|
||||
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", dropDatabaseQuery);
|
||||
}
|
||||
|
||||
private async Task<RestorePlanResponse> VerifyRestore(string backupFileName, bool canRestore, bool execute = false)
|
||||
{
|
||||
string filePath = GetBackupFilePath(backupFileName);
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
|
||||
RestoreDatabaseHelper service = new RestoreDatabaseHelper();
|
||||
var request = new RestoreParams
|
||||
{
|
||||
BackupFilePath = filePath,
|
||||
DatabaseName = string.Empty,
|
||||
OwnerUri = queryTempFile.FilePath
|
||||
};
|
||||
|
||||
var restoreDataObject = service.CreateRestoreDatabaseTaskDataObject(request);
|
||||
var response = service.CreateRestorePlanResponse(restoreDataObject);
|
||||
|
||||
Assert.NotNull(response);
|
||||
Assert.Equal(response.CanRestore, canRestore);
|
||||
if (canRestore)
|
||||
{
|
||||
Assert.True(response.DbFiles.Any());
|
||||
Assert.Equal(response.DatabaseName, "BackupTestDb");
|
||||
if(execute)
|
||||
{
|
||||
await DropDatabase(response.DatabaseName);
|
||||
Thread.Sleep(2000);
|
||||
request.RelocateDbFiles = response.RelocateFilesNeeded;
|
||||
service.ExecuteRestore(restoreDataObject);
|
||||
Assert.True(restoreDataObject.Server.Databases.Contains(response.DatabaseName));
|
||||
await DropDatabase(response.DatabaseName);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private static string TestLocationDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(RunEnvironmentInfo.GetTestDataLocation(), "DisasterRecovery");
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryInfo BackupFileDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
string d = Path.Combine(TestLocationDirectory, "Backups");
|
||||
return new DirectoryInfo(d);
|
||||
}
|
||||
}
|
||||
|
||||
public FileInfo GetBackupFile(string fileName)
|
||||
{
|
||||
return new FileInfo(Path.Combine(BackupFileDirectory.FullName, fileName));
|
||||
}
|
||||
|
||||
private string GetBackupFilePath(string fileName)
|
||||
{
|
||||
FileInfo inputFile = GetBackupFile(fileName);
|
||||
return inputFile.FullName;
|
||||
}
|
||||
|
||||
protected DisasterRecoveryService CreateService()
|
||||
{
|
||||
CreateServiceProviderWithMinServices();
|
||||
|
||||
// Create the service using the service provider, which will initialize dependencies
|
||||
return ServiceProvider.GetService<DisasterRecoveryService>();
|
||||
}
|
||||
|
||||
protected override RegisteredServiceProvider CreateServiceProviderWithMinServices()
|
||||
{
|
||||
return CreateProvider()
|
||||
.RegisterSingleService(new DisasterRecoveryService());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,14 @@
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
|
||||
<ProjectReference Include="../Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.SqlTools.ServiceLayer.UnitTests\Microsoft.SqlTools.ServiceLayer.UnitTests.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170427-09" />
|
||||
<PackageReference Include="xunit" Version="2.2.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<PackageReference Include="xunit" Version="2.2.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Scripts/CreateTestDatabaseObjects.sql" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -50,6 +50,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionService ConnectionService
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConnectionService.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectExplorerService ObjectExplorerService
|
||||
{
|
||||
get
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<PackageReference Include="xunit" Version="2.2.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.2" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Management.SqlScriptPublishModel" Version="140.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<PackageReference Include="xunit" Version="2.2.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.1" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
|
||||
@@ -31,15 +31,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
|
||||
public async Task<TaskResult> FunctionToRun(SqlTask sqlTask)
|
||||
{
|
||||
sqlTask.TaskCanceled += OnTaskCanceled;
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
while (!IsStopped)
|
||||
{
|
||||
//Just keep running
|
||||
if (cancellationTokenSource.Token.IsCancellationRequested)
|
||||
if (sqlTask.TaskStatus == SqlTaskStatus.Canceled)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
break;
|
||||
}
|
||||
if (Failed)
|
||||
{
|
||||
@@ -53,9 +52,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
});
|
||||
}
|
||||
|
||||
private void OnTaskCanceled(object sender, TaskEventArgs<SqlTaskStatus> e)
|
||||
public async Task<TaskResult> FunctionToCancel(SqlTask sqlTask)
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
return new TaskResult
|
||||
{
|
||||
TaskStatus = SqlTaskStatus.Canceled
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,20 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
[Fact]
|
||||
public void CreateSqlTaskGivenInvalidArgumentShouldThrowException()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new SqlTask(null, new DatabaseOperationStub().FunctionToRun));
|
||||
Assert.Throws<ArgumentNullException>(() => new SqlTask(new TaskMetadata(), null));
|
||||
DatabaseOperationStub operation = new DatabaseOperationStub();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => new SqlTask(null, operation.FunctionToRun, operation.FunctionToCancel));
|
||||
Assert.Throws<ArgumentNullException>(() => new SqlTask(new TaskMetadata(), null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSqlTaskShouldGenerateANewId()
|
||||
{
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), new DatabaseOperationStub().FunctionToRun);
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), new DatabaseOperationStub().FunctionToRun, null);
|
||||
Assert.NotNull(sqlTask.TaskId);
|
||||
Assert.True(sqlTask.TaskId != Guid.Empty);
|
||||
|
||||
SqlTask sqlTask2 = new SqlTask(new TaskMetadata(), new DatabaseOperationStub().FunctionToRun);
|
||||
SqlTask sqlTask2 = new SqlTask(new TaskMetadata(), new DatabaseOperationStub().FunctionToRun, null);
|
||||
Assert.False(sqlTask.TaskId.CompareTo(sqlTask2.TaskId) == 0);
|
||||
}
|
||||
|
||||
@@ -40,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
{
|
||||
TaskStatus = expectedStatus
|
||||
};
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun);
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun, null);
|
||||
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.NotStarted);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task => {
|
||||
@@ -67,7 +69,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
{
|
||||
ServerName = "server name",
|
||||
DatabaseName = "database name"
|
||||
}, operation.FunctionToRun);
|
||||
}, operation.FunctionToRun, operation.FunctionToCancel);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task =>
|
||||
{
|
||||
@@ -89,7 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
{
|
||||
TaskStatus = expectedStatus
|
||||
};
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun);
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun, operation.FunctionToCancel);
|
||||
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.NotStarted);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task => {
|
||||
@@ -111,7 +113,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
operation.TaskResult = new TaskResult
|
||||
{
|
||||
};
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun);
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun, operation.FunctionToCancel);
|
||||
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.NotStarted);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task => {
|
||||
@@ -133,7 +135,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
operation.TaskResult = new TaskResult
|
||||
{
|
||||
};
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun);
|
||||
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToRun, operation.FunctionToCancel);
|
||||
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.NotStarted);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task => {
|
||||
|
||||
@@ -71,12 +71,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
operation.TaskResult = new TaskResult
|
||||
{
|
||||
};
|
||||
SqlTask sqlTask = manager.CreateTask(taskMetaData, operation.FunctionToRun);
|
||||
SqlTask sqlTask = manager.CreateTask(taskMetaData, operation.FunctionToRun, operation.FunctionToCancel);
|
||||
Assert.NotNull(sqlTask);
|
||||
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task =>
|
||||
{
|
||||
Assert.Equal(sqlTask.TaskStatus, expectedStatus);
|
||||
Assert.Equal(expectedStatus, sqlTask.TaskStatus);
|
||||
Assert.Equal(sqlTask.IsCancelRequested, true);
|
||||
manager.Reset();
|
||||
|
||||
|
||||
@@ -69,11 +69,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
|
||||
serviceHostMock.AddEventHandling(TaskCreatedNotification.Type, null);
|
||||
serviceHostMock.AddEventHandling(TaskStatusChangedNotification.Type, null);
|
||||
DatabaseOperationStub operation = new DatabaseOperationStub();
|
||||
SqlTask sqlTask = service.TaskManager.CreateTask(taskMetaData, operation.FunctionToRun);
|
||||
SqlTask sqlTask = service.TaskManager.CreateTask(taskMetaData, operation.FunctionToRun, operation.FunctionToCancel);
|
||||
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task =>
|
||||
{
|
||||
serviceHostMock.Verify(x => x.SendEvent(TaskStatusChangedNotification.Type,
|
||||
It.Is<TaskProgressInfo>(t => t.Status == SqlTaskStatus.Canceled)), Times.Once());
|
||||
It.Is<TaskProgressInfo>(t => t.Status == SqlTaskStatus.Canceled)), Times.AtLeastOnce());
|
||||
});
|
||||
CancelTaskParams cancelParams = new CancelTaskParams
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user