mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-03-01 02:51:43 -05:00
Finishing up unit tests
This commit is contained in:
@@ -42,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
public Query(string queryText, ConnectionInfo connection)
|
public Query(string queryText, ConnectionInfo connection)
|
||||||
{
|
{
|
||||||
// Sanity check for input
|
// Sanity check for input
|
||||||
if (queryText == null)
|
if (String.IsNullOrWhiteSpace(queryText))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(queryText), "Query text cannot be null");
|
throw new ArgumentNullException(nameof(queryText), "Query text cannot be null");
|
||||||
}
|
}
|
||||||
@@ -68,50 +68,55 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a connection from the connection details
|
// Create a connection from the connection details
|
||||||
string connectionString = ConnectionService.BuildConnectionString(EditorConnection.ConnectionDetails);
|
try
|
||||||
using (DbConnection conn = EditorConnection.Factory.CreateSqlConnection(connectionString))
|
|
||||||
{
|
{
|
||||||
await conn.OpenAsync(cancellationSource.Token);
|
string connectionString = ConnectionService.BuildConnectionString(EditorConnection.ConnectionDetails);
|
||||||
|
using (DbConnection conn = EditorConnection.Factory.CreateSqlConnection(connectionString))
|
||||||
// Create a command that we'll use for executing the query
|
|
||||||
using (DbCommand command = conn.CreateCommand())
|
|
||||||
{
|
{
|
||||||
command.CommandText = QueryText;
|
await conn.OpenAsync(cancellationSource.Token);
|
||||||
command.CommandType = CommandType.Text;
|
|
||||||
|
|
||||||
// Execute the command to get back a reader
|
// Create a command that we'll use for executing the query
|
||||||
using (DbDataReader reader = await command.ExecuteReaderAsync(cancellationSource.Token))
|
using (DbCommand command = conn.CreateCommand())
|
||||||
{
|
{
|
||||||
do
|
command.CommandText = QueryText;
|
||||||
|
command.CommandType = CommandType.Text;
|
||||||
|
|
||||||
|
// Execute the command to get back a reader
|
||||||
|
using (DbDataReader reader = await command.ExecuteReaderAsync(cancellationSource.Token))
|
||||||
{
|
{
|
||||||
// TODO: This doesn't properly handle scenarios where the query is SELECT but does not have rows
|
do
|
||||||
if (!reader.HasRows)
|
|
||||||
{
|
{
|
||||||
continue;
|
// TODO: This doesn't properly handle scenarios where the query is SELECT but does not have rows
|
||||||
}
|
if (!reader.HasRows)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Read until we hit the end of the result set
|
// Read until we hit the end of the result set
|
||||||
ResultSet resultSet = new ResultSet();
|
ResultSet resultSet = new ResultSet();
|
||||||
while (await reader.ReadAsync(cancellationSource.Token))
|
while (await reader.ReadAsync(cancellationSource.Token))
|
||||||
{
|
{
|
||||||
resultSet.AddRow(reader);
|
resultSet.AddRow(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read off the column schema information
|
// Read off the column schema information
|
||||||
if (reader.CanGetColumnSchema())
|
if (reader.CanGetColumnSchema())
|
||||||
{
|
{
|
||||||
resultSet.Columns = reader.GetColumnSchema().ToArray();
|
resultSet.Columns = reader.GetColumnSchema().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the result set to the results of the query
|
// Add the result set to the results of the query
|
||||||
ResultSets.Add(resultSet);
|
ResultSets.Add(resultSet);
|
||||||
} while (await reader.NextResultAsync(cancellationSource.Token));
|
} while (await reader.NextResultAsync(cancellationSource.Token));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
// Mark that we have executed
|
{
|
||||||
HasExecuted = true;
|
// Mark that we have executed
|
||||||
|
HasExecuted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultSetSubset GetSubset(int resultSetIndex, int startRow, int rowCount)
|
public ResultSetSubset GetSubset(int resultSetIndex, int startRow, int rowCount)
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
using System;
|
//
|
||||||
|
// 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.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Data.Common;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
@@ -36,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
private readonly Lazy<ConcurrentDictionary<string, Query>> queries =
|
private readonly Lazy<ConcurrentDictionary<string, Query>> queries =
|
||||||
new Lazy<ConcurrentDictionary<string, Query>>(() => new ConcurrentDictionary<string, Query>());
|
new Lazy<ConcurrentDictionary<string, Query>>(() => new ConcurrentDictionary<string, Query>());
|
||||||
|
|
||||||
private ConcurrentDictionary<string, Query> ActiveQueries
|
internal ConcurrentDictionary<string, Query> ActiveQueries
|
||||||
{
|
{
|
||||||
get { return queries.Value; }
|
get { return queries.Value; }
|
||||||
}
|
}
|
||||||
@@ -68,59 +74,37 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
public async Task HandleExecuteRequest(QueryExecuteParams executeParams,
|
public async Task HandleExecuteRequest(QueryExecuteParams executeParams,
|
||||||
RequestContext<QueryExecuteResult> requestContext)
|
RequestContext<QueryExecuteResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to get the connection for the editor
|
try
|
||||||
ConnectionInfo connectionInfo;
|
|
||||||
if(!ConnectionService.TryFindConnection(executeParams.OwnerUri, out connectionInfo))
|
|
||||||
{
|
{
|
||||||
await requestContext.SendError("This editor is not connected to a database.");
|
// Get a query new active query
|
||||||
return;
|
Query newQuery = await CreateAndActivateNewQuery(executeParams, requestContext);
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
|
await ExecuteAndCompleteQuery(executeParams, requestContext, newQuery);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
// If there is already an in-flight query, error out
|
|
||||||
Query newQuery = new Query(executeParams.QueryText, connectionInfo);
|
|
||||||
if (!ActiveQueries.TryAdd(executeParams.OwnerUri, newQuery))
|
|
||||||
{
|
{
|
||||||
await requestContext.SendError("A query is already in progress for this editor session." +
|
// Dump any unexpected exceptions as errors
|
||||||
"Please cancel this query or wait for its completion.");
|
await requestContext.SendError(e.Message);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch the query and respond with successfully launching it
|
|
||||||
Task executeTask = newQuery.Execute();
|
|
||||||
await requestContext.SendResult(new QueryExecuteResult
|
|
||||||
{
|
|
||||||
Messages = null
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for query execution and then send back the results
|
|
||||||
await Task.WhenAll(executeTask);
|
|
||||||
QueryExecuteCompleteParams eventParams = new QueryExecuteCompleteParams
|
|
||||||
{
|
|
||||||
Error = false,
|
|
||||||
Messages = new string[]{}, // TODO: Figure out how to get messages back from the server
|
|
||||||
OwnerUri = executeParams.OwnerUri,
|
|
||||||
ResultSetSummaries = newQuery.ResultSummary
|
|
||||||
};
|
|
||||||
await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, eventParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleResultSubsetRequest(QueryExecuteSubsetParams subsetParams,
|
public async Task HandleResultSubsetRequest(QueryExecuteSubsetParams subsetParams,
|
||||||
RequestContext<QueryExecuteSubsetResult> requestContext)
|
RequestContext<QueryExecuteSubsetResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to load the query
|
|
||||||
Query query;
|
|
||||||
if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
|
|
||||||
{
|
|
||||||
var errorResult = new QueryExecuteSubsetResult
|
|
||||||
{
|
|
||||||
Message = "The requested query does not exist."
|
|
||||||
};
|
|
||||||
await requestContext.SendResult(errorResult);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Attempt to load the query
|
||||||
|
Query query;
|
||||||
|
if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(new QueryExecuteSubsetResult
|
||||||
|
{
|
||||||
|
Message = "The requested query does not exist."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the requested subset and return it
|
// Retrieve the requested subset and return it
|
||||||
var result = new QueryExecuteSubsetResult
|
var result = new QueryExecuteSubsetResult
|
||||||
{
|
{
|
||||||
@@ -130,34 +114,143 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
};
|
};
|
||||||
await requestContext.SendResult(result);
|
await requestContext.SendResult(result);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (InvalidOperationException ioe)
|
||||||
{
|
{
|
||||||
|
// Return the error as a result
|
||||||
await requestContext.SendResult(new QueryExecuteSubsetResult
|
await requestContext.SendResult(new QueryExecuteSubsetResult
|
||||||
{
|
{
|
||||||
Message = e.Message
|
Message = ioe.Message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (ArgumentOutOfRangeException aoore)
|
||||||
|
{
|
||||||
|
// Return the error as a result
|
||||||
|
await requestContext.SendResult(new QueryExecuteSubsetResult
|
||||||
|
{
|
||||||
|
Message = aoore.Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// This was unexpected, so send back as error
|
||||||
|
await requestContext.SendError(e.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleDisposeRequest(QueryDisposeParams disposeParams,
|
public async Task HandleDisposeRequest(QueryDisposeParams disposeParams,
|
||||||
RequestContext<QueryDisposeResult> requestContext)
|
RequestContext<QueryDisposeResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to remove the query for the owner uri
|
try
|
||||||
Query result;
|
|
||||||
if (!ActiveQueries.TryRemove(disposeParams.OwnerUri, out result))
|
|
||||||
{
|
{
|
||||||
await requestContext.SendError("Failed to dispose query, ID not found.");
|
// Attempt to remove the query for the owner uri
|
||||||
return;
|
Query result;
|
||||||
}
|
if (!ActiveQueries.TryRemove(disposeParams.OwnerUri, out result))
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(new QueryDisposeResult
|
||||||
|
{
|
||||||
|
Messages = "Failed to dispose query, ID not found."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
await requestContext.SendResult(new QueryDisposeResult
|
await requestContext.SendResult(new QueryDisposeResult
|
||||||
|
{
|
||||||
|
Messages = null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Messages = null
|
await requestContext.SendError(e.Message);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private async Task<Query> CreateAndActivateNewQuery(QueryExecuteParams executeParams, RequestContext<QueryExecuteResult> requestContext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Attempt to get the connection for the editor
|
||||||
|
ConnectionInfo connectionInfo;
|
||||||
|
if (!ConnectionService.TryFindConnection(executeParams.OwnerUri, out connectionInfo))
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(new QueryExecuteResult
|
||||||
|
{
|
||||||
|
Messages = "This editor is not connected to a database."
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to clean out any old query on the owner URI
|
||||||
|
Query oldQuery;
|
||||||
|
if (ActiveQueries.TryGetValue(executeParams.OwnerUri, out oldQuery) && oldQuery.HasExecuted)
|
||||||
|
{
|
||||||
|
ActiveQueries.TryRemove(executeParams.OwnerUri, out oldQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't add the query now, it's assumed the query is in progress
|
||||||
|
Query newQuery = new Query(executeParams.QueryText, connectionInfo);
|
||||||
|
if (!ActiveQueries.TryAdd(executeParams.OwnerUri, newQuery))
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(new QueryExecuteResult
|
||||||
|
{
|
||||||
|
Messages = "A query is already in progress for this editor session." +
|
||||||
|
"Please cancel this query or wait for its completion."
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException ane)
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(new QueryExecuteResult { Messages = ane.Message });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Any other exceptions will fall through here and be collected at the end
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteAndCompleteQuery(QueryExecuteParams executeParams, RequestContext<QueryExecuteResult> requestContext, Query query)
|
||||||
|
{
|
||||||
|
// Skip processing if the query is null
|
||||||
|
if (query == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch the query and respond with successfully launching it
|
||||||
|
Task executeTask = query.Execute();
|
||||||
|
await requestContext.SendResult(new QueryExecuteResult
|
||||||
|
{
|
||||||
|
Messages = null
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Wait for query execution and then send back the results
|
||||||
|
await Task.WhenAll(executeTask);
|
||||||
|
QueryExecuteCompleteParams eventParams = new QueryExecuteCompleteParams
|
||||||
|
{
|
||||||
|
Error = false,
|
||||||
|
Messages = new string[] { }, // TODO: Figure out how to get messages back from the server
|
||||||
|
OwnerUri = executeParams.OwnerUri,
|
||||||
|
ResultSetSummaries = query.ResultSummary
|
||||||
|
};
|
||||||
|
await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, eventParams);
|
||||||
|
}
|
||||||
|
catch (DbException dbe)
|
||||||
|
{
|
||||||
|
// Dump the message to a complete event
|
||||||
|
QueryExecuteCompleteParams errorEvent = new QueryExecuteCompleteParams
|
||||||
|
{
|
||||||
|
Error = true,
|
||||||
|
Messages = new[] {dbe.Message},
|
||||||
|
OwnerUri = executeParams.OwnerUri,
|
||||||
|
ResultSetSummaries = query.ResultSummary
|
||||||
|
};
|
||||||
|
await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, errorEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using Moq.Protected;
|
||||||
@@ -13,6 +17,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
{
|
{
|
||||||
public class Common
|
public class Common
|
||||||
{
|
{
|
||||||
|
public const string OwnerUri = "testFile";
|
||||||
|
|
||||||
public static readonly Dictionary<string, string>[] StandardTestData =
|
public static readonly Dictionary<string, string>[] StandardTestData =
|
||||||
{
|
{
|
||||||
new Dictionary<string, string> { {"col1", "val11"}, { "col2", "val12"}, { "col3", "val13"}, { "col4", "col14"} },
|
new Dictionary<string, string> { {"col1", "val11"}, { "col2", "val12"}, { "col3", "val13"}, { "col4", "col14"} },
|
||||||
@@ -45,43 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Mocking
|
#region DbConnection Mocking
|
||||||
|
|
||||||
//private static DbDataReader CreateTestReader(int columnCount, int rowCount)
|
|
||||||
//{
|
|
||||||
// var readerMock = new Mock<DbDataReader> { CallBase = true };
|
|
||||||
|
|
||||||
// // Setup for column reads
|
|
||||||
// // TODO: We can't test columns because of oddities with how datatable/GetColumn
|
|
||||||
|
|
||||||
// // Setup for row reads
|
|
||||||
// var readSequence = readerMock.SetupSequence(dbReader => dbReader.Read());
|
|
||||||
// for (int i = 0; i < rowCount; i++)
|
|
||||||
// {
|
|
||||||
// readSequence.Returns(true);
|
|
||||||
// }
|
|
||||||
// readSequence.Returns(false);
|
|
||||||
|
|
||||||
// // Make sure that if we call for data from the reader it works
|
|
||||||
// readerMock.Setup(dbReader => dbReader[InColumnRange(columnCount)])
|
|
||||||
// .Returns<object>(i => i.ToString());
|
|
||||||
// readerMock.Setup(dbReader => dbReader[NotInColumnRange(columnCount)])
|
|
||||||
// .Throws(new ArgumentOutOfRangeException());
|
|
||||||
// readerMock.Setup(dbReader => dbReader.HasRows)
|
|
||||||
// .Returns(rowCount > 0);
|
|
||||||
|
|
||||||
// return readerMock.Object;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private static int InColumnRange(int columnCount)
|
|
||||||
//{
|
|
||||||
// return Match.Create<int>(i => i < columnCount && i > 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private static int NotInColumnRange(int columnCount)
|
|
||||||
//{
|
|
||||||
// return Match.Create<int>(i => i >= columnCount || i < 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public static DbCommand CreateTestCommand(Dictionary<string, string>[][] data, bool throwOnRead)
|
public static DbCommand CreateTestCommand(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||||
{
|
{
|
||||||
@@ -138,5 +108,74 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Service Mocking
|
||||||
|
|
||||||
|
public static ConnectionDetails GetTestConnectionDetails()
|
||||||
|
{
|
||||||
|
return new ConnectionDetails
|
||||||
|
{
|
||||||
|
DatabaseName = "123",
|
||||||
|
Password = "456",
|
||||||
|
ServerName = "789",
|
||||||
|
UserName = "012"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QueryExecutionService GetPrimedExecutionService(ISqlConnectionFactory factory, bool isConnected)
|
||||||
|
{
|
||||||
|
var connectionService = new ConnectionService(factory);
|
||||||
|
if (isConnected)
|
||||||
|
{
|
||||||
|
connectionService.Connect(new ConnectParams
|
||||||
|
{
|
||||||
|
Connection = GetTestConnectionDetails(),
|
||||||
|
OwnerUri = OwnerUri
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new QueryExecutionService(connectionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Request Mocking
|
||||||
|
|
||||||
|
public static Mock<RequestContext<QueryExecuteResult>> GetQueryExecuteResultContextMock(
|
||||||
|
Action<QueryExecuteResult> resultCallback,
|
||||||
|
Action<EventType<QueryExecuteCompleteParams>, QueryExecuteCompleteParams> eventCallback,
|
||||||
|
Action<object> errorCallback)
|
||||||
|
{
|
||||||
|
var requestContext = new Mock<RequestContext<QueryExecuteResult>>();
|
||||||
|
|
||||||
|
// Setup the mock for SendResult
|
||||||
|
var sendResultFlow = requestContext
|
||||||
|
.Setup(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()))
|
||||||
|
.Returns(Task.FromResult(0));
|
||||||
|
if (resultCallback != null)
|
||||||
|
{
|
||||||
|
sendResultFlow.Callback(resultCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the mock for SendEvent
|
||||||
|
var sendEventFlow = requestContext.Setup(rc => rc.SendEvent(
|
||||||
|
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
||||||
|
It.IsAny<QueryExecuteCompleteParams>()))
|
||||||
|
.Returns(Task.FromResult(0));
|
||||||
|
if (eventCallback != null)
|
||||||
|
{
|
||||||
|
sendEventFlow.Callback(eventCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,93 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
{
|
{
|
||||||
public class DisposeTests
|
public class DisposeTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DisposeExecutedQuery()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request a query (doesn't matter what kind)
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var executeParams = new QueryExecuteParams {QueryText = "Doesn'tMatter", OwnerUri = Common.OwnerUri};
|
||||||
|
var executeRequest = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||||
|
|
||||||
|
// ... And then I dispose of the query
|
||||||
|
var disposeParams = new QueryDisposeParams {OwnerUri = Common.OwnerUri};
|
||||||
|
QueryDisposeResult result = null;
|
||||||
|
var disposeRequest = GetQueryDisposeResultContextMock(qdr => result = qdr, null);
|
||||||
|
queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should have seen a successful result
|
||||||
|
// ... And the active queries should be empty
|
||||||
|
VerifyQueryDisposeCallCount(disposeRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.Empty(queryService.ActiveQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryDisposeMissingQuery()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I attempt to dispose a query that doesn't exist
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false);
|
||||||
|
var disposeParams = new QueryDisposeParams {OwnerUri = Common.OwnerUri};
|
||||||
|
QueryDisposeResult result = null;
|
||||||
|
var disposeRequest = GetQueryDisposeResultContextMock(qdr => result = qdr, null);
|
||||||
|
queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should have gotten an error result
|
||||||
|
VerifyQueryDisposeCallCount(disposeRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.NotNull(result.Messages);
|
||||||
|
Assert.NotEmpty(result.Messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Mocking
|
||||||
|
|
||||||
|
private Mock<RequestContext<QueryDisposeResult>> GetQueryDisposeResultContextMock(
|
||||||
|
Action<QueryDisposeResult> resultCallback,
|
||||||
|
Action<object> errorCallback)
|
||||||
|
{
|
||||||
|
var requestContext = new Mock<RequestContext<QueryDisposeResult>>();
|
||||||
|
|
||||||
|
// Setup the mock for SendResult
|
||||||
|
var sendResultFlow = requestContext
|
||||||
|
.Setup(rc => rc.SendResult(It.IsAny<QueryDisposeResult>()))
|
||||||
|
.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 void VerifyQueryDisposeCallCount(Mock<RequestContext<QueryDisposeResult>> mock, Times sendResultCalls,
|
||||||
|
Times sendErrorCalls)
|
||||||
|
{
|
||||||
|
mock.Verify(rc => rc.SendResult(It.IsAny<QueryDisposeResult>()), sendResultCalls);
|
||||||
|
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
{
|
{
|
||||||
public class ExecuteTests
|
public class ExecuteTests
|
||||||
{
|
{
|
||||||
|
#region Query Class Tests
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void QueryCreationTest()
|
public void QueryCreationTest()
|
||||||
{
|
{
|
||||||
@@ -160,5 +165,222 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
Assert.NotEmpty(query.ResultSets);
|
Assert.NotEmpty(query.ResultSets);
|
||||||
Assert.NotEmpty(query.ResultSummary);
|
Assert.NotEmpty(query.ResultSummary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData(" ")]
|
||||||
|
[InlineData(null)]
|
||||||
|
public void QueryExecuteNoQuery(string query)
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I create a query that has an empty query
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an exception
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new Query(query, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteNoConnectionInfo()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I create a query that has a null connection info
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an exception
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new Query("Some Query", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Service Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteValidNoResultsTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a valid query with no results
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var queryParams = new QueryExecuteParams { QueryText = "Doesn't Matter", OwnerUri = Common.OwnerUri };
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams completeParams = null;
|
||||||
|
var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No Errors should have been sent
|
||||||
|
// ... A successful result should have been sent with no messages
|
||||||
|
// ... A completion event should have been fired with empty results
|
||||||
|
// ... There should be one active query
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.Empty(completeParams.Messages);
|
||||||
|
Assert.Empty(completeParams.ResultSetSummaries);
|
||||||
|
Assert.False(completeParams.Error);
|
||||||
|
Assert.Equal(1, queryService.ActiveQueries.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteValidResultsTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a valid query with results
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Doesn't Matter" };
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams completeParams = null;
|
||||||
|
var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been sent
|
||||||
|
// ... A successful result should have been sent with no messages
|
||||||
|
// ... A completion event should have been fired with one result
|
||||||
|
// ... There should be one active query
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.Empty(completeParams.Messages);
|
||||||
|
Assert.NotEmpty(completeParams.ResultSetSummaries);
|
||||||
|
Assert.False(completeParams.Error);
|
||||||
|
Assert.Equal(1, queryService.ActiveQueries.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteUnconnectedUriTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a query using a file URI that isn't connected
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = "notConnected", QueryText = "Doesn't Matter" };
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... An error message should have been returned via the result
|
||||||
|
// ... No completion event should have been fired
|
||||||
|
// ... No error event should have been fired
|
||||||
|
// ... There should be no active queries
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Never(), Times.Never());
|
||||||
|
Assert.NotNull(result.Messages);
|
||||||
|
Assert.NotEmpty(result.Messages);
|
||||||
|
Assert.Empty(queryService.ActiveQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteInProgressTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a query
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Some Query" };
|
||||||
|
|
||||||
|
// Note, we don't care about the results of the first request
|
||||||
|
var firstRequestContext = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, firstRequestContext.Object).Wait();
|
||||||
|
|
||||||
|
// ... And then I request another query without waiting for the first to complete
|
||||||
|
queryService.ActiveQueries[Common.OwnerUri].HasExecuted = false; // Simulate query hasn't finished
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
var secondRequestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, secondRequestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been sent
|
||||||
|
// ... A result should have been sent with an error message
|
||||||
|
// ... No completion event should have been fired
|
||||||
|
// ... There should only be one active query
|
||||||
|
VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.AtMostOnce(), Times.Never());
|
||||||
|
Assert.NotNull(result.Messages);
|
||||||
|
Assert.NotEmpty(result.Messages);
|
||||||
|
Assert.Equal(1, queryService.ActiveQueries.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteCompletedTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a query
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Some Query" };
|
||||||
|
|
||||||
|
// Note, we don't care about the results of the first request
|
||||||
|
var firstRequestContext = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, firstRequestContext.Object).Wait();
|
||||||
|
|
||||||
|
// ... And then I request another query after waiting for the first to complete
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams complete = null;
|
||||||
|
var secondRequestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, qecp) => complete = qecp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, secondRequestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been sent
|
||||||
|
// ... A result should have been sent with no errors
|
||||||
|
// ... There should only be one active query
|
||||||
|
VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.False(complete.Error);
|
||||||
|
Assert.Equal(1, queryService.ActiveQueries.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData(" ")]
|
||||||
|
[InlineData(null)]
|
||||||
|
public void QueryExecuteMissingQueryTest(string query)
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a query with a missing query string
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = query };
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been sent
|
||||||
|
// ... A result should have been sent with an error message
|
||||||
|
// ... No completion event should have been fired
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Never(), Times.Never());
|
||||||
|
Assert.NotNull(result.Messages);
|
||||||
|
Assert.NotEmpty(result.Messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteInvalidQueryTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a query that is invalid
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, true), true);
|
||||||
|
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Bad query!" };
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams complete = null;
|
||||||
|
var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, qecp) => complete = qecp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been sent
|
||||||
|
// ... A result should have been sent with success (we successfully started the query)
|
||||||
|
// ... A completion event should have been sent with error
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.True(complete.Error);
|
||||||
|
Assert.NotEmpty(complete.Messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void VerifyQueryExecuteCallCount(Mock<RequestContext<QueryExecuteResult>> mock, Times sendResultCalls, Times sendEventCalls, Times sendErrorCalls)
|
||||||
|
{
|
||||||
|
mock.Verify(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()), sendResultCalls);
|
||||||
|
mock.Verify(rc => rc.SendEvent(
|
||||||
|
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
||||||
|
It.IsAny<QueryExecuteCompleteParams>()), sendEventCalls);
|
||||||
|
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
|
||||||
using Microsoft.SqlTools.Test.Utility;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|
||||||
{
|
|
||||||
public class ServiceTests
|
|
||||||
{
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void QueryExecuteValidNoResultsTest()
|
|
||||||
{
|
|
||||||
// If:
|
|
||||||
// ... I request to execute a valid query with no results
|
|
||||||
var queryService = GetPrimedExecutionService(Common.CreateMockFactory(null, false));
|
|
||||||
var queryParams = new QueryExecuteParams
|
|
||||||
{
|
|
||||||
QueryText = "Doesn't Matter",
|
|
||||||
OwnerUri = "testFile"
|
|
||||||
};
|
|
||||||
|
|
||||||
QueryExecuteResult result = null;
|
|
||||||
QueryExecuteCompleteParams completeParams = null;
|
|
||||||
var requestContext = GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
|
||||||
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
|
||||||
|
|
||||||
// Then:
|
|
||||||
// ... No Errors should have been sent
|
|
||||||
// ... A successful result should have been sent with no messages
|
|
||||||
// ... A completion event should have been fired with empty results
|
|
||||||
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
|
||||||
Assert.Null(result.Messages);
|
|
||||||
Assert.Empty(completeParams.Messages);
|
|
||||||
Assert.Empty(completeParams.ResultSetSummaries);
|
|
||||||
Assert.False(completeParams.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void QueryExecuteValidResultsTest()
|
|
||||||
{
|
|
||||||
// If:
|
|
||||||
// ... I request to execute a valid query with results
|
|
||||||
var queryService = GetPrimedExecutionService(Common.CreateMockFactory(new [] {Common.StandardTestData}, false));
|
|
||||||
var queryParams = new QueryExecuteParams {OwnerUri = "testFile", QueryText = "Doesn't Matter"};
|
|
||||||
|
|
||||||
QueryExecuteResult result = null;
|
|
||||||
QueryExecuteCompleteParams completeParams = null;
|
|
||||||
var requestContext = GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
|
||||||
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
|
||||||
|
|
||||||
// Then:
|
|
||||||
// ... No errors should have been send
|
|
||||||
// ... A successful result should have been sent with no messages
|
|
||||||
// ... A completion event should hvae been fired with one result
|
|
||||||
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
|
||||||
Assert.Null(result.Messages);
|
|
||||||
Assert.Empty(completeParams.Messages);
|
|
||||||
Assert.NotEmpty(completeParams.ResultSetSummaries);
|
|
||||||
Assert.False(completeParams.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private ConnectionDetails GetTestConnectionDetails()
|
|
||||||
{
|
|
||||||
return new ConnectionDetails
|
|
||||||
{
|
|
||||||
DatabaseName = "123",
|
|
||||||
Password = "456",
|
|
||||||
ServerName = "789",
|
|
||||||
UserName = "012"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private QueryExecutionService GetPrimedExecutionService(ISqlConnectionFactory factory)
|
|
||||||
{
|
|
||||||
var connectionService = new ConnectionService(factory);
|
|
||||||
connectionService.Connect(new ConnectParams {Connection = GetTestConnectionDetails(), OwnerUri = "testFile"});
|
|
||||||
return new QueryExecutionService(connectionService);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mock<RequestContext<QueryExecuteResult>> GetQueryExecuteResultContextMock(
|
|
||||||
Action<QueryExecuteResult> resultCallback,
|
|
||||||
Action<EventType<QueryExecuteCompleteParams>, QueryExecuteCompleteParams> eventCallback,
|
|
||||||
Action<object> errorCallback)
|
|
||||||
{
|
|
||||||
var requestContext = new Mock<RequestContext<QueryExecuteResult>>();
|
|
||||||
|
|
||||||
// Setup the mock for SendResult
|
|
||||||
var sendResultFlow = requestContext
|
|
||||||
.Setup(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()))
|
|
||||||
.Returns(Task.FromResult(0));
|
|
||||||
if (resultCallback != null)
|
|
||||||
{
|
|
||||||
sendResultFlow.Callback(resultCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the mock for SendEvent
|
|
||||||
var sendEventFlow = requestContext.Setup(rc => rc.SendEvent(
|
|
||||||
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
|
||||||
It.IsAny<QueryExecuteCompleteParams>()))
|
|
||||||
.Returns(Task.FromResult(0));
|
|
||||||
if (eventCallback != null)
|
|
||||||
{
|
|
||||||
sendEventFlow.Callback(eventCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 void VerifyQueryExecuteCallCount(Mock<RequestContext<QueryExecuteResult>> mock, Times sendResultCalls, Times sendEventCalls, Times sendErrorCalls)
|
|
||||||
{
|
|
||||||
mock.Verify(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()), sendResultCalls);
|
|
||||||
mock.Verify(rc => rc.SendEvent(
|
|
||||||
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
|
||||||
It.IsAny<QueryExecuteCompleteParams>()), sendEventCalls);
|
|
||||||
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
{
|
{
|
||||||
public class SubsetTests
|
public class SubsetTests
|
||||||
{
|
{
|
||||||
|
#region Query Class Tests
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(2)]
|
[InlineData(2)]
|
||||||
[InlineData(20)]
|
[InlineData(20)]
|
||||||
@@ -53,5 +58,149 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
// ... It should throw an exception
|
// ... It should throw an exception
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => q.GetSubset(resultSetIndex, rowStartInex, rowCount));
|
Assert.Throws<ArgumentOutOfRangeException>(() => q.GetSubset(resultSetIndex, rowStartInex, rowCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Service Intergration Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubsetServiceValidTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I have a query that has results (doesn't matter what)
|
||||||
|
var queryService =Common.GetPrimedExecutionService(
|
||||||
|
Common.CreateMockFactory(new[] {Common.StandardTestData}, false), true);
|
||||||
|
var executeParams = new QueryExecuteParams {QueryText = "Doesn'tMatter", OwnerUri = Common.OwnerUri};
|
||||||
|
var executeRequest = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||||
|
|
||||||
|
// ... And I then ask for a valid set of results from it
|
||||||
|
var subsetParams = new QueryExecuteSubsetParams {OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0};
|
||||||
|
QueryExecuteSubsetResult result = null;
|
||||||
|
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
|
||||||
|
queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should have a successful result
|
||||||
|
// ... There should be rows there (other test validate that the rows are correct)
|
||||||
|
// ... There should not be any error calls
|
||||||
|
VerifyQuerySubsetCallCount(subsetRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Message);
|
||||||
|
Assert.NotNull(result.ResultSubset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubsetServiceMissingQueryTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I ask for a set of results for a file that hasn't executed a query
|
||||||
|
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
|
||||||
|
var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
|
||||||
|
QueryExecuteSubsetResult result = null;
|
||||||
|
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
|
||||||
|
queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should have an error result
|
||||||
|
// ... There should be no rows in the result set
|
||||||
|
// ... There should not be any error calls
|
||||||
|
VerifyQuerySubsetCallCount(subsetRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.NotNull(result.Message);
|
||||||
|
Assert.Null(result.ResultSubset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubsetServiceUnexecutedQueryTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I have a query that hasn't finished executing (doesn't matter what)
|
||||||
|
var queryService = Common.GetPrimedExecutionService(
|
||||||
|
Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true);
|
||||||
|
var executeParams = new QueryExecuteParams { QueryText = "Doesn'tMatter", OwnerUri = Common.OwnerUri };
|
||||||
|
var executeRequest = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||||
|
queryService.ActiveQueries[Common.OwnerUri].HasExecuted = false;
|
||||||
|
|
||||||
|
// ... And I then ask for a valid set of results from it
|
||||||
|
var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
|
||||||
|
QueryExecuteSubsetResult result = null;
|
||||||
|
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
|
||||||
|
queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should get an error result
|
||||||
|
// ... There should not be rows
|
||||||
|
// ... There should not be any error calls
|
||||||
|
VerifyQuerySubsetCallCount(subsetRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.NotNull(result.Message);
|
||||||
|
Assert.Null(result.ResultSubset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubsetServiceOutOfRangeSubsetTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I have a query that doesn't have any result sets
|
||||||
|
var queryService = Common.GetPrimedExecutionService(
|
||||||
|
Common.CreateMockFactory(null, false), true);
|
||||||
|
var executeParams = new QueryExecuteParams { QueryText = "Doesn'tMatter", OwnerUri = Common.OwnerUri };
|
||||||
|
var executeRequest = Common.GetQueryExecuteResultContextMock(null, null, null);
|
||||||
|
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||||
|
|
||||||
|
// ... And I then ask for a set of results from it
|
||||||
|
var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
|
||||||
|
QueryExecuteSubsetResult result = null;
|
||||||
|
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
|
||||||
|
queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... I should get an error result
|
||||||
|
// ... There should not be rows
|
||||||
|
// ... There should not be any error calls
|
||||||
|
VerifyQuerySubsetCallCount(subsetRequest, Times.Once(), Times.Never());
|
||||||
|
Assert.NotNull(result.Message);
|
||||||
|
Assert.Null(result.ResultSubset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Mocking
|
||||||
|
|
||||||
|
private Mock<RequestContext<QueryExecuteSubsetResult>> GetQuerySubsetResultContextMock(
|
||||||
|
Action<QueryExecuteSubsetResult> resultCallback,
|
||||||
|
Action<object> errorCallback)
|
||||||
|
{
|
||||||
|
var requestContext = new Mock<RequestContext<QueryExecuteSubsetResult>>();
|
||||||
|
|
||||||
|
// Setup the mock for SendResult
|
||||||
|
var sendResultFlow = requestContext
|
||||||
|
.Setup(rc => rc.SendResult(It.IsAny<QueryExecuteSubsetResult>()))
|
||||||
|
.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 void VerifyQuerySubsetCallCount(Mock<RequestContext<QueryExecuteSubsetResult>> mock, Times sendResultCalls,
|
||||||
|
Times sendErrorCalls)
|
||||||
|
{
|
||||||
|
mock.Verify(rc => rc.SendResult(It.IsAny<QueryExecuteSubsetResult>()), sendResultCalls);
|
||||||
|
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user