mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Second batch of unit tests
Making slight changes to RequestContext to make it easier to mock
This commit is contained in:
@@ -20,7 +20,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
|||||||
this.messageWriter = messageWriter;
|
this.messageWriter = messageWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendResult(TResult resultDetails)
|
public RequestContext() { }
|
||||||
|
|
||||||
|
public virtual async Task SendResult(TResult resultDetails)
|
||||||
{
|
{
|
||||||
await this.messageWriter.WriteResponse<TResult>(
|
await this.messageWriter.WriteResponse<TResult>(
|
||||||
resultDetails,
|
resultDetails,
|
||||||
@@ -28,14 +30,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
|||||||
requestMessage.Id);
|
requestMessage.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
public virtual async Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
||||||
{
|
{
|
||||||
await this.messageWriter.WriteEvent(
|
await this.messageWriter.WriteEvent(
|
||||||
eventType,
|
eventType,
|
||||||
eventParams);
|
eventParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendError(object errorDetails)
|
public virtual async Task SendError(object errorDetails)
|
||||||
{
|
{
|
||||||
await this.messageWriter.WriteMessage(
|
await this.messageWriter.WriteMessage(
|
||||||
Message.ResponseError(
|
Message.ResponseError(
|
||||||
|
|||||||
@@ -19,7 +19,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
get { return instance.Value; }
|
get { return instance.Value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private QueryExecutionService() { }
|
private QueryExecutionService()
|
||||||
|
{
|
||||||
|
ConnectionService = ConnectionService.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal QueryExecutionService(ConnectionService connService)
|
||||||
|
{
|
||||||
|
ConnectionService = connService;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -33,6 +41,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
get { return queries.Value; }
|
get { return queries.Value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ConnectionService ConnectionService { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
@@ -55,12 +65,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
|
|
||||||
#region Request Handlers
|
#region Request Handlers
|
||||||
|
|
||||||
private async Task HandleExecuteRequest(QueryExecuteParams executeParams,
|
public async Task HandleExecuteRequest(QueryExecuteParams executeParams,
|
||||||
RequestContext<QueryExecuteResult> requestContext)
|
RequestContext<QueryExecuteResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to get the connection for the editor
|
// Attempt to get the connection for the editor
|
||||||
ConnectionInfo connectionInfo;
|
ConnectionInfo connectionInfo;
|
||||||
if(!ConnectionService.Instance.TryFindConnection(executeParams.OwnerUri, out connectionInfo))
|
if(!ConnectionService.TryFindConnection(executeParams.OwnerUri, out connectionInfo))
|
||||||
{
|
{
|
||||||
await requestContext.SendError("This editor is not connected to a database.");
|
await requestContext.SendError("This editor is not connected to a database.");
|
||||||
return;
|
return;
|
||||||
@@ -94,7 +104,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, eventParams);
|
await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, eventParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleResultSubsetRequest(QueryExecuteSubsetParams subsetParams,
|
public async Task HandleResultSubsetRequest(QueryExecuteSubsetParams subsetParams,
|
||||||
RequestContext<QueryExecuteSubsetResult> requestContext)
|
RequestContext<QueryExecuteSubsetResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to load the query
|
// Attempt to load the query
|
||||||
@@ -129,7 +139,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleDisposeRequest(QueryDisposeParams disposeParams,
|
public async Task HandleDisposeRequest(QueryDisposeParams disposeParams,
|
||||||
RequestContext<QueryDisposeResult> requestContext)
|
RequestContext<QueryDisposeResult> requestContext)
|
||||||
{
|
{
|
||||||
// Attempt to remove the query for the owner uri
|
// Attempt to remove the query for the owner uri
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||||
|
using Moq;
|
||||||
|
using Moq.Protected;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
|
{
|
||||||
|
public class Common
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<string, string>[] StandardTestData =
|
||||||
|
{
|
||||||
|
new Dictionary<string, string> { {"col1", "val11"}, { "col2", "val12"}, { "col3", "val13"}, { "col4", "col14"} },
|
||||||
|
new Dictionary<string, string> { {"col1", "val21"}, { "col2", "val22"}, { "col3", "val23"}, { "col4", "col24"} },
|
||||||
|
new Dictionary<string, string> { {"col1", "val31"}, { "col2", "val32"}, { "col3", "val33"}, { "col4", "col34"} },
|
||||||
|
new Dictionary<string, string> { {"col1", "val41"}, { "col2", "val42"}, { "col3", "val43"}, { "col4", "col44"} },
|
||||||
|
new Dictionary<string, string> { {"col1", "val51"}, { "col2", "val52"}, { "col3", "val53"}, { "col4", "col54"} },
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Dictionary<string, string>[] GetTestData(int columns, int rows)
|
||||||
|
{
|
||||||
|
Dictionary<string, string>[] output = new Dictionary<string, string>[rows];
|
||||||
|
for (int row = 0; row < rows; row++)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> rowDictionary = new Dictionary<string, string>();
|
||||||
|
for (int column = 0; column < columns; column++)
|
||||||
|
{
|
||||||
|
rowDictionary.Add(String.Format("column{0}", column), String.Format("val{0}{1}", column, row));
|
||||||
|
}
|
||||||
|
output[row] = rowDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Query GetBasicExecutedQuery()
|
||||||
|
{
|
||||||
|
Query query = new Query("SIMPLE QUERY", CreateTestConnectionInfo(new[] { StandardTestData }, false));
|
||||||
|
query.Execute().Wait();
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Mocking
|
||||||
|
|
||||||
|
//private static DbDataReader CreateTestReader(int columnCount, int rowCount)
|
||||||
|
//{
|
||||||
|
// var readerMock = new Mock<DbDataReader> { CallBase = true };
|
||||||
|
|
||||||
|
// // Setup for column reads
|
||||||
|
// // TODO: We can't test columns because of oddities with how datatable/GetColumn
|
||||||
|
|
||||||
|
// // Setup for row reads
|
||||||
|
// var readSequence = readerMock.SetupSequence(dbReader => dbReader.Read());
|
||||||
|
// for (int i = 0; i < rowCount; i++)
|
||||||
|
// {
|
||||||
|
// readSequence.Returns(true);
|
||||||
|
// }
|
||||||
|
// readSequence.Returns(false);
|
||||||
|
|
||||||
|
// // Make sure that if we call for data from the reader it works
|
||||||
|
// readerMock.Setup(dbReader => dbReader[InColumnRange(columnCount)])
|
||||||
|
// .Returns<object>(i => i.ToString());
|
||||||
|
// readerMock.Setup(dbReader => dbReader[NotInColumnRange(columnCount)])
|
||||||
|
// .Throws(new ArgumentOutOfRangeException());
|
||||||
|
// readerMock.Setup(dbReader => dbReader.HasRows)
|
||||||
|
// .Returns(rowCount > 0);
|
||||||
|
|
||||||
|
// return readerMock.Object;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static int InColumnRange(int columnCount)
|
||||||
|
//{
|
||||||
|
// return Match.Create<int>(i => i < columnCount && i > 0);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static int NotInColumnRange(int columnCount)
|
||||||
|
//{
|
||||||
|
// return Match.Create<int>(i => i >= columnCount || i < 0);
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static DbCommand CreateTestCommand(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||||
|
{
|
||||||
|
var commandMock = new Mock<DbCommand> { CallBase = true };
|
||||||
|
var commandMockSetup = commandMock.Protected()
|
||||||
|
.Setup<DbDataReader>("ExecuteDbDataReader", It.IsAny<CommandBehavior>());
|
||||||
|
|
||||||
|
// Setup the expected behavior
|
||||||
|
if (throwOnRead)
|
||||||
|
{
|
||||||
|
commandMockSetup.Throws(new Mock<DbException>().Object);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commandMockSetup.Returns(new TestDbDataReader(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return commandMock.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DbConnection CreateTestConnection(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||||
|
{
|
||||||
|
var connectionMock = new Mock<DbConnection> { CallBase = true };
|
||||||
|
connectionMock.Protected()
|
||||||
|
.Setup<DbCommand>("CreateDbCommand")
|
||||||
|
.Returns(CreateTestCommand(data, throwOnRead));
|
||||||
|
|
||||||
|
return connectionMock.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISqlConnectionFactory CreateMockFactory(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||||
|
{
|
||||||
|
var mockFactory = new Mock<ISqlConnectionFactory>();
|
||||||
|
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>()))
|
||||||
|
.Returns(CreateTestConnection(data, throwOnRead));
|
||||||
|
|
||||||
|
return mockFactory.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionInfo CreateTestConnectionInfo(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||||
|
{
|
||||||
|
// Create connection info
|
||||||
|
ConnectionDetails connDetails = new ConnectionDetails
|
||||||
|
{
|
||||||
|
UserName = "sa",
|
||||||
|
Password = "Yukon900",
|
||||||
|
DatabaseName = "AdventureWorks2016CTP3_2",
|
||||||
|
ServerName = "sqltools11"
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ConnectionInfo(CreateMockFactory(data, throwOnRead), "test://test", connDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Data.Common;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.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.Test.Utility;
|
|
||||||
using Moq;
|
|
||||||
using Moq.Protected;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
{
|
{
|
||||||
public class ExecuteTests
|
public class ExecuteTests
|
||||||
{
|
{
|
||||||
private static Dictionary<string, string>[] testData =
|
|
||||||
{
|
|
||||||
new Dictionary<string, string> { {"col1", "val11"}, { "col2", "val12"}, { "col3", "val13"}, { "col4", "col14"} },
|
|
||||||
new Dictionary<string, string> { {"col1", "val21"}, { "col2", "val22"}, { "col3", "val23"}, { "col4", "col24"} },
|
|
||||||
new Dictionary<string, string> { {"col1", "val31"}, { "col2", "val32"}, { "col3", "val33"}, { "col4", "col34"} },
|
|
||||||
new Dictionary<string, string> { {"col1", "val41"}, { "col2", "val42"}, { "col3", "val43"}, { "col4", "col44"} },
|
|
||||||
new Dictionary<string, string> { {"col1", "val51"}, { "col2", "val52"}, { "col3", "val53"}, { "col4", "col54"} },
|
|
||||||
};
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void QueryCreationTest()
|
public void QueryCreationTest()
|
||||||
{
|
{
|
||||||
// If I create a new query...
|
// If I create a new query...
|
||||||
Query query = new Query("NO OP", CreateTestConnectionInfo(null));
|
Query query = new Query("NO OP", Common.CreateTestConnectionInfo(null, false));
|
||||||
|
|
||||||
// Then:
|
// Then:
|
||||||
// ... It should not have executed
|
// ... It should not have executed
|
||||||
@@ -44,7 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
public void QueryExecuteNoResultSets()
|
public void QueryExecuteNoResultSets()
|
||||||
{
|
{
|
||||||
// If I execute a query that should get no result sets
|
// If I execute a query that should get no result sets
|
||||||
Query query = new Query("Query with no result sets", CreateTestConnectionInfo(null));
|
Query query = new Query("Query with no result sets", Common.CreateTestConnectionInfo(null, false));
|
||||||
query.Execute().Wait();
|
query.Execute().Wait();
|
||||||
|
|
||||||
// Then:
|
// Then:
|
||||||
@@ -63,7 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void QueryExecuteQueryOneResultSet()
|
public void QueryExecuteQueryOneResultSet()
|
||||||
{
|
{
|
||||||
ConnectionInfo ci = CreateTestConnectionInfo(new[] {testData});
|
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] {Common.StandardTestData}, false);
|
||||||
|
|
||||||
// If I execute a query that should get one result set
|
// If I execute a query that should get one result set
|
||||||
int resultSets = 1;
|
int resultSets = 1;
|
||||||
@@ -99,11 +82,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void QueryExecuteQueryTwoResultSets()
|
public void QueryExecuteQueryTwoResultSets()
|
||||||
{
|
{
|
||||||
var dataset = new[] {testData, testData};
|
var dataset = new[] {Common.StandardTestData, Common.StandardTestData};
|
||||||
int resultSets = dataset.Length;
|
int resultSets = dataset.Length;
|
||||||
int rows = testData.Length;
|
int rows = Common.StandardTestData.Length;
|
||||||
int columns = testData[0].Count;
|
int columns = Common.StandardTestData[0].Count;
|
||||||
ConnectionInfo ci = CreateTestConnectionInfo(dataset);
|
ConnectionInfo ci = Common.CreateTestConnectionInfo(dataset, false);
|
||||||
|
|
||||||
// If I execute a query that should get two result sets
|
// If I execute a query that should get two result sets
|
||||||
Query query = new Query("Query with two result sets", ci);
|
Query query = new Query("Query with two result sets", ci);
|
||||||
@@ -139,87 +122,43 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Mocking
|
[Fact]
|
||||||
|
public void QueryExecuteInvalidQuery()
|
||||||
//private static DbDataReader CreateTestReader(int columnCount, int rowCount)
|
|
||||||
//{
|
|
||||||
// var readerMock = new Mock<DbDataReader> { CallBase = true };
|
|
||||||
|
|
||||||
// // Setup for column reads
|
|
||||||
// // TODO: We can't test columns because of oddities with how datatable/GetColumn
|
|
||||||
|
|
||||||
// // Setup for row reads
|
|
||||||
// var readSequence = readerMock.SetupSequence(dbReader => dbReader.Read());
|
|
||||||
// for (int i = 0; i < rowCount; i++)
|
|
||||||
// {
|
|
||||||
// readSequence.Returns(true);
|
|
||||||
// }
|
|
||||||
// readSequence.Returns(false);
|
|
||||||
|
|
||||||
// // Make sure that if we call for data from the reader it works
|
|
||||||
// readerMock.Setup(dbReader => dbReader[InColumnRange(columnCount)])
|
|
||||||
// .Returns<object>(i => i.ToString());
|
|
||||||
// readerMock.Setup(dbReader => dbReader[NotInColumnRange(columnCount)])
|
|
||||||
// .Throws(new ArgumentOutOfRangeException());
|
|
||||||
// readerMock.Setup(dbReader => dbReader.HasRows)
|
|
||||||
// .Returns(rowCount > 0);
|
|
||||||
|
|
||||||
// return readerMock.Object;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private static int InColumnRange(int columnCount)
|
|
||||||
//{
|
|
||||||
// return Match.Create<int>(i => i < columnCount && i > 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private static int NotInColumnRange(int columnCount)
|
|
||||||
//{
|
|
||||||
// return Match.Create<int>(i => i >= columnCount || i < 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
private static DbCommand CreateTestCommand(Dictionary<string, string>[][] data)
|
|
||||||
{
|
{
|
||||||
var commandMock = new Mock<DbCommand> {CallBase = true};
|
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
|
||||||
commandMock.Protected()
|
|
||||||
.Setup<DbDataReader>("ExecuteDbDataReader", It.IsAny<CommandBehavior>())
|
|
||||||
.Returns(new TestDbDataReader(data));
|
|
||||||
|
|
||||||
return commandMock.Object;
|
// If I execute a query that is invalid
|
||||||
|
Query query = new Query("Invalid query", ci);
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an exception
|
||||||
|
Exception e = Assert.Throws<AggregateException>(() => query.Execute().Wait());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DbConnection CreateTestConnection(Dictionary<string, string>[][] data)
|
[Fact]
|
||||||
|
public void QueryExecuteExecutedQuery()
|
||||||
{
|
{
|
||||||
var connectionMock = new Mock<DbConnection> {CallBase = true};
|
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] {Common.StandardTestData}, false);
|
||||||
connectionMock.Protected()
|
|
||||||
.Setup<DbCommand>("CreateDbCommand")
|
|
||||||
.Returns(CreateTestCommand(data));
|
|
||||||
|
|
||||||
return connectionMock.Object;
|
// If I execute a query
|
||||||
|
Query query = new Query("Any query", ci);
|
||||||
|
query.Execute().Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... It should have executed
|
||||||
|
Assert.True(query.HasExecuted, "The query should have been marked executed.");
|
||||||
|
|
||||||
|
// If I execute it again
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an invalid operation exception wrapped in an aggregate exception
|
||||||
|
AggregateException ae = Assert.Throws<AggregateException>(() => query.Execute().Wait());
|
||||||
|
Assert.Equal(1, ae.InnerExceptions.Count);
|
||||||
|
Assert.IsType<InvalidOperationException>(ae.InnerExceptions[0]);
|
||||||
|
|
||||||
|
// ... The data should still be available
|
||||||
|
Assert.True(query.HasExecuted, "The query should still be marked executed.");
|
||||||
|
Assert.NotEmpty(query.ResultSets);
|
||||||
|
Assert.NotEmpty(query.ResultSummary);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ISqlConnectionFactory CreateMockFactory(Dictionary<string, string>[][] data)
|
|
||||||
{
|
|
||||||
var mockFactory = new Mock<ISqlConnectionFactory>();
|
|
||||||
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>()))
|
|
||||||
.Returns(CreateTestConnection(data));
|
|
||||||
|
|
||||||
return mockFactory.Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConnectionInfo CreateTestConnectionInfo(Dictionary<string, string>[][] data)
|
|
||||||
{
|
|
||||||
// Create connection info
|
|
||||||
ConnectionDetails connDetails = new ConnectionDetails
|
|
||||||
{
|
|
||||||
UserName = "sa",
|
|
||||||
Password = "Yukon900",
|
|
||||||
DatabaseName = "AdventureWorks2016CTP3_2",
|
|
||||||
ServerName = "sqltools11"
|
|
||||||
};
|
|
||||||
|
|
||||||
return new ConnectionInfo(CreateMockFactory(data), "test://test", connDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
|
using Microsoft.SqlTools.Test.Utility;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
|
{
|
||||||
|
public class ServiceTests
|
||||||
|
{
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteValidNoResultsTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a valid query with no results
|
||||||
|
var queryService = GetPrimedExecutionService(Common.CreateMockFactory(null, false));
|
||||||
|
var queryParams = new QueryExecuteParams
|
||||||
|
{
|
||||||
|
QueryText = "Doesn't Matter",
|
||||||
|
OwnerUri = "testFile"
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams completeParams = null;
|
||||||
|
var requestContext = GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No Errors should have been sent
|
||||||
|
// ... A successful result should have been sent with no messages
|
||||||
|
// ... A completion event should have been fired with empty results
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.Empty(completeParams.Messages);
|
||||||
|
Assert.Empty(completeParams.ResultSetSummaries);
|
||||||
|
Assert.False(completeParams.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueryExecuteValidResultsTest()
|
||||||
|
{
|
||||||
|
// If:
|
||||||
|
// ... I request to execute a valid query with results
|
||||||
|
var queryService = GetPrimedExecutionService(Common.CreateMockFactory(new [] {Common.StandardTestData}, false));
|
||||||
|
var queryParams = new QueryExecuteParams {OwnerUri = "testFile", QueryText = "Doesn't Matter"};
|
||||||
|
|
||||||
|
QueryExecuteResult result = null;
|
||||||
|
QueryExecuteCompleteParams completeParams = null;
|
||||||
|
var requestContext = GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null);
|
||||||
|
queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait();
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// ... No errors should have been send
|
||||||
|
// ... A successful result should have been sent with no messages
|
||||||
|
// ... A completion event should hvae been fired with one result
|
||||||
|
VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never());
|
||||||
|
Assert.Null(result.Messages);
|
||||||
|
Assert.Empty(completeParams.Messages);
|
||||||
|
Assert.NotEmpty(completeParams.ResultSetSummaries);
|
||||||
|
Assert.False(completeParams.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ConnectionDetails GetTestConnectionDetails()
|
||||||
|
{
|
||||||
|
return new ConnectionDetails
|
||||||
|
{
|
||||||
|
DatabaseName = "123",
|
||||||
|
Password = "456",
|
||||||
|
ServerName = "789",
|
||||||
|
UserName = "012"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private QueryExecutionService GetPrimedExecutionService(ISqlConnectionFactory factory)
|
||||||
|
{
|
||||||
|
var connectionService = new ConnectionService(factory);
|
||||||
|
connectionService.Connect(new ConnectParams {Connection = GetTestConnectionDetails(), OwnerUri = "testFile"});
|
||||||
|
return new QueryExecutionService(connectionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mock<RequestContext<QueryExecuteResult>> GetQueryExecuteResultContextMock(
|
||||||
|
Action<QueryExecuteResult> resultCallback,
|
||||||
|
Action<EventType<QueryExecuteCompleteParams>, QueryExecuteCompleteParams> eventCallback,
|
||||||
|
Action<object> errorCallback)
|
||||||
|
{
|
||||||
|
var requestContext = new Mock<RequestContext<QueryExecuteResult>>();
|
||||||
|
|
||||||
|
// Setup the mock for SendResult
|
||||||
|
var sendResultFlow = requestContext
|
||||||
|
.Setup(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()))
|
||||||
|
.Returns(Task.FromResult(0));
|
||||||
|
if (resultCallback != null)
|
||||||
|
{
|
||||||
|
sendResultFlow.Callback(resultCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the mock for SendEvent
|
||||||
|
var sendEventFlow = requestContext.Setup(rc => rc.SendEvent(
|
||||||
|
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
||||||
|
It.IsAny<QueryExecuteCompleteParams>()))
|
||||||
|
.Returns(Task.FromResult(0));
|
||||||
|
if (eventCallback != null)
|
||||||
|
{
|
||||||
|
sendEventFlow.Callback(eventCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the mock for SendError
|
||||||
|
var sendErrorFlow = requestContext.Setup(rc => rc.SendError(It.IsAny<object>()))
|
||||||
|
.Returns(Task.FromResult(0));
|
||||||
|
if (errorCallback != null)
|
||||||
|
{
|
||||||
|
sendErrorFlow.Callback(errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VerifyQueryExecuteCallCount(Mock<RequestContext<QueryExecuteResult>> mock, Times sendResultCalls, Times sendEventCalls, Times sendErrorCalls)
|
||||||
|
{
|
||||||
|
mock.Verify(rc => rc.SendResult(It.IsAny<QueryExecuteResult>()), sendResultCalls);
|
||||||
|
mock.Verify(rc => rc.SendEvent(
|
||||||
|
It.Is<EventType<QueryExecuteCompleteParams>>(m => m == QueryExecuteCompleteEvent.Type),
|
||||||
|
It.IsAny<QueryExecuteCompleteParams>()), sendEventCalls);
|
||||||
|
mock.Verify(rc => rc.SendError(It.IsAny<object>()), sendErrorCalls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,57 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
using System.Linq;
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||||
using System.Threading.Tasks;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||||
{
|
{
|
||||||
public class SubsetTests
|
public class SubsetTests
|
||||||
{
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(2)]
|
||||||
|
[InlineData(20)]
|
||||||
|
public void SubsetValidTest(int rowCount)
|
||||||
|
{
|
||||||
|
// If I have an executed query
|
||||||
|
Query q = Common.GetBasicExecutedQuery();
|
||||||
|
|
||||||
|
// ... And I ask for a subset with valid arguments
|
||||||
|
ResultSetSubset subset = q.GetSubset(0, 0, rowCount);
|
||||||
|
|
||||||
|
// Then:
|
||||||
|
// I should get the requested number of rows
|
||||||
|
Assert.Equal(Math.Min(rowCount, Common.StandardTestData.Length), subset.RowCount);
|
||||||
|
Assert.Equal(Math.Min(rowCount, Common.StandardTestData.Length), subset.Rows.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubsetUnexecutedQueryTest()
|
||||||
|
{
|
||||||
|
// If I have a query that has *not* been executed
|
||||||
|
Query q = new Query("NO OP", Common.CreateTestConnectionInfo(null, false));
|
||||||
|
|
||||||
|
// ... And I ask for a subset with valid arguments
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an exception
|
||||||
|
Assert.Throws<InvalidOperationException>(() => q.GetSubset(0, 0, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(-1, 0, 2)] // Invalid result set, too low
|
||||||
|
[InlineData(2, 0, 2)] // Invalid result set, too high
|
||||||
|
[InlineData(0, -1, 2)] // Invalid start index, too low
|
||||||
|
[InlineData(0, 10, 2)] // Invalid start index, too high
|
||||||
|
[InlineData(0, 0, -1)] // Invalid row count, too low
|
||||||
|
[InlineData(0, 0, 0)] // Invalid row count, zero
|
||||||
|
public void SubsetInvalidParamsTest(int resultSetIndex, int rowStartInex, int rowCount)
|
||||||
|
{
|
||||||
|
// If I have an executed query
|
||||||
|
Query q = Common.GetBasicExecutedQuery();
|
||||||
|
|
||||||
|
// ... And I ask for a subset with an invalid result set index
|
||||||
|
// Then:
|
||||||
|
// ... It should throw an exception
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => q.GetSubset(resultSetIndex, rowStartInex, rowCount));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
|
|||||||
|
|
||||||
private IEnumerator<Dictionary<string, string>> Rows { get; set; }
|
private IEnumerator<Dictionary<string, string>> Rows { get; set; }
|
||||||
|
|
||||||
private const string tableNameTestCommand = "SELECT name FROM sys.tables";
|
|
||||||
|
|
||||||
private List<Dictionary<string, string>> tableNamesTest = new List<Dictionary<string, string>>
|
|
||||||
{
|
|
||||||
new Dictionary<string, string> { {"name", "table1"} },
|
|
||||||
new Dictionary<string, string> { {"name", "table2"} }
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestDbDataReader(Dictionary<string, string>[][] data)
|
public TestDbDataReader(Dictionary<string, string>[][] data)
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
|
|||||||
Reference in New Issue
Block a user