mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 17:23:27 -05:00
The main change in this pull request is to add a new event that will be fired upon completion of a resultset but before the completion of a batch. This event will only fire if a resultset is available and generated. Changes: * ConnectionService - Slight changes to enable mocking, cleanup * Batch - Moving summary generation into ResultSet class, adding generation of ordinals for resultset and locking of result set list (which needs further refinement, but would be outside scope of this change) * Adding new event and associated parameters for completion of a resultset. Params return the resultset summary * Adding logic for assigning the event a handler in the query execution service * Adding unit tests for testing the new event /making sure the existing tests work * Refactoring some private properties into member variables * Refactor to remove SectionData class in favor of BufferRange * Adding callback for batch completion that will let the extension know that a batch has completed execution * Refactoring to make progressive results work as per async query execution * Allowing retrieval of batch results while query is in progress * reverting global.json, whoops * Adding a few missing comments, and fixing a couple code style bugs * Using SelectionData everywhere again * One more missing comment * Adding new notification type for result set completion * Plumbing event for result set completion * Unit tests for result set events This includes a fairly substantial change to create a mock of the ConnectionService and to create separate memorystream storage arrays. It preserves more correct behavior with a integration test, fixes an issue where the test db reader will return n-1 rows because the Reliable Connection Helper steals a record. * Adding locking to ResultSets for thread safety * Adding/fixing unit tests * Adding batch ID to result set summary
364 lines
18 KiB
C#
364 lines
18 KiB
C#
// 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 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
|
|
{
|
|
/// <summary>
|
|
/// Tests for saving a result set to a file
|
|
/// </summary>
|
|
public class SaveResultsTests
|
|
{
|
|
/// <summary>
|
|
/// Test save results to a file as CSV with correct parameters
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsCsvSuccessTest()
|
|
{
|
|
// Execute a query
|
|
var workplaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new [] {Common.StandardTestData}, true, false, workplaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as csv with correct parameters
|
|
var saveParams = new SaveResultsAsCsvRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_1.csv",
|
|
IncludeHeaders = true
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
|
|
// Call save results and wait on the save task
|
|
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
|
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
|
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);
|
|
Assert.True(File.Exists(saveParams.FilePath));
|
|
|
|
// Delete temp file after test
|
|
if (File.Exists(saveParams.FilePath))
|
|
{
|
|
File.Delete(saveParams.FilePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test save results to a file as CSV with a selection of cells and correct parameters
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsCsvWithSelectionSuccessTest()
|
|
{
|
|
// Execute a query
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new []{Common.StandardTestData}, true, false, workspaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as csv with correct parameters
|
|
var saveParams = new SaveResultsAsCsvRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_2.csv",
|
|
IncludeHeaders = true,
|
|
RowStartIndex = 0,
|
|
RowEndIndex = 0,
|
|
ColumnStartIndex = 0,
|
|
ColumnEndIndex = 0
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
|
|
// Call save results and wait on the save task
|
|
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
|
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
|
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath);
|
|
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);
|
|
Assert.True(File.Exists(saveParams.FilePath));
|
|
|
|
// Delete temp file after test
|
|
if (File.Exists(saveParams.FilePath))
|
|
{
|
|
File.Delete(saveParams.FilePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test handling exception in saving results to CSV file
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsCsvExceptionTest()
|
|
{
|
|
// Execute a query
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false, workspaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as csv with incorrect filepath
|
|
var saveParams = new SaveResultsAsCsvRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.csv" : "/test.csv"
|
|
};
|
|
|
|
SaveResultRequestError errMessage = null;
|
|
var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (SaveResultRequestError) err);
|
|
|
|
// Call save results and wait on the save task
|
|
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
|
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
|
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
|
|
|
// Expect to see error message
|
|
VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
|
|
Assert.NotNull(errMessage);
|
|
Assert.False(File.Exists(saveParams.FilePath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test saving results to CSV file when the requested result set is no longer active
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task SaveResultsAsCsvQueryNotFoundTest()
|
|
{
|
|
// Create a query execution service
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
|
|
|
// Request to save the results as csv with query that is no longer active
|
|
var saveParams = new SaveResultsAsCsvRequestParams
|
|
{
|
|
OwnerUri = "falseuri",
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_3.csv"
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
|
|
|
// Expect message that save failed
|
|
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
|
|
Assert.NotNull(result.Messages);
|
|
Assert.False(File.Exists(saveParams.FilePath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test save results to a file as JSON with correct parameters
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsJsonSuccessTest()
|
|
{
|
|
// Execute a query
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false, workspaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as json with correct parameters
|
|
var saveParams = new SaveResultsAsJsonRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_4.json"
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
|
|
// 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;
|
|
|
|
// Expect to see a file successfully created in filepath and a success message
|
|
Assert.Null(result.Messages);
|
|
Assert.True(File.Exists(saveParams.FilePath));
|
|
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
|
|
|
|
// Delete temp file after test
|
|
if (File.Exists(saveParams.FilePath))
|
|
{
|
|
File.Delete(saveParams.FilePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test save results to a file as JSON with a selection of cells and correct parameters
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsJsonWithSelectionSuccessTest()
|
|
{
|
|
// Execute a query
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new[] { Common.StandardTestData }, true, false, workspaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as json with correct parameters
|
|
var saveParams = new SaveResultsAsJsonRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_5.json",
|
|
RowStartIndex = 0,
|
|
RowEndIndex = 1,
|
|
ColumnStartIndex = 0,
|
|
ColumnEndIndex = 1
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
|
|
// 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];
|
|
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);
|
|
Assert.True(File.Exists(saveParams.FilePath));
|
|
|
|
// Delete temp file after test
|
|
if (File.Exists(saveParams.FilePath))
|
|
{
|
|
File.Delete(saveParams.FilePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test handling exception in saving results to JSON file
|
|
/// </summary>
|
|
[Fact]
|
|
public async void SaveResultsAsJsonExceptionTest()
|
|
{
|
|
// Execute a query
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(new [] {Common.StandardTestData}, true, false, workspaceService);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
|
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
|
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
|
|
|
// Request to save the results as json with incorrect filepath
|
|
var saveParams = new SaveResultsAsJsonRequestParams
|
|
{
|
|
OwnerUri = Common.OwnerUri,
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.json" : "/test.json"
|
|
};
|
|
|
|
|
|
SaveResultRequestError errMessage = null;
|
|
var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (SaveResultRequestError) err);
|
|
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
|
|
|
|
// 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];
|
|
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
|
|
|
// Expect to see error message
|
|
Assert.NotNull(errMessage);
|
|
VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
|
|
Assert.False(File.Exists(saveParams.FilePath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test saving results to JSON file when the requested result set is no longer active
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task SaveResultsAsJsonQueryNotFoundTest()
|
|
{
|
|
// Create a query service
|
|
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
|
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
|
|
|
// Request to save the results as json with query that is no longer active
|
|
var saveParams = new SaveResultsAsJsonRequestParams
|
|
{
|
|
OwnerUri = "falseuri",
|
|
ResultSetIndex = 0,
|
|
BatchIndex = 0,
|
|
FilePath = "testwrite_6.json"
|
|
};
|
|
SaveResultRequestResult result = null;
|
|
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
|
|
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
|
|
|
|
// Expect message that save failed
|
|
Assert.Equal("Failed to save results, ID not found.", result.Messages);
|
|
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
|
|
|
|
}
|
|
}
|