using System; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Moq; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution { public class ExecuteTests { #region Query Class Tests [Fact] public void QueryCreationTest() { // If I create a new query... Query query = new Query("NO OP", Common.CreateTestConnectionInfo(null, false)); // Then: // ... It should not have executed Assert.False(query.HasExecuted, "The query should not have executed."); // ... The results should be empty Assert.Empty(query.ResultSets); Assert.Empty(query.ResultSummary); } [Fact] public void QueryExecuteNoResultSets() { // If I execute a query that should get no result sets Query query = new Query("Query with no result sets", Common.CreateTestConnectionInfo(null, false)); query.Execute().Wait(); // Then: // ... It should have executed Assert.True(query.HasExecuted, "The query should have been marked executed."); // ... The results should be empty Assert.Empty(query.ResultSets); Assert.Empty(query.ResultSummary); // ... The results should not be null Assert.NotNull(query.ResultSets); Assert.NotNull(query.ResultSummary); } [Fact] public void QueryExecuteQueryOneResultSet() { ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] {Common.StandardTestData}, false); // If I execute a query that should get one result set int resultSets = 1; int rows = 5; int columns = 4; Query query = new Query("Query with one result sets", ci); query.Execute().Wait(); // Then: // ... It should have executed Assert.True(query.HasExecuted, "The query should have been marked executed."); // ... There should be exactly one result set Assert.Equal(resultSets, query.ResultSets.Count); // ... Inside the result set should be with 5 rows Assert.Equal(rows, query.ResultSets[0].Rows.Count); // ... Inside the result set should have 5 columns and 5 column definitions Assert.Equal(columns, query.ResultSets[0].Rows[0].Length); Assert.Equal(columns, query.ResultSets[0].Columns.Length); // ... There should be exactly one result set summary Assert.Equal(resultSets, query.ResultSummary.Length); // ... Inside the result summary, there should be 5 column definitions Assert.Equal(columns, query.ResultSummary[0].ColumnInfo.Length); // ... Inside the result summary, there should be 5 rows Assert.Equal(rows, query.ResultSummary[0].RowCount); } [Fact] public void QueryExecuteQueryTwoResultSets() { var dataset = new[] {Common.StandardTestData, Common.StandardTestData}; int resultSets = dataset.Length; int rows = Common.StandardTestData.Length; int columns = Common.StandardTestData[0].Count; ConnectionInfo ci = Common.CreateTestConnectionInfo(dataset, false); // If I execute a query that should get two result sets Query query = new Query("Query with two result sets", ci); query.Execute().Wait(); // Then: // ... It should have executed Assert.True(query.HasExecuted, "The query should have been marked executed."); // ... There should be exactly two result sets Assert.Equal(resultSets, query.ResultSets.Count); foreach (ResultSet rs in query.ResultSets) { // ... Each result set should have 5 rows Assert.Equal(rows, rs.Rows.Count); // ... Inside each result set should be 5 columns and 5 column definitions Assert.Equal(columns, rs.Rows[0].Length); Assert.Equal(columns, rs.Columns.Length); } // ... There should be exactly two result set summaries Assert.Equal(resultSets, query.ResultSummary.Length); foreach (ResultSetSummary rs in query.ResultSummary) { // ... Inside each result summary, there should be 5 column definitions Assert.Equal(columns, rs.ColumnInfo.Length); // ... Inside each result summary, there should be 5 rows Assert.Equal(rows, rs.RowCount); } } [Fact] public void QueryExecuteInvalidQuery() { ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true); // 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(() => query.Execute().Wait()); } [Fact] public void QueryExecuteExecutedQuery() { ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] {Common.StandardTestData}, false); // 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(() => query.Execute().Wait()); Assert.Equal(1, ae.InnerExceptions.Count); Assert.IsType(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); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void QueryExecuteNoQuery(string query) { // If: // ... I create a query that has an empty query // Then: // ... It should throw an exception Assert.Throws(() => new Query(query, null)); } [Fact] public void QueryExecuteNoConnectionInfo() { // If: // ... I create a query that has a null connection info // Then: // ... It should throw an exception Assert.Throws(() => new Query("Some Query", null)); } #endregion #region Service Tests [Fact] public void QueryExecuteValidNoResultsTest() { // If: // ... I request to execute a valid query with no results var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true); var queryParams = new QueryExecuteParams { QueryText = "Doesn't Matter", OwnerUri = Common.OwnerUri }; QueryExecuteResult result = null; QueryExecuteCompleteParams completeParams = null; var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null); queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait(); // Then: // ... No Errors should have been sent // ... A successful result should have been sent with no messages // ... A completion event should have been fired with empty results // ... There should be one active query VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never()); Assert.Null(result.Messages); Assert.Empty(completeParams.Messages); Assert.Empty(completeParams.ResultSetSummaries); Assert.False(completeParams.Error); Assert.Equal(1, queryService.ActiveQueries.Count); } [Fact] public void QueryExecuteValidResultsTest() { // If: // ... I request to execute a valid query with results var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true); var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Doesn't Matter" }; QueryExecuteResult result = null; QueryExecuteCompleteParams completeParams = null; var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, cp) => completeParams = cp, null); queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait(); // Then: // ... No errors should have been sent // ... A successful result should have been sent with no messages // ... A completion event should have been fired with one result // ... There should be one active query VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never()); Assert.Null(result.Messages); Assert.Empty(completeParams.Messages); Assert.NotEmpty(completeParams.ResultSetSummaries); Assert.False(completeParams.Error); Assert.Equal(1, queryService.ActiveQueries.Count); } [Fact] public void QueryExecuteUnconnectedUriTest() { // If: // ... I request to execute a query using a file URI that isn't connected var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false); var queryParams = new QueryExecuteParams { OwnerUri = "notConnected", QueryText = "Doesn't Matter" }; QueryExecuteResult result = null; var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null); queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait(); // Then: // ... An error message should have been returned via the result // ... No completion event should have been fired // ... No error event should have been fired // ... There should be no active queries VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Never(), Times.Never()); Assert.NotNull(result.Messages); Assert.NotEmpty(result.Messages); Assert.Empty(queryService.ActiveQueries); } [Fact] public void QueryExecuteInProgressTest() { // If: // ... I request to execute a query var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true); var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Some Query" }; // Note, we don't care about the results of the first request var firstRequestContext = Common.GetQueryExecuteResultContextMock(null, null, null); queryService.HandleExecuteRequest(queryParams, firstRequestContext.Object).Wait(); // ... And then I request another query without waiting for the first to complete queryService.ActiveQueries[Common.OwnerUri].HasExecuted = false; // Simulate query hasn't finished QueryExecuteResult result = null; var secondRequestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null); queryService.HandleExecuteRequest(queryParams, secondRequestContext.Object).Wait(); // Then: // ... No errors should have been sent // ... A result should have been sent with an error message // ... No completion event should have been fired // ... There should only be one active query VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.AtMostOnce(), Times.Never()); Assert.NotNull(result.Messages); Assert.NotEmpty(result.Messages); Assert.Equal(1, queryService.ActiveQueries.Count); } [Fact] public void QueryExecuteCompletedTest() { // If: // ... I request to execute a query var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true); var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Some Query" }; // Note, we don't care about the results of the first request var firstRequestContext = Common.GetQueryExecuteResultContextMock(null, null, null); queryService.HandleExecuteRequest(queryParams, firstRequestContext.Object).Wait(); // ... And then I request another query after waiting for the first to complete QueryExecuteResult result = null; QueryExecuteCompleteParams complete = null; var secondRequestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, qecp) => complete = qecp, null); queryService.HandleExecuteRequest(queryParams, secondRequestContext.Object).Wait(); // Then: // ... No errors should have been sent // ... A result should have been sent with no errors // ... There should only be one active query VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.Once(), Times.Never()); Assert.Null(result.Messages); Assert.False(complete.Error); Assert.Equal(1, queryService.ActiveQueries.Count); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void QueryExecuteMissingQueryTest(string query) { // If: // ... I request to execute a query with a missing query string var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true); var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = query }; QueryExecuteResult result = null; var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, null, null); queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait(); // Then: // ... No errors should have been sent // ... A result should have been sent with an error message // ... No completion event should have been fired VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Never(), Times.Never()); Assert.NotNull(result.Messages); Assert.NotEmpty(result.Messages); } [Fact] public void QueryExecuteInvalidQueryTest() { // If: // ... I request to execute a query that is invalid var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, true), true); var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QueryText = "Bad query!" }; QueryExecuteResult result = null; QueryExecuteCompleteParams complete = null; var requestContext = Common.GetQueryExecuteResultContextMock(qer => result = qer, (et, qecp) => complete = qecp, null); queryService.HandleExecuteRequest(queryParams, requestContext.Object).Wait(); // Then: // ... No errors should have been sent // ... A result should have been sent with success (we successfully started the query) // ... A completion event should have been sent with error VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Never()); Assert.Null(result.Messages); Assert.True(complete.Error); Assert.NotEmpty(complete.Messages); } #endregion private void VerifyQueryExecuteCallCount(Mock> mock, Times sendResultCalls, Times sendEventCalls, Times sendErrorCalls) { mock.Verify(rc => rc.SendResult(It.IsAny()), sendResultCalls); mock.Verify(rc => rc.SendEvent( It.Is>(m => m == QueryExecuteCompleteEvent.Type), It.IsAny()), sendEventCalls); mock.Verify(rc => rc.SendError(It.IsAny()), sendErrorCalls); } } }