Event Flow Validator (#199)

This is a reworking of the unit tests to permit us to better test events from the service host. This new Event Flow Validator class allows creating a chain of events that can then be validated after execution of the request. Each event can have its own custom validation logic for verifying that the object sent via the service host is correct. It also allows us to validate that the order of events are correct.

The big drawback is that (at this time) the validator cannot support asynchronous events or non-determinant ordering of events. We don't need this for the query execution functionality despite messages being sent asynchronously because async messages aren't sent during unit tests (due to the db message event only being present on SqlDbConnection classes). If the need arises to do async or out of order event validation, then I have some ideas for how we can do that.

* Applying the event flow validator to the query execution service integration tests

* Undoing changes to events that were included in cherry-picked commit

* Cleaning up event flow validation to query execution

* Add efv to cancel tests

* Adding efv to dispose tests

* Adding efv to subset tests

* Adding efv to SaveResults tests

* Copyright
This commit is contained in:
Benjamin Russell
2016-12-20 11:59:03 -08:00
committed by GitHub
parent 0f19de38fb
commit 1e59166147
6 changed files with 481 additions and 615 deletions

View File

@@ -2,15 +2,12 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.IO;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
@@ -42,8 +39,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
FilePath = "testwrite_1.csv",
IncludeHeaders = true
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.Null(r.Messages);
}).Complete();
// Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
@@ -51,8 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await selectedResultSet.GetSaveTask(saveParams.FilePath);
// Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages);
saveRequest.Validate();
Assert.True(File.Exists(saveParams.FilePath));
// Delete temp file after test
@@ -88,8 +87,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
ColumnStartIndex = 0,
ColumnEndIndex = 0
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.Null(r.Messages);
}).Complete();
// Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
@@ -98,8 +100,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await saveTask;
// Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages);
saveRequest.Validate();
Assert.True(File.Exists(saveParams.FilePath));
// Delete temp file after test
@@ -130,9 +131,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
BatchIndex = 0,
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.csv" : "/test.csv"
};
SaveResultRequestError errMessage = null;
var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (SaveResultRequestError) err);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddErrorValidation<SaveResultRequestError>(e =>
{
Assert.False(string.IsNullOrWhiteSpace(e.message));
}).Complete();
// Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
@@ -140,8 +143,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await selectedResultSet.GetSaveTask(saveParams.FilePath);
// Expect to see error message
VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
Assert.NotNull(errMessage);
saveRequest.Validate();
Assert.False(File.Exists(saveParams.FilePath));
}
@@ -163,13 +165,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
BatchIndex = 0,
FilePath = "testwrite_3.csv"
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.NotNull(r.Messages);
}).Complete();
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
// Expect message that save failed
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.NotNull(result.Messages);
saveRequest.Validate();
Assert.False(File.Exists(saveParams.FilePath));
}
@@ -194,19 +198,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
BatchIndex = 0,
FilePath = "testwrite_4.json"
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.Null(r.Messages);
}).Complete();
// Call save results and wait on the save task
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask;
await selectedResultSet.GetSaveTask(saveParams.FilePath);
// Expect to see a file successfully created in filepath and a success message
Assert.Null(result.Messages);
saveRequest.Validate();
Assert.True(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
// Delete temp file after test
if (File.Exists(saveParams.FilePath))
@@ -240,8 +245,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
ColumnStartIndex = 0,
ColumnEndIndex = 1
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.Null(r.Messages);
}).Complete();
// Call save results and wait on the save task
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
@@ -249,8 +257,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await selectedResultSet.GetSaveTask(saveParams.FilePath);
// Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages);
saveRequest.Validate();
Assert.True(File.Exists(saveParams.FilePath));
// Delete temp file after test
@@ -281,10 +288,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
BatchIndex = 0,
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.json" : "/test.json"
};
SaveResultRequestError errMessage = null;
var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (SaveResultRequestError) err);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddErrorValidation<SaveResultRequestError>(e =>
{
Assert.False(string.IsNullOrWhiteSpace(e.message));
}).Complete();
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
// Call save results and wait on the save task
@@ -293,8 +301,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await selectedResultSet.GetSaveTask(saveParams.FilePath);
// Expect to see error message
Assert.NotNull(errMessage);
VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
saveRequest.Validate();
Assert.False(File.Exists(saveParams.FilePath));
}
@@ -316,48 +323,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
BatchIndex = 0,
FilePath = "testwrite_6.json"
};
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
.AddResultValidation(r =>
{
Assert.Equal("Failed to save results, ID not found.", r.Messages);
}).Complete();
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
// Expect message that save failed
Assert.Equal("Failed to save results, ID not found.", result.Messages);
saveRequest.Validate();
Assert.False(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
}
#region Mocking
/// <summary>
/// Mock the requestContext for saving a result set
/// </summary>
/// <param name="resultCallback"></param>
/// <param name="errorCallback"></param>
/// <returns></returns>
private static Mock<RequestContext<SaveResultRequestResult>> GetSaveResultsContextMock(
Action<SaveResultRequestResult> resultCallback,
Action<object> errorCallback)
{
var requestContext = RequestContextMocks.Create(resultCallback)
.AddErrorHandling(errorCallback);
return requestContext;
}
/// <summary>
/// Verify the call count for sendResult and error
/// </summary>
/// <param name="mock"></param>
/// <param name="sendResultCalls"></param>
/// <param name="sendErrorCalls"></param>
private static void VerifySaveResultsCallCount(Mock<RequestContext<SaveResultRequestResult>> mock,
Times sendResultCalls, Times sendErrorCalls)
{
mock.Verify(rc => rc.SendResult(It.IsAny<SaveResultRequestResult>()), sendResultCalls);
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
}
#endregion
}
}