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
This commit is contained in:
Benjamin Russell
2017-01-30 15:24:12 -08:00
committed by GitHub
parent 41d38b2107
commit 857b526553
22 changed files with 428 additions and 274 deletions

View File

@@ -3,30 +3,108 @@
// 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.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
{
public class ServiceIntegrationTests
{
#region Get SQL Tests
[Fact]
public async Task QueryExecuteAllBatchesNoOp()
public void GetSqlTextFromDocumentRequestFull()
{
// Setup:
// ... Create a workspace service with a multi-line constructed query
// ... Create a query execution service without a connection service (we won't be
// executing queries), and the previously created workspace service
string query = string.Format("{0}{1}GO{1}{0}", Common.StandardQuery, Environment.NewLine);
var workspaceService = GetDefaultWorkspaceService(query);
var queryService = new QueryExecutionService(null, workspaceService);
// If: I attempt to get query text from execute document params (entire document)
var queryParams = new ExecuteDocumentSelectionParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryText = queryService.GetSqlText(queryParams);
// Then: The text should match the constructed query
Assert.Equal(query, queryText);
}
[Fact]
public void GetSqlTextFromDocumentRequestPartial()
{
// Setup:
// ... Create a workspace service with a multi-line constructed query
string query = string.Format("{0}{1}GO{1}{0}", Common.StandardQuery, Environment.NewLine);
var workspaceService = GetDefaultWorkspaceService(query);
var queryService = new QueryExecutionService(null, workspaceService);
// If: I attempt to get query text from execute document params (partial document)
var queryParams = new ExecuteDocumentSelectionParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.SubsectionDocument};
var queryText = queryService.GetSqlText(queryParams);
// Then: The text should be a subset of the constructed query
Assert.Contains(queryText, query);
}
[Fact]
public void GetSqlTextFromStringRequest()
{
// Setup:
// ... Create a query execution service without a connection service or workspace
// service (we won't execute code that uses either
var queryService = new QueryExecutionService(null, null);
// If: I attempt to get query text from execute string params
var queryParams = new ExecuteStringParams {OwnerUri = Common.OwnerUri, Query = Common.StandardQuery};
var queryText = queryService.GetSqlText(queryParams);
// Then: The text should match the standard query
Assert.Equal(Common.StandardQuery, queryText);
}
[Fact]
public void GetSqlTextFromInvalidType()
{
// Setup:
// ... Mock up an implementation of ExecuteRequestParamsBase
// ... Create a query execution service without a connection service or workspace
// service (we won't execute code that uses either
var mockParams = new Mock<ExecuteRequestParamsBase>().Object;
var queryService = new QueryExecutionService(null, null);
// If: I attempt to get query text from the mock params
// Then: It should throw an exception
Assert.Throws<InvalidCastException>(() => queryService.GetSqlText(mockParams));
}
#endregion
#region Execution Tests
// NOTE: In order to limit test duplication, we're running the ExecuteDocumentSelection
// version of execute query. The code paths are almost identical.
[Fact]
private async Task QueryExecuteAllBatchesNoOp()
{
// If:
// ... I request to execute a valid query with all batches as no op
var workspaceService = GetDefaultWorkspaceService(string.Format("{0}\r\nGO\r\n{0}", Common.NoOpQuery));
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
var queryParams = new ExecuteDocumentSelectionParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardMessageValidator()
.AddEventValidation(QueryExecuteCompleteEvent.Type, p =>
.AddEventValidation(QueryCompleteEvent.Type, p =>
{
// Validate OwnerURI matches
Assert.Equal(Common.OwnerUri, p.OwnerUri);
@@ -50,9 +128,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... I request to execute a valid query with no results
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
var queryParams = new ExecuteDocumentSelectionParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardMessageValidator()
@@ -78,9 +156,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false,
workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardResultSetValidator()
@@ -106,9 +184,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var dataset = new[] {Common.StandardTestData, Common.StandardTestData};
var queryService = Common.GetPrimedExecutionService(dataset, true, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardResultSetValidator()
@@ -134,9 +212,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
var workspaceService = GetDefaultWorkspaceService(string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery));
var dataSet = new[] {Common.StandardTestData};
var queryService = Common.GetPrimedExecutionService(dataSet, true, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardResultSetValidator()
@@ -166,9 +244,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... I request to execute a query using a file URI that isn't connected
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, false, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = "notConnected", QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = "notConnected", QuerySelection = Common.WholeDocument};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddErrorValidation<string>(Assert.NotEmpty)
.Complete();
await Common.AwaitExecution(queryService, queryParams, efv.Object);
@@ -188,15 +266,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... I request to execute a query
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(null);
var firstRequestContext = RequestContextMocks.Create<ExecuteRequestResult>(null);
await Common.AwaitExecution(queryService, queryParams, firstRequestContext.Object);
// ... And then I request another query without waiting for the first to complete
queryService.ActiveQueries[Common.OwnerUri].HasExecuted = false; // Simulate query hasn't finished
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddErrorValidation<string>(Assert.NotEmpty)
.Complete();
await Common.AwaitExecution(queryService, queryParams, efv.Object);
@@ -216,14 +294,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... I request to execute a query
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(null);
var firstRequestContext = RequestContextMocks.Create<ExecuteRequestResult>(null);
await Common.AwaitExecution(queryService, queryParams, firstRequestContext.Object);
// ... And then I request another query after waiting for the first to complete
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardBatchCompleteValidator()
@@ -250,9 +328,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// If:
// ... I request to execute a query with a missing query string
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = null};
var queryParams = new ExecuteDocumentSelectionParams { OwnerUri = Common.OwnerUri, QuerySelection = null};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddErrorValidation<string>(Assert.NotEmpty)
.Complete();
await queryService.HandleExecuteRequest(queryParams, efv.Object);
@@ -272,9 +350,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... I request to execute a query that is invalid
var workspaceService = GetDefaultWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, true, workspaceService);
var queryParams = new QueryExecuteParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var queryParams = new ExecuteDocumentSelectionParams {OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument};
var efv = new EventFlowValidator<QueryExecuteResult>()
var efv = new EventFlowValidator<ExecuteRequestResult>()
.AddStandardQueryResultValidator()
.AddStandardBatchStartValidator()
.AddStandardBatchCompleteValidator()
@@ -290,6 +368,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.Equal(1, queryService.ActiveQueries.Count);
}
#endregion
private static WorkspaceService<SqlToolsSettings> GetDefaultWorkspaceService(string query)
{
WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings = new SqlToolsSettings();
@@ -300,8 +380,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static class EventFlowValidatorExtensions
{
public static EventFlowValidator<QueryExecuteResult> AddStandardQueryResultValidator(
this EventFlowValidator<QueryExecuteResult> efv)
public static EventFlowValidator<ExecuteRequestResult> AddStandardQueryResultValidator(
this EventFlowValidator<ExecuteRequestResult> efv)
{
// We just need to makes sure we get a result back, there's no params to validate
return efv.AddResultValidation(Assert.NotNull);
@@ -310,7 +390,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static EventFlowValidator<TRequestContext> AddStandardBatchStartValidator<TRequestContext>(
this EventFlowValidator<TRequestContext> efv)
{
return efv.AddEventValidation(QueryExecuteBatchStartEvent.Type, p =>
return efv.AddEventValidation(BatchStartEvent.Type, p =>
{
// Validate OwnerURI and batch summary is returned
Assert.Equal(Common.OwnerUri, p.OwnerUri);
@@ -321,7 +401,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static EventFlowValidator<TRequestContext> AddStandardBatchCompleteValidator<TRequestContext>(
this EventFlowValidator<TRequestContext> efv)
{
return efv.AddEventValidation(QueryExecuteBatchCompleteEvent.Type, p =>
return efv.AddEventValidation(BatchCompleteEvent.Type, p =>
{
// Validate OwnerURI and result summary are returned
Assert.Equal(Common.OwnerUri, p.OwnerUri);
@@ -332,7 +412,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static EventFlowValidator<TRequestContext> AddStandardMessageValidator<TRequestContext>(
this EventFlowValidator<TRequestContext> efv)
{
return efv.AddEventValidation(QueryExecuteMessageEvent.Type, p =>
return efv.AddEventValidation(MessageEvent.Type, p =>
{
// Validate OwnerURI and message are returned
Assert.Equal(Common.OwnerUri, p.OwnerUri);
@@ -343,7 +423,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static EventFlowValidator<TRequestContext> AddStandardResultSetValidator<TRequestContext>(
this EventFlowValidator<TRequestContext> efv)
{
return efv.AddEventValidation(QueryExecuteResultSetCompleteEvent.Type, p =>
return efv.AddEventValidation(ResultSetCompleteEvent.Type, p =>
{
// Validate OwnerURI and summary are returned
Assert.Equal(Common.OwnerUri, p.OwnerUri);
@@ -354,7 +434,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public static EventFlowValidator<TRequestContext> AddStandardQueryCompleteValidator<TRequestContext>(
this EventFlowValidator<TRequestContext> efv, int expectedBatches)
{
return efv.AddEventValidation(QueryExecuteCompleteEvent.Type, p =>
return efv.AddEventValidation(QueryCompleteEvent.Type, p =>
{
Assert.Equal(Common.OwnerUri, p.OwnerUri);
Assert.NotNull(p.BatchSummaries);