Unit Test Cleanup (#141)

This is a fairly large set of changes to the unit tests that help isolate the effectiveness of the unit tests.

* Unit tests for query execution have been split into separate files for different classes.
* Unit tests have been added for the ResultSet class which previously did not have tests
* The InMemoryStreamWrapper has been improved to share memory, creating a simulated filesystem
* Creating a mock ConnectionService to decrease noisy exceptions and prevent "row stealing". Unfortunately this lowers code coverage. However, since the tests that touched the connection service were not really testing it, this helps keep us honest. But it will require adding more unit tests for connection service.
* Standardizing the await mechanism for query execution
* Cleaning up the mechanism for getting WorkspaceService mocks and mock FileStreamFactories

* Refactor the query execution tests into their own files

* Removing tests from ExecuteTests.cs that were moved to separate files

* Adding tests for ResultSet class

* Adding test for the FOR XML/JSON component of the resultset class

* Setting up shared storage between file stream readers/writers

* Standardizing on Workspace mocking, awaiting execution completion

* Adding comment for ResultSet class
This commit is contained in:
Benjamin Russell
2016-11-10 11:42:31 -08:00
committed by GitHub
parent 9ff9a02932
commit ec94d986a8
16 changed files with 1171 additions and 1069 deletions

View File

@@ -77,9 +77,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
} }
/// <summary> /// <summary>
/// Default constructor is private since it's a singleton class /// Default constructor should be private since it's a singleton class, but we need a constructor
/// for use in unit test mocking.
/// </summary> /// </summary>
private ConnectionService() public ConnectionService()
{ {
} }
@@ -129,7 +130,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
} }
// Attempts to link a URI to an actively used connection for this URI // Attempts to link a URI to an actively used connection for this URI
public bool TryFindConnection(string ownerUri, out ConnectionInfo connectionInfo) public virtual bool TryFindConnection(string ownerUri, out ConnectionInfo connectionInfo)
{ {
return this.ownerToConnectionMap.TryGetValue(ownerUri, out connectionInfo); return this.ownerToConnectionMap.TryGetValue(ownerUri, out connectionInfo);
} }

View File

@@ -8,7 +8,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Common; using System.Data.Common;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
@@ -17,6 +16,10 @@ using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{ {
/// <summary>
/// Class that represents a resultset the was generated from a query. Contains logic for
/// storing and retrieving results. Is contained by a Batch class.
/// </summary>
public class ResultSet : IDisposable public class ResultSet : IDisposable
{ {
#region Constants #region Constants
@@ -35,11 +38,21 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
#region Member Variables #region Member Variables
/// <summary>
/// The reader to use for this resultset
/// </summary>
private readonly StorageDataReader dataReader;
/// <summary> /// <summary>
/// For IDisposable pattern, whether or not object has been disposed /// For IDisposable pattern, whether or not object has been disposed
/// </summary> /// </summary>
private bool disposed; private bool disposed;
/// <summary>
/// A list of offsets into the buffer file that correspond to where rows start
/// </summary>
private readonly LongList<long> fileOffsets;
/// <summary> /// <summary>
/// The factory to use to get reading/writing handlers /// The factory to use to get reading/writing handlers
/// </summary> /// </summary>
@@ -63,12 +76,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <summary> /// <summary>
/// Whether the resultSet is in the process of being disposed /// Whether the resultSet is in the process of being disposed
/// </summary> /// </summary>
private bool isBeingDisposed; private readonly ConcurrentDictionary<string, Task> saveTasks;
/// <summary>
/// All save tasks currently saving this ResultSet
/// </summary>
private ConcurrentDictionary<string, Task> saveTasks;
#endregion #endregion
@@ -82,11 +90,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Sanity check to make sure we got a reader // Sanity check to make sure we got a reader
Validate.IsNotNull(nameof(reader), SR.QueryServiceResultSetReaderNull); Validate.IsNotNull(nameof(reader), SR.QueryServiceResultSetReaderNull);
DataReader = new StorageDataReader(reader); dataReader = new StorageDataReader(reader);
// Initialize the storage // Initialize the storage
outputFileName = factory.CreateFile(); outputFileName = factory.CreateFile();
FileOffsets = new LongList<long>(); fileOffsets = new LongList<long>();
// Store the factory // Store the factory
fileStreamFactory = factory; fileStreamFactory = factory;
@@ -100,29 +108,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// Whether the resultSet is in the process of being disposed /// Whether the resultSet is in the process of being disposed
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal bool IsBeingDisposed internal bool IsBeingDisposed { get; private set; }
{
get
{
return isBeingDisposed;
}
}
/// <summary> /// <summary>
/// The columns for this result set /// The columns for this result set
/// </summary> /// </summary>
public DbColumnWrapper[] Columns { get; private set; } public DbColumnWrapper[] Columns { get; private set; }
/// <summary>
/// The reader to use for this resultset
/// </summary>
private StorageDataReader DataReader { get; set; }
/// <summary>
/// A list of offsets into the buffer file that correspond to where rows start
/// </summary>
private LongList<long> FileOffsets { get; set; }
/// <summary> /// <summary>
/// Maximum number of characters to store for a field /// Maximum number of characters to store for a field
/// </summary> /// </summary>
@@ -178,14 +170,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
if (isSingleColumnXmlJsonResultSet) if (isSingleColumnXmlJsonResultSet)
{ {
// Iterate over all the rows and process them into a list of string builders // Iterate over all the rows and process them into a list of string builders
IEnumerable<string> rowValues = FileOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)[0].DisplayValue); IEnumerable<string> rowValues = fileOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)[0].DisplayValue);
rows = new[] { new[] { string.Join(string.Empty, rowValues) } }; rows = new[] { new[] { string.Join(string.Empty, rowValues) } };
} }
else else
{ {
// Figure out which rows we need to read back // Figure out which rows we need to read back
IEnumerable<long> rowOffsets = FileOffsets.Skip(startRow).Take(rowCount); IEnumerable<long> rowOffsets = fileOffsets.Skip(startRow).Take(rowCount);
// Iterate over the rows we need and process them into output // Iterate over the rows we need and process them into output
rows = rowOffsets.Select(rowOffset => rows = rowOffsets.Select(rowOffset =>
@@ -216,18 +208,22 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
using (IFileStreamWriter fileWriter = fileStreamFactory.GetWriter(outputFileName, MaxCharsToStore, MaxXmlCharsToStore)) using (IFileStreamWriter fileWriter = fileStreamFactory.GetWriter(outputFileName, MaxCharsToStore, MaxXmlCharsToStore))
{ {
// If we can initialize the columns using the column schema, use that // If we can initialize the columns using the column schema, use that
if (!DataReader.DbDataReader.CanGetColumnSchema()) if (!dataReader.DbDataReader.CanGetColumnSchema())
{ {
throw new InvalidOperationException(SR.QueryServiceResultSetNoColumnSchema); throw new InvalidOperationException(SR.QueryServiceResultSetNoColumnSchema);
} }
Columns = DataReader.Columns; Columns = dataReader.Columns;
long currentFileOffset = 0;
while (await DataReader.ReadAsync(cancellationToken)) long currentFileOffset = 0;
while (await dataReader.ReadAsync(cancellationToken))
{ {
// Store the beginning of the row
long rowStart = currentFileOffset;
currentFileOffset += fileWriter.WriteRow(dataReader);
// Add the row to the list of rows we have only if the row was successfully written
RowCount++; RowCount++;
FileOffsets.Add(currentFileOffset); fileOffsets.Add(rowStart);
currentFileOffset += fileWriter.WriteRow(DataReader);
} }
} }
// Check if resultset is 'for xml/json'. If it is, set isJson/isXml value in column metadata // Check if resultset is 'for xml/json'. If it is, set isJson/isXml value in column metadata
@@ -251,7 +247,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return; return;
} }
isBeingDisposed = true; IsBeingDisposed = true;
// Check if saveTasks are running for this ResultSet // Check if saveTasks are running for this ResultSet
if (!saveTasks.IsEmpty) if (!saveTasks.IsEmpty)
{ {
@@ -263,7 +259,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
fileStreamFactory.DisposeFile(outputFileName); fileStreamFactory.DisposeFile(outputFileName);
} }
disposed = true; disposed = true;
isBeingDisposed = false; IsBeingDisposed = false;
}); });
} }
else else
@@ -274,7 +270,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
fileStreamFactory.DisposeFile(outputFileName); fileStreamFactory.DisposeFile(outputFileName);
} }
disposed = true; disposed = true;
isBeingDisposed = false; IsBeingDisposed = false;
} }
} }

View File

@@ -32,7 +32,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
// If: // If:
// ... I request a query (doesn't matter what kind) and execute it // ... I request a query (doesn't matter what kind) and execute it
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
var executeParams = new QueryExecuteParams { QuerySelection = Common.SubsectionDocument, OwnerUri = Common.OwnerUri }; var executeParams = new QueryExecuteParams { QuerySelection = Common.SubsectionDocument, OwnerUri = Common.OwnerUri };
var executeRequest = var executeRequest =
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null); RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
@@ -44,7 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
var cancelParams = new QueryCancelParams {OwnerUri = Common.OwnerUri}; var cancelParams = new QueryCancelParams {OwnerUri = Common.OwnerUri};
QueryCancelResult result = null; QueryCancelResult result = null;
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null); var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
queryService.HandleCancelRequest(cancelParams, cancelRequest.Object).Wait(); await queryService.HandleCancelRequest(cancelParams, cancelRequest.Object);
// Then: // Then:
// ... I should have seen a successful event (no messages) // ... I should have seen a successful event (no messages)
@@ -68,7 +68,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
.Returns(fileMock.Object); .Returns(fileMock.Object);
// If: // If:
// ... I request a query (doesn't matter what kind) and wait for execution // ... I request a query (doesn't matter what kind) and wait for execution
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
var executeParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri}; var executeParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
var executeRequest = var executeRequest =
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null); RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
@@ -97,11 +97,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>(); var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
// If: // If:
// ... I request to cancel a query that doesn't exist // ... I request to cancel a query that doesn't exist
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, false, false, workspaceService.Object);
var cancelParams = new QueryCancelParams {OwnerUri = "Doesn't Exist"}; var cancelParams = new QueryCancelParams {OwnerUri = "Doesn't Exist"};
QueryCancelResult result = null; QueryCancelResult result = null;
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null); var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
queryService.HandleCancelRequest(cancelParams, cancelRequest.Object).Wait(); await queryService.HandleCancelRequest(cancelParams, cancelRequest.Object);
// Then: // Then:
// ... I should have seen a result event with an error message // ... I should have seen a result event with an error message

View File

@@ -6,16 +6,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Data.SqlClient;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
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.LanguageServices; 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 Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage; using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
@@ -71,7 +67,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public static Batch GetBasicExecutedBatch() public static Batch GetBasicExecutedBatch()
{ {
Batch batch = new Batch(StandardQuery, SubsectionDocument, 1, GetFileStreamFactory()); Batch batch = new Batch(StandardQuery, SubsectionDocument, 1, GetFileStreamFactory(new Dictionary<string, byte[]>()));
batch.Execute(CreateTestConnection(new[] {StandardTestData}, false), CancellationToken.None).Wait(); batch.Execute(CreateTestConnection(new[] {StandardTestData}, false), CancellationToken.None).Wait();
return batch; return batch;
} }
@@ -79,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public static Query GetBasicExecutedQuery() public static Query GetBasicExecutedQuery()
{ {
ConnectionInfo ci = CreateTestConnectionInfo(new[] {StandardTestData}, false); ConnectionInfo ci = CreateTestConnectionInfo(new[] {StandardTestData}, false);
Query query = new Query(StandardQuery, ci, new QueryExecutionSettings(), GetFileStreamFactory()); Query query = new Query(StandardQuery, ci, new QueryExecutionSettings(), GetFileStreamFactory(new Dictionary<string, byte[]>()));
query.Execute(); query.Execute();
query.ExecutionTask.Wait(); query.ExecutionTask.Wait();
return query; return query;
@@ -101,18 +97,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
return output; return output;
} }
public static async Task AwaitExecution(QueryExecutionService service, QueryExecuteParams qeParams,
RequestContext<QueryExecuteResult> requestContext)
{
await service.HandleExecuteRequest(qeParams, requestContext);
if (service.ActiveQueries.ContainsKey(qeParams.OwnerUri) && service.ActiveQueries[qeParams.OwnerUri].ExecutionTask != null)
{
await service.ActiveQueries[qeParams.OwnerUri].ExecutionTask;
}
}
#endregion #endregion
#region FileStreamWriteMocking #region FileStreamWriteMocking
public static IFileStreamFactory GetFileStreamFactory() public static IFileStreamFactory GetFileStreamFactory(Dictionary<string, byte[]> storage)
{ {
Mock<IFileStreamFactory> mock = new Mock<IFileStreamFactory>(); Mock<IFileStreamFactory> mock = new Mock<IFileStreamFactory>();
mock.Setup(fsf => fsf.CreateFile())
.Returns(() =>
{
string fileName = Guid.NewGuid().ToString();
storage.Add(fileName, new byte[8192]);
return fileName;
});
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>())) mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
.Returns(new ServiceBufferFileStreamReader(new InMemoryWrapper(), It.IsAny<string>())); .Returns<string>(output => new ServiceBufferFileStreamReader(new InMemoryWrapper(storage[output]), output));
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
.Returns(new ServiceBufferFileStreamWriter(new InMemoryWrapper(), It.IsAny<string>(), 1024, .Returns<string, int, int>((output, chars, xml) => new ServiceBufferFileStreamWriter(
1024)); new InMemoryWrapper(storage[output]), output, chars, xml));
return mock.Object; return mock.Object;
} }
@@ -121,9 +134,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{ {
private readonly MemoryStream memoryStream; private readonly MemoryStream memoryStream;
private bool readingOnly; private bool readingOnly;
private readonly byte[] storage = new byte[8192];
public InMemoryWrapper() public InMemoryWrapper(byte[] storage)
{ {
memoryStream = new MemoryStream(storage); memoryStream = new MemoryStream(storage);
} }
@@ -226,62 +238,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
#region Service Mocking #region Service Mocking
public static void GetAutoCompleteTestObjects( public static QueryExecutionService GetPrimedExecutionService(Dictionary<string, string>[][] data, bool isConnected, bool throwOnRead, WorkspaceService<SqlToolsSettings> workspaceService)
out TextDocumentPosition textDocument,
out ScriptFile scriptFile,
out ConnectionInfo connInfo
)
{ {
textDocument = new TextDocumentPosition // Create a place for the temp "files" to be written
{ Dictionary<string, byte[]> storage = new Dictionary<string, byte[]>();
TextDocument = new TextDocumentIdentifier {Uri = OwnerUri},
Position = new Position
{
Line = 0,
Character = 0
}
};
// Create the connection factory with the dataset
var factory = CreateTestConnectionInfo(data, throwOnRead).Factory;
connInfo = CreateTestConnectionInfo(null, false); // Mock the connection service
var connectionService = new Mock<ConnectionService>();
var srvConn = GetServerConnection(connInfo); ConnectionInfo ci = new ConnectionInfo(factory, OwnerUri, StandardConnectionDetails);
var metadataProvider = SmoMetadataProvider.CreateConnectedProvider(srvConn); ConnectionInfo outValMock;
var binder = BinderProvider.CreateBinder(metadataProvider); connectionService
connInfo = Common.CreateTestConnectionInfo(null, false); .Setup(service => service.TryFindConnection(It.IsAny<string>(), out outValMock))
.OutCallback((string owner, out ConnectionInfo connInfo) => connInfo = isConnected ? ci : null)
LanguageService.Instance.ScriptParseInfoMap.Add(textDocument.TextDocument.Uri, new ScriptParseInfo()); .Returns(isConnected);
scriptFile = new ScriptFile {ClientFilePath = textDocument.TextDocument.Uri};
return new QueryExecutionService(connectionService.Object, workspaceService) {BufferFileStreamFactory = GetFileStreamFactory(storage)};
} }
public static ServerConnection GetServerConnection(ConnectionInfo connection) public static WorkspaceService<SqlToolsSettings> GetPrimedWorkspaceService(string query)
{
string connectionString = ConnectionService.BuildConnectionString(connection.ConnectionDetails);
var sqlConnection = new SqlConnection(connectionString);
return new ServerConnection(sqlConnection);
}
public static async Task<QueryExecutionService> GetPrimedExecutionService(ISqlConnectionFactory factory, bool isConnected, WorkspaceService<SqlToolsSettings> workspaceService)
{
var connectionService = new ConnectionService(factory);
if (isConnected)
{
await connectionService.Connect(new ConnectParams
{
Connection = StandardConnectionDetails,
OwnerUri = OwnerUri
});
}
return new QueryExecutionService(connectionService, workspaceService) {BufferFileStreamFactory = GetFileStreamFactory()};
}
public static WorkspaceService<SqlToolsSettings> GetPrimedWorkspaceService()
{ {
// Set up file for returning the query // Set up file for returning the query
var fileMock = new Mock<ScriptFile>(); var fileMock = new Mock<ScriptFile>();
fileMock.SetupGet(file => file.Contents).Returns(StandardQuery); fileMock.SetupGet(file => file.Contents).Returns(query);
// Set up workspace mock // Set up workspace mock
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>(); var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();

View File

@@ -17,7 +17,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
private static void VerifyReadWrite<T>(int valueLength, T value, Func<ServiceBufferFileStreamWriter, T, int> writeFunc, Func<ServiceBufferFileStreamReader, FileStreamReadResult> readFunc) private static void VerifyReadWrite<T>(int valueLength, T value, Func<ServiceBufferFileStreamWriter, T, int> writeFunc, Func<ServiceBufferFileStreamReader, FileStreamReadResult> readFunc)
{ {
// Setup: Create a mock file stream wrapper // Setup: Create a mock file stream wrapper
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(); Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
try try
{ {
// If: // If:
@@ -223,7 +223,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
public void StringNullTest() public void StringNullTest()
{ {
// Setup: Create a mock file stream wrapper // Setup: Create a mock file stream wrapper
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(); Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
// If: // If:
// ... I write null as a string to the writer // ... I write null as a string to the writer
@@ -259,7 +259,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
public void BytesNullTest() public void BytesNullTest()
{ {
// Setup: Create a mock file stream wrapper // Setup: Create a mock file stream wrapper
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(); Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
// If: // If:
// ... I write null as a string to the writer // ... I write null as a string to the writer

View File

@@ -47,7 +47,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
.Returns(fileMock.Object); .Returns(fileMock.Object);
// If: // If:
// ... I request a query (doesn't matter what kind) // ... I request a query (doesn't matter what kind)
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri}; var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
var executeRequest = RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null); var executeRequest = RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
@@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
var disposeRequest = GetQueryDisposeResultContextMock(qdr => { var disposeRequest = GetQueryDisposeResultContextMock(qdr => {
result = qdr; result = qdr;
}, null); }, null);
queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object).Wait(); await queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object);
// Then: // Then:
// ... I should have seen a successful result // ... I should have seen a successful result
@@ -75,11 +75,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>(); var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
// If: // If:
// ... I attempt to dispose a query that doesn't exist // ... I attempt to dispose a query that doesn't exist
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, false, false, workspaceService.Object);
var disposeParams = new QueryDisposeParams {OwnerUri = Common.OwnerUri}; var disposeParams = new QueryDisposeParams {OwnerUri = Common.OwnerUri};
QueryDisposeResult result = null; QueryDisposeResult result = null;
var disposeRequest = GetQueryDisposeResultContextMock(qdr => result = qdr, null); var disposeRequest = GetQueryDisposeResultContextMock(qdr => result = qdr, null);
queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object).Wait(); await queryService.HandleDisposeRequest(disposeParams, disposeRequest.Object);
// Then: // Then:
// ... I should have gotten an error result // ... I should have gotten an error result
@@ -99,8 +99,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>())) workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
.Returns(fileMock.Object); .Returns(fileMock.Object);
// ... We need a query service // ... We need a query service
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService.Object);
workspaceService.Object);
// If: // If:
// ... I execute some bogus query // ... I execute some bogus query

View File

@@ -5,839 +5,17 @@
//#define USE_LIVE_CONNECTION //#define USE_LIVE_CONNECTION
using System;
using System.Data.Common; using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; 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 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 namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{ {
public class ExecuteTests public class ExecuteTests
{ {
#region Batch Class Tests
[Fact]
public void BatchCreationTest()
{
// If I create a new batch...
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
// Then:
// ... The text of the batch should be stored
Assert.NotEmpty(batch.BatchText);
// ... It should not have executed and no error
Assert.False(batch.HasExecuted, "The query should not have executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The results should be empty
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
Assert.Empty(batch.ResultMessages);
// ... The start line of the batch should be 0
Assert.Equal(0, batch.Selection.StartLine);
// ... It's ordinal ID should be what I set it to
Assert.Equal(Common.Ordinal, batch.Id);
}
[Fact]
public void BatchExecuteNoResultSets()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get no result sets
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
batch.BatchCompletion += callback;
batch.Execute(GetConnection(Common.CreateTestConnectionInfo(null, false)), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The query should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The results should be empty
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
// ... The results should not be null
Assert.NotNull(batch.ResultSets);
Assert.NotNull(batch.ResultSummaries);
// ... There should be a message for how many rows were affected
Assert.Equal(1, batch.ResultMessages.Count());
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteOneResultSet()
{
const int resultSets = 1;
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get one result set
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... There should be exactly one result set
Assert.Equal(resultSets, batch.ResultSets.Count());
Assert.Equal(resultSets, batch.ResultSummaries.Length);
// ... Inside the result set should be with 5 rows
Assert.Equal(Common.StandardRows, batch.ResultSets.First().RowCount);
Assert.Equal(Common.StandardRows, batch.ResultSummaries[0].RowCount);
// ... Inside the result set should have 5 columns
Assert.Equal(Common.StandardColumns, batch.ResultSets.First().Columns.Length);
Assert.Equal(Common.StandardColumns, batch.ResultSummaries[0].ColumnInfo.Length);
// ... There should be a message for how many rows were affected
Assert.Equal(resultSets, batch.ResultMessages.Count());
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteTwoResultSets()
{
var dataset = new[] { Common.StandardTestData, Common.StandardTestData };
int resultSets = dataset.Length;
ConnectionInfo ci = Common.CreateTestConnectionInfo(dataset, false);
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get two result sets
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... There should be exactly two result sets
Assert.Equal(resultSets, batch.ResultSets.Count());
foreach (ResultSet rs in batch.ResultSets)
{
// ... Each result set should have 5 rows
Assert.Equal(Common.StandardRows, rs.RowCount);
// ... Inside each result set should be 5 columns
Assert.Equal(Common.StandardColumns, rs.Columns.Length);
}
// ... There should be exactly two result set summaries
Assert.Equal(resultSets, batch.ResultSummaries.Length);
foreach (ResultSetSummary rs in batch.ResultSummaries)
{
// ... Inside each result summary, there should be 5 rows
Assert.Equal(Common.StandardRows, rs.RowCount);
// ... Inside each result summary, there should be 5 column definitions
Assert.Equal(Common.StandardColumns, rs.ColumnInfo.Length);
}
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteInvalidQuery()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
// If I execute a batch that is invalid
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed with error
Assert.True(batch.HasExecuted);
Assert.True(batch.HasError);
// ... There should be no result sets
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
// ... There should be plenty of messages for the error
Assert.NotEmpty(batch.ResultMessages);
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public async Task BatchExecuteExecuted()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
// If I execute a batch
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory());
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
// If I execute it again
// Then:
// ... It should throw an invalid operation exception
await Assert.ThrowsAsync<InvalidOperationException>(() =>
batch.Execute(GetConnection(ci), CancellationToken.None));
// ... The data should still be available without error
Assert.False(batch.HasError, "The batch should not be in an error condition");
Assert.True(batch.HasExecuted, "The batch should still be marked executed.");
Assert.NotEmpty(batch.ResultSets);
Assert.NotEmpty(batch.ResultSummaries);
}
[Theory]
[InlineData("")]
[InlineData(null)]
public void BatchExecuteNoSql(string query)
{
// If:
// ... I create a batch that has an empty query
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentException>(() => new Batch(query, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory()));
}
[Fact]
public void BatchNoBufferFactory()
{
// If:
// ... I create a batch that has no file stream factory
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() => new Batch("stuff", Common.SubsectionDocument, Common.Ordinal, null));
}
[Fact]
public void BatchInvalidOrdinal()
{
// If:
// ... I create a batch has has an ordinal less than 0
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentOutOfRangeException>(() => new Batch("stuff", Common.SubsectionDocument, -1, Common.GetFileStreamFactory()));
}
#endregion
#region Query Class Tests
[Fact]
public void QueryExecuteNoQueryText()
{
// If:
// ... I create a query that has a null query text
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentException>(() =>
new Query(null, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), Common.GetFileStreamFactory()));
}
[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, new QueryExecutionSettings(), Common.GetFileStreamFactory()));
}
[Fact]
public void QueryExecuteNoSettings()
{
// If:
// ... I create a query that has a null settings
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), null, Common.GetFileStreamFactory()));
}
[Fact]
public void QueryExecuteNoBufferFactory()
{
// If:
// ... I create a query that has a null file stream factory
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(),null));
}
[Fact]
public void QueryExecuteSingleBatch()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from a single batch (without separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
query.BatchCompleted += batchCallback;
// Then:
// ... I should get a single batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 1 time
Assert.Equal(1, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteNoOpBatch()
{
// Setup:
// ... Create a callback for batch completion
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
throw new Exception("Batch completion callback was called");
};
// If:
// ... I create a query from a single batch that does nothing
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
query.BatchCompleted += batchCallback;
// Then:
// ... I should get no batches back
Assert.NotEmpty(query.QueryText);
Assert.Empty(query.Batches);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I Then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with no batch summaries returned
Assert.True(query.HasExecuted);
Assert.Empty(query.BatchSummaries);
}
[Fact]
public void QueryExecuteMultipleBatches()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery);
Query query = new Query(queryText, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back two batches to execute that haven't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(2, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with two batch summaries returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(2, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 2 times
Assert.Equal(2, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteMultipleBatchesWithNoOp()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from a two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{1}", Common.StandardQuery, Common.NoOpQuery);
Query query = new Query(queryText, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back one batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// .. I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 1 time
Assert.Equal(1, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteInvalidBatch()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from an invalid batch
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back a query with one batch not executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... There should be an error on the batch
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
Assert.True(query.BatchSummaries[0].HasError);
Assert.NotEmpty(query.BatchSummaries[0].Messages);
// ... The batch callback should have been called once
Assert.Equal(1, batchCallbacksReceived);
}
#endregion
#region Service Tests
[Fact]
public async void QueryExecuteValidNoResultsTest()
{
// Given:
// ... Default settings are stored in the workspace service
WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings = new SqlToolsSettings();
// 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 to execute a valid query with no results
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
var queryParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
QueryExecuteBatchCompleteParams batchCompleteParams = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p);
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
// Then:
// ... No Errors should have been sent
// ... A successful result should have been sent with messages on the first batch
// ... A completion event should have been fired with empty results
// ... A batch completion event should have been fired with empty results
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
Assert.Empty(completeParams.BatchSummaries[0].ResultSetSummaries);
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.NotNull(batchCompleteParams);
Assert.Empty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
Assert.Equal(completeParams.OwnerUri, batchCompleteParams.OwnerUri);
// ... There should be one active query
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteValidResultsTest()
{
// 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 to execute a valid query with results
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true,
workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
QueryExecuteBatchCompleteParams batchCompleteParams = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p);
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
// Then:
// ... No errors should have been sent
// ... A successful result should have been sent with messages
// ... A completion event should have been fired with one result
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
Assert.NotEmpty(completeParams.BatchSummaries[0].ResultSetSummaries);
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.False(completeParams.BatchSummaries[0].HasError);
Assert.NotNull(batchCompleteParams);
Assert.NotEmpty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
Assert.Equal(completeParams.OwnerUri, batchCompleteParams.OwnerUri);
// ... There should be one active query
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteUnconnectedUriTest()
{
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
// If:
// ... I request to execute a query using a file URI that isn't connected
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = "notConnected", QuerySelection = Common.WholeDocument };
object error = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(e => error = e);
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
// Then:
// ... An error should have been returned
// ... No result should have been returned
// ... No completion event should have been fired
// ... There should be no active queries
VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.IsType<string>(error);
Assert.NotEmpty((string)error);
Assert.Empty(queryService.ActiveQueries);
}
[Fact]
public async void QueryExecuteInProgressTest()
{
// 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 to execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(null);
await 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
object error = null;
var secondRequestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(e => error = e);
await AwaitExecution(queryService, queryParams, secondRequestContext.Object);
// Then:
// ... An error should have been sent
// ... A result should have not have been sent
// ... No completion event should have been fired
// ... There should only be one active query
VerifyQueryExecuteCallCount(secondRequestContext, Times.Never(), Times.AtMostOnce(), Times.AtMostOnce(), Times.Once());
Assert.IsType<string>(error);
Assert.NotEmpty((string)error);
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteCompletedTest()
{
// 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 to execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(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;
QueryExecuteBatchCompleteParams batchComplete = null;
var secondRequestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
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.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.False(complete.BatchSummaries.Any(b => b.HasError));
Assert.Equal(1, queryService.ActiveQueries.Count);
Assert.NotNull(batchComplete);
Assert.False(batchComplete.BatchSummary.HasError);
Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
}
[Fact]
public async Task QueryExecuteMissingSelectionTest()
{
// Set up file for returning the query
var fileMock = new Mock<ScriptFile>();
fileMock.SetupGet(file => file.Contents).Returns("");
// 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 to execute a query with a missing query string
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = null };
object errorResult = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(error => errorResult = error);
await queryService.HandleExecuteRequest(queryParams, requestContext.Object);
// Then:
// ... Am error should have been sent
// ... No result should have been sent
// ... No completion event should have been fired
// ... An active query should not have been added
VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.NotNull(errorResult);
Assert.IsType<string>(errorResult);
Assert.DoesNotContain(Common.OwnerUri, queryService.ActiveQueries.Keys);
// ... There should not be an active query
Assert.Empty(queryService.ActiveQueries);
}
[Fact]
public async void QueryExecuteInvalidQueryTest()
{
// 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 to execute a query that is invalid
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, true), true, workspaceService.Object);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
QueryExecuteResult result = null;
QueryExecuteCompleteParams complete = null;
QueryExecuteBatchCompleteParams batchComplete = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
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.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, complete.BatchSummaries.Length);
Assert.True(complete.BatchSummaries[0].HasError);
Assert.NotEmpty(complete.BatchSummaries[0].Messages);
Assert.NotNull(batchComplete);
Assert.True(batchComplete.BatchSummary.HasError);
Assert.NotEmpty(batchComplete.BatchSummary.Messages);
Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
}
#if USE_LIVE_CONNECTION #if USE_LIVE_CONNECTION
[Fact] [Fact]
@@ -866,32 +44,5 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
Assert.NotEmpty(query.BatchSummaries[0].Messages); Assert.NotEmpty(query.BatchSummaries[0].Messages);
} }
#endif #endif
#endregion
private static void VerifyQueryExecuteCallCount(Mock<RequestContext<QueryExecuteResult>> mock, Times sendResultCalls,
Times sendCompletionEventCalls, Times sendBatchCompletionEvent, 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>()), sendCompletionEventCalls);
mock.Verify(rc => rc.SendEvent(
It.Is<EventType<QueryExecuteBatchCompleteParams>>(m => m == QueryExecuteBatchCompleteEvent.Type),
It.IsAny<QueryExecuteBatchCompleteParams>()), sendBatchCompletionEvent);
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
}
private static DbConnection GetConnection(ConnectionInfo info)
{
return info.Factory.CreateSqlConnection(ConnectionService.BuildConnectionString(info.ConnectionDetails));
}
private static async Task AwaitExecution(QueryExecutionService service, QueryExecuteParams qeParams,
RequestContext<QueryExecuteResult> requestContext)
{
await service.HandleExecuteRequest(qeParams, requestContext);
await service.ActiveQueries[qeParams.OwnerUri].ExecutionTask;
}
} }
} }

View File

@@ -0,0 +1,310 @@
//
// 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.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
{
public class BatchTests
{
[Fact]
public void BatchCreationTest()
{
// If I create a new batch...
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory(null));
// Then:
// ... The text of the batch should be stored
Assert.NotEmpty(batch.BatchText);
// ... It should not have executed and no error
Assert.False(batch.HasExecuted, "The query should not have executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The results should be empty
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
Assert.Empty(batch.ResultMessages);
// ... The start line of the batch should be 0
Assert.Equal(0, batch.Selection.StartLine);
// ... It's ordinal ID should be what I set it to
Assert.Equal(Common.Ordinal, batch.Id);
}
[Fact]
public void BatchExecuteNoResultSets()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get no result sets
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
batch.BatchCompletion += callback;
batch.Execute(GetConnection(Common.CreateTestConnectionInfo(null, false)), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The query should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The results should be empty
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
// ... The results should not be null
Assert.NotNull(batch.ResultSets);
Assert.NotNull(batch.ResultSummaries);
// ... There should be a message for how many rows were affected
Assert.Equal(1, batch.ResultMessages.Count());
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteOneResultSet()
{
const int resultSets = 1;
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get one result set
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... There should be exactly one result set
Assert.Equal(resultSets, batch.ResultSets.Count());
Assert.Equal(resultSets, batch.ResultSummaries.Length);
// ... Inside the result set should be with 5 rows
Assert.Equal(Common.StandardRows, batch.ResultSets.First().RowCount);
Assert.Equal(Common.StandardRows, batch.ResultSummaries[0].RowCount);
// ... Inside the result set should have 5 columns
Assert.Equal(Common.StandardColumns, batch.ResultSets.First().Columns.Length);
Assert.Equal(Common.StandardColumns, batch.ResultSummaries[0].ColumnInfo.Length);
// ... There should be a message for how many rows were affected
Assert.Equal(resultSets, batch.ResultMessages.Count());
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteTwoResultSets()
{
var dataset = new[] { Common.StandardTestData, Common.StandardTestData };
int resultSets = dataset.Length;
ConnectionInfo ci = Common.CreateTestConnectionInfo(dataset, false);
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
// If I execute a query that should get two result sets
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... There should be exactly two result sets
Assert.Equal(resultSets, batch.ResultSets.Count());
foreach (ResultSet rs in batch.ResultSets)
{
// ... Each result set should have 5 rows
Assert.Equal(Common.StandardRows, rs.RowCount);
// ... Inside each result set should be 5 columns
Assert.Equal(Common.StandardColumns, rs.Columns.Length);
}
// ... There should be exactly two result set summaries
Assert.Equal(resultSets, batch.ResultSummaries.Length);
foreach (ResultSetSummary rs in batch.ResultSummaries)
{
// ... Inside each result summary, there should be 5 rows
Assert.Equal(Common.StandardRows, rs.RowCount);
// ... Inside each result summary, there should be 5 column definitions
Assert.Equal(Common.StandardColumns, rs.ColumnInfo.Length);
}
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public void BatchExecuteInvalidQuery()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
// If I execute a batch that is invalid
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed with error
Assert.True(batch.HasExecuted);
Assert.True(batch.HasError);
// ... There should be no result sets
Assert.Empty(batch.ResultSets);
Assert.Empty(batch.ResultSummaries);
// ... There should be plenty of messages for the error
Assert.NotEmpty(batch.ResultMessages);
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
}
[Fact]
public async Task BatchExecuteExecuted()
{
// Setup: Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
bool completionCallbackFired = false;
Batch.BatchAsyncEventHandler callback = b =>
{
completionCallbackFired = true;
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
};
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
// If I execute a batch
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
batch.BatchCompletion += callback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
// Then:
// ... It should have executed without error
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
// ... The callback for batch completion should have been fired
Assert.True(completionCallbackFired);
Assert.NotNull(batchSummaryFromCallback);
// If I execute it again
// Then:
// ... It should throw an invalid operation exception
await Assert.ThrowsAsync<InvalidOperationException>(() =>
batch.Execute(GetConnection(ci), CancellationToken.None));
// ... The data should still be available without error
Assert.False(batch.HasError, "The batch should not be in an error condition");
Assert.True(batch.HasExecuted, "The batch should still be marked executed.");
Assert.NotEmpty(batch.ResultSets);
Assert.NotEmpty(batch.ResultSummaries);
}
[Theory]
[InlineData("")]
[InlineData(null)]
public void BatchExecuteNoSql(string query)
{
// If:
// ... I create a batch that has an empty query
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentException>(() => new Batch(query, Common.SubsectionDocument, Common.Ordinal, Common.GetFileStreamFactory(null)));
}
[Fact]
public void BatchNoBufferFactory()
{
// If:
// ... I create a batch that has no file stream factory
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() => new Batch("stuff", Common.SubsectionDocument, Common.Ordinal, null));
}
[Fact]
public void BatchInvalidOrdinal()
{
// If:
// ... I create a batch has has an ordinal less than 0
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentOutOfRangeException>(() => new Batch("stuff", Common.SubsectionDocument, -1, Common.GetFileStreamFactory(null)));
}
private static DbConnection GetConnection(ConnectionInfo info)
{
return info.Factory.CreateSqlConnection(ConnectionService.BuildConnectionString(info.ConnectionDetails));
}
}
}

View File

@@ -0,0 +1,208 @@
//
// 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.Generic;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
{
public class QueryTests
{
[Fact]
public void QueryExecuteNoQueryText()
{
// If:
// ... I create a query that has a null query text
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentException>(() =>
new Query(null, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), Common.GetFileStreamFactory(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, new QueryExecutionSettings(), Common.GetFileStreamFactory(null)));
}
[Fact]
public void QueryExecuteNoSettings()
{
// If:
// ... I create a query that has a null settings
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), null, Common.GetFileStreamFactory(null)));
}
[Fact]
public void QueryExecuteNoBufferFactory()
{
// If:
// ... I create a query that has a null file stream factory
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), null));
}
[Fact]
public void QueryExecuteSingleBatch()
{
// If:
// ... I create a query from a single batch (without separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
// Then:
// ... I should get a single batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
}
[Fact]
public void QueryExecuteNoOpBatch()
{
// If:
// ... I create a query from a single batch that does nothing
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
// Then:
// ... I should get no batches back
Assert.NotEmpty(query.QueryText);
Assert.Empty(query.Batches);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I Then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with no batch summaries returned
Assert.True(query.HasExecuted);
Assert.Empty(query.BatchSummaries);
}
[Fact]
public void QueryExecuteMultipleBatches()
{
// If:
// ... I create a query from two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
// Then:
// ... I should get back two batches to execute that haven't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(2, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with two batch summaries returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(2, query.BatchSummaries.Length);
}
[Fact]
public void QueryExecuteMultipleBatchesWithNoOp()
{
// If:
// ... I create a query from a two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{1}", Common.StandardQuery, Common.NoOpQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
// Then:
// ... I should get back one batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// .. I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
}
[Fact]
public void QueryExecuteInvalidBatch()
{
// If:
// ... I create a query from an invalid batch
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
// Then:
// ... I should get back a query with one batch not executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... There should be an error on the batch
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
Assert.True(query.BatchSummaries[0].HasError);
Assert.NotEmpty(query.BatchSummaries[0].Messages);
}
}
}

View File

@@ -0,0 +1,175 @@
//
// 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.Generic;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
{
public class ResultSetTests
{
[Fact]
public void ResultCreation()
{
// If:
// ... I create a new result set with a valid db data reader
DbDataReader mockReader = GetReader(null, false, string.Empty);
ResultSet resultSet = new ResultSet(mockReader, Common.GetFileStreamFactory(new Dictionary<string, byte[]>()));
// Then:
// ... There should not be any data read yet
Assert.Null(resultSet.Columns);
Assert.Equal(0, resultSet.RowCount);
}
[Fact]
public void ResultCreationInvalidReader()
{
// If:
// ... I create a new result set without a reader
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() => new ResultSet(null, null));
}
[Fact]
public async Task ReadToEndSuccess()
{
// If:
// ... I create a new resultset with a valid db data reader that has data
// ... and I read it to the end
DbDataReader mockReader = GetReader(new [] {Common.StandardTestData}, false, Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
ResultSet resultSet = new ResultSet(mockReader, fileStreamFactory);
await resultSet.ReadResultToEnd(CancellationToken.None);
// Then:
// ... The columns should be set
// ... There should be rows to read back
Assert.NotNull(resultSet.Columns);
Assert.NotEmpty(resultSet.Columns);
Assert.Equal(Common.StandardRows, resultSet.RowCount);
}
[Theory]
[InlineData("JSON")]
[InlineData("XML")]
public async Task ReadToEndForXmlJson(string forType)
{
// Setup:
// ... Build a FOR XML or FOR JSON data set
string columnName = string.Format("{0}_F52E2B61-18A1-11d1-B105-00805F49916B", forType);
List<Dictionary<string, string>> data = new List<Dictionary<string, string>>();
for(int i = 0; i < Common.StandardRows; i++)
{
data.Add(new Dictionary<string, string> { { columnName, "test data"} });
}
Dictionary<string, string>[][] dataSets = {data.ToArray()};
// If:
// ... I create a new resultset with a valid db data reader that is FOR XML/JSON
// ... and I read it to the end
DbDataReader mockReader = GetReader(dataSets, false, Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
ResultSet resultSet = new ResultSet(mockReader, fileStreamFactory);
await resultSet.ReadResultToEnd(CancellationToken.None);
// Then:
// ... There should only be one column
// ... There should only be one row
// ... The result should be marked as complete
Assert.Equal(1, resultSet.Columns.Length);
Assert.Equal(1, resultSet.RowCount);
// If:
// ... I attempt to read back the results
// Then:
// ... I should only get one row
var subset = await resultSet.GetSubset(0, 10);
Assert.Equal(1, subset.RowCount);
}
[Fact]
public async Task GetSubsetWithoutExecution()
{
// If:
// ... I create a new result set with a valid db data reader without executing it
DbDataReader mockReader = GetReader(null, false, string.Empty);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
ResultSet resultSet = new ResultSet(mockReader, fileStreamFactory);
// Then:
// ... Attempting to read a subset should fail miserably
await Assert.ThrowsAsync<InvalidOperationException>(() => resultSet.GetSubset(0, 0));
}
[Theory]
[InlineData(-1, 0)] // Too small start row
[InlineData(20, 0)] // Too large start row
[InlineData(0, -1)] // Negative row count
public async Task GetSubsetInvalidParameters(int startRow, int rowCount)
{
// If:
// ... I create a new result set with a valid db data reader
// ... And execute the result
DbDataReader mockReader = GetReader(new[] {Common.StandardTestData}, false, Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
ResultSet resultSet = new ResultSet(mockReader, fileStreamFactory);
await resultSet.ReadResultToEnd(CancellationToken.None);
// ... And attempt to get a subset with invalid parameters
// Then:
// ... It should throw an exception for an invalid parameter
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => resultSet.GetSubset(startRow, rowCount));
}
[Theory]
[InlineData(0, 3)] // Standard scenario, 3 rows should come back
[InlineData(0, 20)] // Asking for too many rows, 5 rows should come back
[InlineData(1, 3)] // Standard scenario from non-zero start
[InlineData(1, 20)] // Asking for too many rows at a non-zero start
public async Task GetSubsetSuccess(int startRow, int rowCount)
{
// If:
// ... I create a new result set with a valid db data reader
// ... And execute the result set
DbDataReader mockReader = GetReader(new[] { Common.StandardTestData }, false, Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
ResultSet resultSet = new ResultSet(mockReader, fileStreamFactory);
await resultSet.ReadResultToEnd(CancellationToken.None);
// ... And attempt to get a subset with valid number of rows
ResultSetSubset subset = await resultSet.GetSubset(startRow, rowCount);
// Then:
// ... There should be rows in the subset, either the number of rows or the number of
// rows requested or the number of rows in the result set, whichever is lower
long availableRowsFromStart = resultSet.RowCount - startRow;
Assert.Equal(Math.Min(availableRowsFromStart, rowCount), subset.RowCount);
// ... The rows should have the same number of columns as the resultset
Assert.Equal(resultSet.Columns.Length, subset.Rows[0].Length);
}
private static DbDataReader GetReader(Dictionary<string, string>[][] dataSet, bool throwOnRead, string query)
{
var info = Common.CreateTestConnectionInfo(dataSet, throwOnRead);
var connection = info.Factory.CreateSqlConnection(ConnectionService.BuildConnectionString(info.ConnectionDetails));
var command = connection.CreateCommand();
command.CommandText = query;
return command.ExecuteReader();
}
}
}

View File

@@ -0,0 +1,294 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
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
{
[Fact]
public async void QueryExecuteValidNoResultsTest()
{
// Given:
// ... Default settings are stored in the workspace service
// ... A workspace with a standard query is configured
WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings = new SqlToolsSettings();
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a valid query with no results
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
QueryExecuteBatchCompleteParams batchCompleteParams = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
// Then:
// ... No Errors should have been sent
// ... A successful result should have been sent with messages on the first batch
// ... A completion event should have been fired with empty results
// ... A batch completion event should have been fired with empty results
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
Assert.Empty(completeParams.BatchSummaries[0].ResultSetSummaries);
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.NotNull(batchCompleteParams);
Assert.Empty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
Assert.Equal(completeParams.OwnerUri, batchCompleteParams.OwnerUri);
// ... There should be one active query
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteValidResultsTest()
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a valid query with results
var queryService = Common.GetPrimedExecutionService(new[] { Common.StandardTestData }, true, false, workspaceService);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
QueryExecuteBatchCompleteParams batchCompleteParams = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
// Then:
// ... No errors should have been sent
// ... A successful result should have been sent with messages
// ... A completion event should have been fired with one result
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
Assert.NotEmpty(completeParams.BatchSummaries[0].ResultSetSummaries);
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.False(completeParams.BatchSummaries[0].HasError);
Assert.NotNull(batchCompleteParams);
Assert.NotEmpty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
Assert.Equal(completeParams.OwnerUri, batchCompleteParams.OwnerUri);
// ... There should be one active query
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteUnconnectedUriTest()
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a query using a file URI that isn't connected
var queryService = Common.GetPrimedExecutionService(null, false, false, workspaceService);
var queryParams = new QueryExecuteParams { OwnerUri = "notConnected", QuerySelection = Common.WholeDocument };
object error = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(e => error = e);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
// Then:
// ... An error should have been returned
// ... No result should have been returned
// ... No completion event should have been fired
// ... There should be no active queries
VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.IsType<string>(error);
Assert.NotEmpty((string)error);
Assert.Empty(queryService.ActiveQueries);
}
[Fact]
public async void QueryExecuteInProgressTest()
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a query
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(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
object error = null;
var secondRequestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(e => error = e);
await Common.AwaitExecution(queryService, queryParams, secondRequestContext.Object);
// Then:
// ... An error should have been sent
// ... A result should have not have been sent
// ... No completion event should have been fired
// ... There should only be one active query
VerifyQueryExecuteCallCount(secondRequestContext, Times.Never(), Times.AtMostOnce(), Times.AtMostOnce(), Times.Once());
Assert.IsType<string>(error);
Assert.NotEmpty((string)error);
Assert.Equal(1, queryService.ActiveQueries.Count);
}
[Fact]
public async void QueryExecuteCompletedTest()
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a query
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
// Note, we don't care about the results of the first request
var firstRequestContext = RequestContextMocks.Create<QueryExecuteResult>(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;
QueryExecuteBatchCompleteParams batchComplete = null;
var secondRequestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
await Common.AwaitExecution(queryService, queryParams, secondRequestContext.Object);
// 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.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.False(complete.BatchSummaries.Any(b => b.HasError));
Assert.Equal(1, queryService.ActiveQueries.Count);
Assert.NotNull(batchComplete);
Assert.False(batchComplete.BatchSummary.HasError);
Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
}
[Theory]
[InlineData(null)]
public async Task QueryExecuteMissingSelectionTest(SelectionData selection)
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(string.Empty);
// 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 };
object errorResult = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(null)
.AddErrorHandling(error => errorResult = error);
await queryService.HandleExecuteRequest(queryParams, requestContext.Object);
// Then:
// ... Am error should have been sent
// ... No result should have been sent
// ... No completion event should have been fired
// ... An active query should not have been added
VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.NotNull(errorResult);
Assert.IsType<string>(errorResult);
Assert.DoesNotContain(Common.OwnerUri, queryService.ActiveQueries.Keys);
// ... There should not be an active query
Assert.Empty(queryService.ActiveQueries);
}
[Fact]
public async void QueryExecuteInvalidQueryTest()
{
// Given:
// ... A workspace with a standard query is configured
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
// If:
// ... I request to execute a query that is invalid
var queryService = Common.GetPrimedExecutionService(null, true, true, workspaceService);
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
QueryExecuteResult result = null;
QueryExecuteCompleteParams complete = null;
QueryExecuteBatchCompleteParams batchComplete = null;
var requestContext = RequestContextMocks.Create<QueryExecuteResult>(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
// 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.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, complete.BatchSummaries.Length);
Assert.True(complete.BatchSummaries[0].HasError);
Assert.NotEmpty(complete.BatchSummaries[0].Messages);
Assert.NotNull(batchComplete);
Assert.True(batchComplete.BatchSummary.HasError);
Assert.NotEmpty(batchComplete.BatchSummary.Messages);
Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
}
private static void VerifyQueryExecuteCallCount(Mock<RequestContext<QueryExecuteResult>> mock, Times sendResultCalls,
Times sendCompletionEventCalls, Times sendBatchCompletionEvent, 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>()), sendCompletionEventCalls);
mock.Verify(rc => rc.SendEvent(
It.Is<EventType<QueryExecuteBatchCompleteParams>>(m => m == QueryExecuteBatchCompleteEvent.Type),
It.IsAny<QueryExecuteBatchCompleteParams>()), sendBatchCompletionEvent);
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
}
}
}

View File

@@ -3,16 +3,13 @@
// //
using System; using System;
using System.Linq;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; 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 Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Utility; using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Moq; using Moq;
using Xunit; using Xunit;
@@ -30,11 +27,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsCsvSuccessTest() public async void SaveResultsAsCsvSuccessTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as csv with correct parameters // Request to save the results as csv with correct parameters
var saveParams = new SaveResultsAsCsvRequestParams var saveParams = new SaveResultsAsCsvRequestParams
@@ -47,18 +44,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
// Call save results and wait on the save task // Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex]; ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath); await selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask;
// Expect to see a file successfully created in filepath and a success message // Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages); Assert.Null(result.Messages);
Assert.True(File.Exists(saveParams.FilePath)); Assert.True(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
// Delete temp file after test // Delete temp file after test
if (File.Exists(saveParams.FilePath)) if (File.Exists(saveParams.FilePath))
@@ -74,11 +69,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsCsvWithSelectionSuccessTest() public async void SaveResultsAsCsvWithSelectionSuccessTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as csv with correct parameters // Request to save the results as csv with correct parameters
var saveParams = new SaveResultsAsCsvRequestParams var saveParams = new SaveResultsAsCsvRequestParams
@@ -95,7 +90,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
// Call save results and wait on the save task // Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
@@ -104,9 +98,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
await saveTask; await saveTask;
// Expect to see a file successfully created in filepath and a success message // Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages); Assert.Null(result.Messages);
Assert.True(File.Exists(saveParams.FilePath)); Assert.True(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
// Delete temp file after test // Delete temp file after test
if (File.Exists(saveParams.FilePath)) if (File.Exists(saveParams.FilePath))
@@ -120,13 +114,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
/// </summary> /// </summary>
[Fact] [Fact]
public async void SaveResultsAsCsvExceptionTest() public async void SaveResultsAsCsvExceptionTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as csv with incorrect filepath // Request to save the results as csv with incorrect filepath
var saveParams = new SaveResultsAsCsvRequestParams var saveParams = new SaveResultsAsCsvRequestParams
@@ -139,17 +133,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
SaveResultRequestError errMessage = null; SaveResultRequestError errMessage = null;
var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (SaveResultRequestError) err); 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 // Call save results and wait on the save task
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex]; ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath); await selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask;
// Expect to see error message // Expect to see error message
Assert.NotNull(errMessage);
VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once()); VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
Assert.NotNull(errMessage);
Assert.False(File.Exists(saveParams.FilePath)); Assert.False(File.Exists(saveParams.FilePath));
} }
@@ -160,8 +152,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsCsvQueryNotFoundTest() public async void SaveResultsAsCsvQueryNotFoundTest()
{ {
// Create a query execution service // Create a query execution service
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>(); var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
// Request to save the results as csv with query that is no longer active // Request to save the results as csv with query that is no longer active
var saveParams = new SaveResultsAsCsvRequestParams var saveParams = new SaveResultsAsCsvRequestParams
@@ -173,12 +165,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object).Wait(); await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
// Expect message that save failed // Expect message that save failed
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.NotNull(result.Messages); Assert.NotNull(result.Messages);
Assert.False(File.Exists(saveParams.FilePath)); Assert.False(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
} }
/// <summary> /// <summary>
@@ -188,11 +180,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsJsonSuccessTest() public async void SaveResultsAsJsonSuccessTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as json with correct parameters // Request to save the results as json with correct parameters
var saveParams = new SaveResultsAsJsonRequestParams var saveParams = new SaveResultsAsJsonRequestParams
@@ -200,19 +192,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
OwnerUri = Common.OwnerUri, OwnerUri = Common.OwnerUri,
ResultSetIndex = 0, ResultSetIndex = 0,
BatchIndex = 0, BatchIndex = 0,
FilePath = "testwrite_4.json" FilePath = "testwrite_4.json"
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
// Call save results and wait on the save task // Call save results and wait on the save task
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex]; ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath); Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask; await saveTask;
// Expect to see a file successfully created in filepath and a success message // Expect to see a file successfully created in filepath and a success message
Assert.Null(result.Messages); Assert.Null(result.Messages);
@@ -233,11 +222,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsJsonWithSelectionSuccessTest() public async void SaveResultsAsJsonWithSelectionSuccessTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as json with correct parameters // Request to save the results as json with correct parameters
var saveParams = new SaveResultsAsJsonRequestParams var saveParams = new SaveResultsAsJsonRequestParams
@@ -253,18 +242,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
// Call save results and wait on the save task // Call save results and wait on the save task
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex]; ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath); await selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask;
// Expect to see a file successfully created in filepath and a success message // Expect to see a file successfully created in filepath and a success message
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
Assert.Null(result.Messages); Assert.Null(result.Messages);
Assert.True(File.Exists(saveParams.FilePath)); Assert.True(File.Exists(saveParams.FilePath));
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
// Delete temp file after test // Delete temp file after test
if (File.Exists(saveParams.FilePath)) if (File.Exists(saveParams.FilePath))
@@ -280,11 +267,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
public async void SaveResultsAsJsonExceptionTest() public async void SaveResultsAsJsonExceptionTest()
{ {
// Execute a query // Execute a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); 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 executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
await queryService.ActiveQueries[Common.OwnerUri].ExecutionTask;
// Request to save the results as json with incorrect filepath // Request to save the results as json with incorrect filepath
var saveParams = new SaveResultsAsJsonRequestParams var saveParams = new SaveResultsAsJsonRequestParams
@@ -303,8 +290,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
// Call save results and wait on the save task // Call save results and wait on the save task
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object); await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex]; ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath); await selectedResultSet.GetSaveTask(saveParams.FilePath);
await saveTask;
// Expect to see error message // Expect to see error message
Assert.NotNull(errMessage); Assert.NotNull(errMessage);
@@ -318,10 +304,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
[Fact] [Fact]
public async void SaveResultsAsJsonQueryNotFoundTest() public async void SaveResultsAsJsonQueryNotFoundTest()
{ {
// Create a query service // Create a query service
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>(); var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
// Request to save the results as json with query that is no longer active // Request to save the results as json with query that is no longer active
var saveParams = new SaveResultsAsJsonRequestParams var saveParams = new SaveResultsAsJsonRequestParams
@@ -333,7 +318,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}; };
SaveResultRequestResult result = null; SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null); var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object).Wait(); await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
// Expect message that save failed // Expect message that save failed
Assert.Equal("Failed to save results, ID not found.", result.Messages); Assert.Equal("Failed to save results, ID not found.", result.Messages);
@@ -353,25 +338,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
Action<SaveResultRequestResult> resultCallback, Action<SaveResultRequestResult> resultCallback,
Action<object> errorCallback) Action<object> errorCallback)
{ {
var requestContext = new Mock<RequestContext<SaveResultRequestResult>>(); var requestContext = RequestContextMocks.Create(resultCallback)
.AddErrorHandling(errorCallback);
// Setup the mock for SendResult
var sendResultFlow = requestContext
.Setup(rc => rc.SendResult(It.IsAny<SaveResultRequestResult> ()))
.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; return requestContext;
} }

View File

@@ -9,10 +9,7 @@ using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; 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 Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Utility; using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Moq; using Moq;
using Xunit; using Xunit;
@@ -100,7 +97,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{ {
// If: // If:
// ... I have a batch that hasn't completed execution // ... I have a batch that hasn't completed execution
Batch b = new Batch(Common.StandardQuery, Common.WholeDocument, Common.Ordinal, Common.GetFileStreamFactory()); Batch b = new Batch(Common.StandardQuery, Common.WholeDocument, Common.Ordinal, Common.GetFileStreamFactory(null));
Assert.False(b.HasExecuted); Assert.False(b.HasExecuted);
// ... And I ask for a subset // ... And I ask for a subset
@@ -135,19 +132,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
[Fact] [Fact]
public async Task SubsetServiceValidTest() public async Task SubsetServiceValidTest()
{ {
// 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: // If:
// ... I have a query that has results (doesn't matter what) // ... I have a query that has results (doesn't matter what)
var queryService = await Common.GetPrimedExecutionService( var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
Common.CreateMockFactory(new[] {Common.StandardTestData}, false), true, var queryService = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false, workspaceService);
workspaceService.Object);
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri}; var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
@@ -171,15 +159,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
[Fact] [Fact]
public async void SubsetServiceMissingQueryTest() public async void SubsetServiceMissingQueryTest()
{ {
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
// If: // If:
// ... I ask for a set of results for a file that hasn't executed a query // ... I ask for a set of results for a file that hasn't executed a query
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object); var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 }; var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
QueryExecuteSubsetResult result = null; QueryExecuteSubsetResult result = null;
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null); var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object).Wait(); await queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object);
// Then: // Then:
// ... I should have an error result // ... I should have an error result
@@ -193,19 +180,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
[Fact] [Fact]
public async void SubsetServiceUnexecutedQueryTest() public async void SubsetServiceUnexecutedQueryTest()
{ {
// 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: // If:
// ... I have a query that hasn't finished executing (doesn't matter what) // ... I have a query that hasn't finished executing (doesn't matter what)
var queryService = await Common.GetPrimedExecutionService( var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true, var queryService = Common.GetPrimedExecutionService(new[] { Common.StandardTestData }, true, false, workspaceService);
workspaceService.Object);
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri }; var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
@@ -232,8 +210,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
{ {
// If: // If:
// ... I have a query that doesn't have any result sets // ... I have a query that doesn't have any result sets
var queryService = await Common.GetPrimedExecutionService( var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
Common.CreateMockFactory(null, false), true, Common.GetPrimedWorkspaceService()); var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri }; var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null); var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object); await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);

View File

@@ -0,0 +1,42 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Reflection;
using Moq.Language;
using Moq.Language.Flow;
namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
{
public static class MoqExtensions
{
public delegate void OutAction<TOut>(out TOut outVal);
public delegate void OutAction<in T1, TOut>(T1 arg1, out TOut outVal);
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(
this ICallback<TMock, TReturn> mock, OutAction<TOut> action) where TMock : class
{
return OutCallbackInternal(mock, action);
}
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(
this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action) where TMock : class
{
return OutCallbackInternal(mock, action);
}
private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(
ICallback<TMock, TReturn> mock, object action) where TMock : class
{
typeof(ICallback<TMock, TReturn>).GetTypeInfo()
.Assembly.GetType("Moq.MethodCall")
.GetMethod("SetCallbackWithArguments",
BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(mock, new[] { action });
return mock as IReturnsThrows<TMock, TReturn>;
}
}
}

View File

@@ -8,10 +8,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
{ {
public class TestDbColumn : DbColumn public class TestDbColumn : DbColumn
{ {
public TestDbColumn() public TestDbColumn(string columnName)
{ {
base.IsLong = false; base.IsLong = false;
base.ColumnName = "Test Column"; base.ColumnName = columnName;
base.ColumnSize = 128; base.ColumnSize = 128;
base.AllowDBNull = true; base.AllowDBNull = true;
base.DataType = typeof(string); base.DataType = typeof(string);

View File

@@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Data.Common; using System.Data.Common;
using System.Linq; using System.Linq;
using Moq;
namespace Microsoft.SqlTools.ServiceLayer.Test.Utility namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
{ {
@@ -93,7 +92,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
List<DbColumn> columns = new List<DbColumn>(); List<DbColumn> columns = new List<DbColumn>();
for (int i = 0; i < ResultSet.Current[0].Count; i++) for (int i = 0; i < ResultSet.Current[0].Count; i++)
{ {
columns.Add(new TestDbColumn()); columns.Add(new TestDbColumn(ResultSet.Current[0].Keys.ToArray()[i]));
} }
return new ReadOnlyCollection<DbColumn>(columns); return new ReadOnlyCollection<DbColumn>(columns);
} }