Files
sqltoolsservice/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/ExecutionPlanTests.cs
Benjamin Russell 857b526553 query/executeString (#217)
This is a small API addition that allows us to execute queries directly as strings. This will make it easier to execute queries outside the confines of a workspace like VS Code.

* Refactor execution requests and events are now named less redundantly and moved into a separate namespace for organization. This is the bulk of the changes.
    * QueryExecuteBatchNotification -> ExecuteRequests/BatchEvents
    * QueryExecuteMessageNotification -> ExecuteRequests/MessageEvent
    * QueryExecuteCompleteNotification -> ExecuteRequests/QueryCompleteEvent
    * QueryExecuteResultSetCompleteNotification -> ExecuteRequests/ResultSetEvents
    * QueryExecuteSubsetRequest -> SubsetRequest.cs
* Creating an inheritance pattern where
    * `ExecuteRequestParamsBase` has execution options and ID for a query execute request
    * `ExecuteDocumentSelectionParams` inherits from `ExecuteRequestParamsBase` and provides a document selection
    * `ExecuteStringParams`  inherits from `ExecuteRequestParamsBase` and provides the query text
* Adding a helper method to get SQL text based on request type
* Through the AWESOME POWER OF POLYMORPHISM, we are able to create a request for executing straight SQL basically for free.
* **Breaking change:** query/execute => query/executeDocumentSelection to make it more obvious what is expected.
* Adding unit tests for the code that gets SQL text

* Refactoring of execute contracts into their own namespace

* Refactoring application

* Adding new request for executing queries as strings

* Adding forgotten string request

* Changing the logic for checking the request param types

* Removing redundant declarations
2017-01-30 15:24:12 -08:00

245 lines
10 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.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{
public class ExecutionPlanTests
{
#region ResultSet Class Tests
[Fact]
public void ExecutionPlanValid()
{
// Setup:
// ... I have a batch that has been executed with a execution plan
Batch b = Common.GetExecutedBatchWithExecutionPlan();
// If:
// ... I have a result set and I ask for a valid execution plan
ResultSet planResultSet = b.ResultSets.First();
ExecutionPlan plan = planResultSet.GetExecutionPlan().Result;
// Then:
// ... I should get the execution plan back
Assert.Equal("xml", plan.Format);
Assert.Contains("Execution Plan", plan.Content);
}
[Fact]
public async Task ExecutionPlanInvalid()
{
// Setup:
// ... I have a batch that has been executed
Batch b = Common.GetBasicExecutedBatch();
// If:
// ... I have a result set and I ask for an execution plan that doesn't exist
ResultSet planResultSet = b.ResultSets.First();
// Then:
// ... It should throw an exception
await Assert.ThrowsAsync<Exception>(() => planResultSet.GetExecutionPlan());
}
#endregion
#region Batch Class Tests
[Fact]
public void BatchExecutionPlanValidTest()
{
// If I have an executed batch which has an execution plan
Batch b = Common.GetExecutedBatchWithExecutionPlan();
// ... And I ask for a valid execution plan
ExecutionPlan plan = b.GetExecutionPlan(0).Result;
// Then:
// ... I should get the execution plan back
Assert.Equal("xml", plan.Format);
Assert.Contains("Execution Plan", plan.Content);
}
[Fact]
public async Task BatchExecutionPlanInvalidTest()
{
// Setup:
// ... I have a batch that has been executed without an execution plan
Batch b = Common.GetBasicExecutedBatch();
// If:
// ... I ask for an invalid execution plan
await Assert.ThrowsAsync<Exception>(() => b.GetExecutionPlan(0));
}
[Theory]
[InlineData(-1)] // Invalid result set, too low
[InlineData(2)] // Invalid result set, too high
public async Task BatchExecutionPlanInvalidParamsTest(int resultSetIndex)
{
// If I have an executed batch which has an execution plan
Batch b = Common.GetExecutedBatchWithExecutionPlan();
// ... And I ask for an execution plan with an invalid result set index
// Then:
// ... It should throw an exception
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => b.GetExecutionPlan(resultSetIndex));
}
#endregion
#region Query Class Tests
[Theory]
[InlineData(-1)] // Invalid batch, too low
[InlineData(2)] // Invalid batch, too high
public async Task QueryExecutionPlanInvalidParamsTest(int batchIndex)
{
// Setup query settings
QueryExecutionSettings querySettings = new QueryExecutionSettings
{
ExecutionPlanOptions = new ExecutionPlanOptions
{
IncludeActualExecutionPlanXml = false,
IncludeEstimatedExecutionPlanXml = true
}
};
// If I have an executed query
Query q = Common.GetBasicExecutedQuery(querySettings);
// ... And I ask for a subset with an invalid result set index
// Then:
// ... It should throw an exception
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => q.GetExecutionPlan(batchIndex, 0));
}
#endregion
#region Service Intergration Tests
[Fact]
public async Task ExecutionPlanServiceValidTest()
{
// If:
// ... I have a query that has results in the form of an execution plan
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(new[] {Common.GetExecutionPlanTestData()}, true, false, workspaceService);
var executeParams = new ExecuteDocumentSelectionParams
{
QuerySelection = null,
OwnerUri = Common.OwnerUri,
ExecutionPlanOptions = new ExecutionPlanOptions
{
IncludeActualExecutionPlanXml = false,
IncludeEstimatedExecutionPlanXml = true
}
};
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// ... And I then ask for a valid execution plan
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, BatchIndex = 0, ResultSetIndex = 0 };
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
.AddResultValidation(r =>
{
// Then: Messages should be null and execution plan should not be null
Assert.NotNull(r.ExecutionPlan);
}).Complete();
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
executionPlanRequest.Validate();
}
[Fact]
public async Task ExecutionPlanServiceMissingQueryTest()
{
// If:
// ... I ask for an execution plan for a file that hasn't executed a query
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
.AddErrorValidation<string>(Assert.NotNull).Complete();
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
executionPlanRequest.Validate();
}
[Fact]
public async Task ExecutionPlanServiceUnexecutedQueryTest()
{
// If:
// ... I have a query that hasn't finished executing (doesn't matter what)
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(new[] { Common.GetExecutionPlanTestData() }, true, false, workspaceService);
var executeParams = new ExecuteDocumentSelectionParams
{
QuerySelection = null,
OwnerUri = Common.OwnerUri,
ExecutionPlanOptions = new ExecutionPlanOptions
{
IncludeActualExecutionPlanXml = false,
IncludeEstimatedExecutionPlanXml = true
}
};
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
queryService.ActiveQueries[Common.OwnerUri].Batches[0].ResultSets[0].hasBeenRead = false;
// ... And I then ask for a valid execution plan from it
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
.AddErrorValidation<string>(Assert.NotNull).Complete();
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
executionPlanRequest.Validate();
}
[Fact]
public async Task ExecutionPlanServiceOutOfRangeSubsetTest()
{
// If:
// ... I have a query that doesn't have any result sets
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var executeParams = new ExecuteDocumentSelectionParams
{
QuerySelection = null,
OwnerUri = Common.OwnerUri,
ExecutionPlanOptions = new ExecutionPlanOptions
{
IncludeActualExecutionPlanXml = false,
IncludeEstimatedExecutionPlanXml = true
}
};
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// ... And I then ask for an execution plan from a result set
var executionPlanParams = new QueryExecutionPlanParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
var executionPlanRequest = new EventFlowValidator<QueryExecutionPlanResult>()
.AddErrorValidation<string>(Assert.NotNull).Complete();
await queryService.HandleExecutionPlanRequest(executionPlanParams, executionPlanRequest.Object);
executionPlanRequest.Validate();
}
#endregion
}
}