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
152 lines
6.9 KiB
C#
152 lines
6.9 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.Threading.Tasks;
|
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
|
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|
{
|
|
public class CancelTests
|
|
{
|
|
[Fact]
|
|
public async void CancelInProgressQueryTest()
|
|
{
|
|
// Set up file for returning the query
|
|
var fileMock = new Mock<ScriptFile>();
|
|
fileMock.Setup(file => file.GetLinesInRange(It.IsAny<BufferRange>()))
|
|
.Returns(new[] { Common.StandardQuery });
|
|
// Set up workspace mock
|
|
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
|
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
|
.Returns(fileMock.Object);
|
|
|
|
// If:
|
|
// ... I request a query (doesn't matter what kind) and execute it
|
|
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
|
|
var executeParams = new QueryExecuteParams { QuerySelection = Common.SubsectionDocument, OwnerUri = Common.OwnerUri };
|
|
var executeRequest =
|
|
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
|
|
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
|
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
|
queryService.ActiveQueries[Common.OwnerUri].HasExecuted = false; // Fake that it hasn't completed execution
|
|
|
|
// ... And then I request to cancel the query
|
|
var cancelParams = new QueryCancelParams {OwnerUri = Common.OwnerUri};
|
|
QueryCancelResult result = null;
|
|
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
|
|
await queryService.HandleCancelRequest(cancelParams, cancelRequest.Object);
|
|
|
|
// Then:
|
|
// ... I should have seen a successful event (no messages)
|
|
VerifyQueryCancelCallCount(cancelRequest, Times.Once(), Times.Never());
|
|
Assert.Null(result.Messages);
|
|
|
|
// ... The query should not have been disposed
|
|
Assert.Equal(1, queryService.ActiveQueries.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async void CancelExecutedQueryTest()
|
|
{
|
|
|
|
// Set up file for returning the query
|
|
var fileMock = new Mock<ScriptFile>();
|
|
fileMock.SetupGet(file => file.Contents).Returns(Common.StandardQuery);
|
|
// Set up workspace mock
|
|
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
|
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
|
.Returns(fileMock.Object);
|
|
// If:
|
|
// ... I request a query (doesn't matter what kind) and wait for execution
|
|
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
|
|
var executeParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
|
|
var executeRequest =
|
|
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
|
|
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
|
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
|
|
|
// ... And then I request to cancel the query
|
|
var cancelParams = new QueryCancelParams {OwnerUri = Common.OwnerUri};
|
|
QueryCancelResult result = null;
|
|
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
|
|
await queryService.HandleCancelRequest(cancelParams, cancelRequest.Object);
|
|
|
|
// Then:
|
|
// ... I should have seen a result event with an error message
|
|
VerifyQueryCancelCallCount(cancelRequest, Times.Once(), Times.Never());
|
|
Assert.NotNull(result.Messages);
|
|
|
|
// ... The query should not have been disposed
|
|
Assert.NotEmpty(queryService.ActiveQueries);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CancelNonExistantTest()
|
|
{
|
|
|
|
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
|
// If:
|
|
// ... I request to cancel a query that doesn't exist
|
|
var queryService = Common.GetPrimedExecutionService(null, false, false, workspaceService.Object);
|
|
var cancelParams = new QueryCancelParams {OwnerUri = "Doesn't Exist"};
|
|
QueryCancelResult result = null;
|
|
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
|
|
await queryService.HandleCancelRequest(cancelParams, cancelRequest.Object);
|
|
|
|
// Then:
|
|
// ... I should have seen a result event with an error message
|
|
VerifyQueryCancelCallCount(cancelRequest, Times.Once(), Times.Never());
|
|
Assert.NotNull(result.Messages);
|
|
}
|
|
|
|
#region Mocking
|
|
|
|
private static Mock<RequestContext<QueryCancelResult>> GetQueryCancelResultContextMock(
|
|
Action<QueryCancelResult> resultCallback,
|
|
Action<object> errorCallback)
|
|
{
|
|
var requestContext = new Mock<RequestContext<QueryCancelResult>>();
|
|
|
|
// Setup the mock for SendResult
|
|
var sendResultFlow = requestContext
|
|
.Setup(rc => rc.SendResult(It.IsAny<QueryCancelResult>()))
|
|
.Returns(Task.FromResult(0));
|
|
if (resultCallback != null)
|
|
{
|
|
sendResultFlow.Callback(resultCallback);
|
|
}
|
|
|
|
// Setup the mock for SendError
|
|
var sendErrorFlow = requestContext
|
|
.Setup(rc => rc.SendError(It.IsAny<object>()))
|
|
.Returns(Task.FromResult(0));
|
|
if (errorCallback != null)
|
|
{
|
|
sendErrorFlow.Callback(errorCallback);
|
|
}
|
|
|
|
return requestContext;
|
|
}
|
|
|
|
private static void VerifyQueryCancelCallCount(Mock<RequestContext<QueryCancelResult>> mock,
|
|
Times sendResultCalls, Times sendErrorCalls)
|
|
{
|
|
mock.Verify(rc => rc.SendResult(It.IsAny<QueryCancelResult>()), sendResultCalls);
|
|
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|