Isolate Shared Test Code (#252)

The goal of this make sure that test code is correctly organized to ensure that test suites aren't dependent on each other.
* UnitTests get their own project now (renaming Microsoft.SqlTools.ServiceLayer.Test to Microsoft.SqlTools.ServiceLayer.UnitTests) which is about 90% of the changes to the files.
* IntegrationTests no longer depends on UnitTests, only Test.Common
* Any shared components from TestObjects that spins up a "live" connection has been moved to IntegrationTests Utility/LiveConnectionHelper.cs
* The dictionary-based mock file stream factory has been moved to Test.Common since it is used by UnitTests and IntegrationTests
    * Added a overload that doesn't take a dictionary for when we don't care about monitoring the storage (about 90% of the time)
* The RunIf* wrapper methods have been moved to Test.Common
* OwnerUri and StandardQuery constants have been moved to Test.Common Constants file

* Updating to latest SDK version available at https://www.microsoft.com/net/core#windowscmd

* Moving unit tests to unit test folder

* Changing namespaces to UnitTests

* Moving some constants and shared functionality into common project, making the UnitTests reference it

* Unit tests are working!

* Integration tests are working

* Updating automated test runs

* Fixing one last broken unit test

* Exposing internals for other projects

* Moving edit data tests to UnitTest project

* Applying refactor fixes to unit tests

* Fixing flaky test that wasn't awaiting completion
This commit is contained in:
Benjamin Russell
2017-03-02 13:00:31 -08:00
committed by GitHub
parent f9abe5f0bd
commit 1166778249
110 changed files with 700 additions and 764 deletions

View File

@@ -0,0 +1,28 @@
//
// 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 Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults
{
public class BatchTests
{
[Theory]
[InlineData(-1)]
[InlineData(100)]
public void SaveAsFailsOutOfRangeResultSet(int resultSetIndex)
{
// If: I attempt to save results for an invalid result set index
// Then: I should get an ArgumentOutOfRange exception
Batch batch = Common.GetBasicExecutedBatch();
SaveResultsRequestParams saveParams = new SaveResultsRequestParams {ResultSetIndex = resultSetIndex};
Assert.Throws<ArgumentOutOfRangeException>(() =>
batch.SaveAs(saveParams, null, null, null));
}
}
}

View File

@@ -0,0 +1,28 @@
//
// 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 Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults
{
public class QueryTests
{
[Theory]
[InlineData(-1)]
[InlineData(100)]
public void SaveAsFailsOutOfRangeBatch(int batchIndex)
{
// If: I save a basic query's results with out of range batch index
// Then: I should get an out of range exception
Query query = Common.GetBasicExecutedQuery();
SaveResultsRequestParams saveParams = new SaveResultsRequestParams {BatchIndex = batchIndex};
Assert.Throws<ArgumentOutOfRangeException>(() =>
query.SaveAs(saveParams, null, null, null));
}
}
}

View File

@@ -0,0 +1,186 @@
//
// 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 Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults
{
public class ResultSetTests
{
[Fact]
public void SaveAsNullParams()
{
// If: I attempt to save with a null set of params
// Then: I should get a null argument exception
ResultSet rs = new ResultSet(
GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal,
MemoryFileSystem.GetFileStreamFactory());
Assert.Throws<ArgumentNullException>(() => rs.SaveAs(
null,
MemoryFileSystem.GetFileStreamFactory(),
null, null));
}
[Fact]
public void SaveAsNullFactory()
{
// If: I attempt to save with a null set of params
// Then: I should get a null argument exception
ResultSet rs = new ResultSet(
GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal,
MemoryFileSystem.GetFileStreamFactory());
Assert.Throws<ArgumentNullException>(() => rs.SaveAs(
new SaveResultsRequestParams(),
null, null, null));
}
[Fact]
public void SaveAsFailedIncomplete()
{
// If: I attempt to save a result set that hasn't completed execution
// Then: I should get an invalid operation exception
ResultSet rs = new ResultSet(
GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal,
MemoryFileSystem.GetFileStreamFactory());
Assert.Throws<InvalidOperationException>(() => rs.SaveAs(
new SaveResultsRequestParams(),
MemoryFileSystem.GetFileStreamFactory(),
null, null));
}
[Fact]
public void SaveAsFailedExistingTaskInProgress()
{
// Setup:
// ... Create a result set that has been executed
ResultSet rs = new ResultSet(
GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery),
Common.Ordinal, Common.Ordinal,
MemoryFileSystem.GetFileStreamFactory());
// ... Insert a non-started task into the save as tasks
rs.SaveTasks.AddOrUpdate(Constants.OwnerUri, new Task(() => { }), (s, t) => null);
// If: I attempt to save results with the same name as the non-completed task
// Then: I should get an invalid operation exception
var requestParams = new SaveResultsRequestParams {FilePath = Constants.OwnerUri};
Assert.Throws<InvalidOperationException>(() => rs.SaveAs(
requestParams, GetMockFactory(GetMockWriter().Object, null),
null, null));
}
[Fact]
public async Task SaveAsWithoutRowSelection()
{
// Setup:
// ... Create a mock reader/writer for reading the result
IFileStreamFactory resultFactory = MemoryFileSystem.GetFileStreamFactory();
// ... Create a result set with dummy data and read to the end
ResultSet rs = new ResultSet(
GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery),
Common.Ordinal, Common.Ordinal,
resultFactory);
await rs.ReadResultToEnd(CancellationToken.None);
// ... Create a mock writer for writing the save as file
Mock<IFileStreamWriter> saveWriter = GetMockWriter();
IFileStreamFactory saveFactory = GetMockFactory(saveWriter.Object, resultFactory.GetReader);
// If: I attempt to save results and await completion
rs.SaveAs(new SaveResultsRequestParams {FilePath = Constants.OwnerUri}, saveFactory, null, null);
Assert.True(rs.SaveTasks.ContainsKey(Constants.OwnerUri));
await rs.SaveTasks[Constants.OwnerUri];
// Then:
// ... The task should have completed successfully
Assert.Equal(TaskStatus.RanToCompletion, rs.SaveTasks[Constants.OwnerUri].Status);
// ... All the rows should have been written successfully
saveWriter.Verify(
w => w.WriteRow(It.IsAny<IList<DbCellValue>>(), It.IsAny<IList<DbColumnWrapper>>()),
Times.Exactly(Common.StandardRows));
}
[Fact]
public async Task SaveAsWithRowSelection()
{
// Setup:
// ... Create a mock reader/writer for reading the result
IFileStreamFactory resultFactory = MemoryFileSystem.GetFileStreamFactory();
// ... Create a result set with dummy data and read to the end
ResultSet rs = new ResultSet(
GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery),
Common.Ordinal, Common.Ordinal,
resultFactory);
await rs.ReadResultToEnd(CancellationToken.None);
// ... Create a mock writer for writing the save as file
Mock<IFileStreamWriter> saveWriter = GetMockWriter();
IFileStreamFactory saveFactory = GetMockFactory(saveWriter.Object, resultFactory.GetReader);
// If: I attempt to save results that has a selection made
var saveParams = new SaveResultsRequestParams
{
FilePath = Constants.OwnerUri,
RowStartIndex = 1,
RowEndIndex = Common.StandardRows - 2,
ColumnStartIndex = 0, // Column start/end doesn't matter, but are required to be
ColumnEndIndex = 10 // considered a "save selection"
};
rs.SaveAs(saveParams, saveFactory, null, null);
Assert.True(rs.SaveTasks.ContainsKey(Constants.OwnerUri));
await rs.SaveTasks[Constants.OwnerUri];
// Then:
// ... The task should have completed successfully
Assert.Equal(TaskStatus.RanToCompletion, rs.SaveTasks[Constants.OwnerUri].Status);
// ... All the rows should have been written successfully
saveWriter.Verify(
w => w.WriteRow(It.IsAny<IList<DbCellValue>>(), It.IsAny<IList<DbColumnWrapper>>()),
Times.Exactly(Common.StandardRows - 2));
}
private static Mock<IFileStreamWriter> GetMockWriter()
{
var mockWriter = new Mock<IFileStreamWriter>();
mockWriter.Setup(w => w.WriteRow(It.IsAny<IList<DbCellValue>>(), It.IsAny<IList<DbColumnWrapper>>()));
return mockWriter;
}
private static IFileStreamFactory GetMockFactory(IFileStreamWriter writer, Func<string, IFileStreamReader> readerGenerator)
{
var mockFactory = new Mock<IFileStreamFactory>();
mockFactory.Setup(f => f.GetWriter(It.IsAny<string>()))
.Returns(writer);
mockFactory.Setup(f => f.GetReader(It.IsAny<string>()))
.Returns(readerGenerator);
return mockFactory.Object;
}
private static DbDataReader GetReader(TestResultSet[] 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,303 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults
{
public class ServiceIntegrationTests
{
#region CSV Tests
[Fact]
public async Task SaveResultsCsvNonExistentQuery()
{
// Given: A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(null);
QueryExecutionService qes = Common.GetPrimedExecutionService(null, false, false, ws);
// If: I attempt to save a result set from a query that doesn't exist
SaveResultsAsCsvRequestParams saveParams = new SaveResultsAsCsvRequestParams
{
OwnerUri = Constants.OwnerUri // Won't exist because nothing has executed
};
var evf = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardErrorValidator()
.Complete();
await qes.HandleSaveResultsAsCsvRequest(saveParams, evf.Object);
// Then:
// ... An error event should have been fired
// ... No success event should have been fired
evf.Validate();
}
[Fact]
public async Task SaveResultAsCsvFailure()
{
// Given:
// ... A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Constants.StandardQuery);
Dictionary<string, byte[]> storage;
QueryExecutionService qes = Common.GetPrimedExecutionService(Common.ExecutionPlanTestDataSet, true, false, ws, out storage);
// ... The query execution service has executed a query with results
var executeParams = new ExecuteDocumentSelectionParams { QuerySelection = null, OwnerUri = Constants.OwnerUri };
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
await qes.ActiveQueries[Constants.OwnerUri].ExecutionTask;
// If: I attempt to save a result set and get it to throw because of invalid column selection
SaveResultsAsCsvRequestParams saveParams = new SaveResultsAsCsvRequestParams
{
BatchIndex = 0,
FilePath = "qqq",
OwnerUri = Constants.OwnerUri,
ResultSetIndex = 0,
ColumnStartIndex = -1,
ColumnEndIndex = 100,
RowStartIndex = 0,
RowEndIndex = 5
};
qes.CsvFileFactory = GetCsvStreamFactory(storage, saveParams);
var efv = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardErrorValidator()
.Complete();
await qes.HandleSaveResultsAsCsvRequest(saveParams, efv.Object);
await qes.ActiveQueries[saveParams.OwnerUri]
.Batches[saveParams.BatchIndex]
.ResultSets[saveParams.ResultSetIndex]
.SaveTasks[saveParams.FilePath];
// Then:
// ... An error event should have been fired
// ... No success event should have been fired
efv.Validate();
}
[Fact]
public async Task SaveResultsAsCsvSuccess()
{
// Given:
// ... A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Constants.StandardQuery);
Dictionary<string, byte[]> storage;
QueryExecutionService qes = Common.GetPrimedExecutionService(Common.ExecutionPlanTestDataSet, true, false, ws, out storage);
// ... The query execution service has executed a query with results
var executeParams = new ExecuteDocumentSelectionParams {QuerySelection = null, OwnerUri = Constants.OwnerUri};
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
await qes.ActiveQueries[Constants.OwnerUri].ExecutionTask;
// If: I attempt to save a result set from a query
SaveResultsAsCsvRequestParams saveParams = new SaveResultsAsCsvRequestParams
{
OwnerUri = Constants.OwnerUri,
FilePath = "qqq",
BatchIndex = 0,
ResultSetIndex = 0
};
qes.CsvFileFactory = GetCsvStreamFactory(storage, saveParams);
var efv = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardResultValidator()
.Complete();
await qes.HandleSaveResultsAsCsvRequest(saveParams, efv.Object);
await qes.ActiveQueries[saveParams.OwnerUri]
.Batches[saveParams.BatchIndex]
.ResultSets[saveParams.ResultSetIndex]
.SaveTasks[saveParams.FilePath];
// Then:
// ... I should have a successful result
// ... There should not have been an error
efv.Validate();
}
#endregion
#region JSON tests
[Fact]
public async Task SaveResultsJsonNonExistentQuery()
{
// Given: A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(null);
QueryExecutionService qes = Common.GetPrimedExecutionService(null, false, false, ws);
// If: I attempt to save a result set from a query that doesn't exist
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams
{
OwnerUri = Constants.OwnerUri // Won't exist because nothing has executed
};
var efv = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardErrorValidator()
.Complete();
await qes.HandleSaveResultsAsJsonRequest(saveParams, efv.Object);
// Then:
// ... An error event should have been fired
// ... No success event should have been fired
efv.Validate();
}
[Fact]
public async Task SaveResultAsJsonFailure()
{
// Given:
// ... A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Constants.StandardQuery);
Dictionary<string, byte[]> storage;
QueryExecutionService qes = Common.GetPrimedExecutionService(Common.StandardTestDataSet, true, false, ws, out storage);
// ... The query execution service has executed a query with results
var executeParams = new ExecuteDocumentSelectionParams { QuerySelection = null, OwnerUri = Constants.OwnerUri };
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
await qes.ActiveQueries[Constants.OwnerUri].ExecutionTask;
// If: I attempt to save a result set and get it to throw because of invalid column selection
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams
{
BatchIndex = 0,
FilePath = "qqq",
OwnerUri = Constants.OwnerUri,
ResultSetIndex = 0,
ColumnStartIndex = -1,
ColumnEndIndex = 100,
RowStartIndex = 0,
RowEndIndex = 5
};
qes.JsonFileFactory = GetJsonStreamFactory(storage, saveParams);
var efv = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardErrorValidator()
.Complete();
await qes.HandleSaveResultsAsJsonRequest(saveParams, efv.Object);
await qes.ActiveQueries[saveParams.OwnerUri]
.Batches[saveParams.BatchIndex]
.ResultSets[saveParams.ResultSetIndex]
.SaveTasks[saveParams.FilePath];
// Then:
// ... An error event should have been fired
// ... No success event should have been fired
efv.Validate();
}
[Fact]
public async Task SaveResultsAsJsonSuccess()
{
// Given:
// ... A working query and workspace service
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Constants.StandardQuery);
Dictionary<string, byte[]> storage;
QueryExecutionService qes = Common.GetPrimedExecutionService(Common.StandardTestDataSet, true, false, ws, out storage);
// ... The query execution service has executed a query with results
var executeParams = new ExecuteDocumentSelectionParams { QuerySelection = null, OwnerUri = Constants.OwnerUri };
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
await qes.ActiveQueries[Constants.OwnerUri].ExecutionTask;
// If: I attempt to save a result set from a query
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams
{
OwnerUri = Constants.OwnerUri,
FilePath = "qqq",
BatchIndex = 0,
ResultSetIndex = 0
};
qes.JsonFileFactory = GetJsonStreamFactory(storage, saveParams);
var efv = new EventFlowValidator<SaveResultRequestResult>()
.AddStandardResultValidator()
.Complete();
await qes.HandleSaveResultsAsJsonRequest(saveParams, efv.Object);
await qes.ActiveQueries[saveParams.OwnerUri]
.Batches[saveParams.BatchIndex]
.ResultSets[saveParams.ResultSetIndex]
.SaveTasks[saveParams.FilePath];
// Then:
// ... I should have a successful result
// ... There should not have been an error
efv.Validate();
}
#endregion
#region Private Helpers
private static IFileStreamFactory GetCsvStreamFactory(IDictionary<string, byte[]> storage, SaveResultsAsCsvRequestParams saveParams)
{
Mock<IFileStreamFactory> mock = new Mock<IFileStreamFactory>();
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
.Returns<string>(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output]), new QueryExecutionSettings()));
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>()))
.Returns<string>(output =>
{
storage.Add(output, new byte[8192]);
return new SaveAsCsvFileStreamWriter(new MemoryStream(storage[output]), saveParams);
});
return mock.Object;
}
private static IFileStreamFactory GetJsonStreamFactory(IDictionary<string, byte[]> storage, SaveResultsAsJsonRequestParams saveParams)
{
Mock<IFileStreamFactory> mock = new Mock<IFileStreamFactory>();
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
.Returns<string>(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output]), new QueryExecutionSettings()));
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>()))
.Returns<string>(output =>
{
storage.Add(output, new byte[8192]);
return new SaveAsJsonFileStreamWriter(new MemoryStream(storage[output]), saveParams);
});
return mock.Object;
}
#endregion
}
public static class SaveResultEventFlowValidatorExtensions
{
public static EventFlowValidator<SaveResultRequestResult> AddStandardErrorValidator(
this EventFlowValidator<SaveResultRequestResult> efv)
{
return efv.AddErrorValidation<SaveResultRequestError>(e =>
{
Assert.NotNull(e);
Assert.NotNull(e.message);
});
}
public static EventFlowValidator<SaveResultRequestResult> AddStandardResultValidator(
this EventFlowValidator<SaveResultRequestResult> efv)
{
return efv.AddResultValidation(r =>
{
Assert.NotNull(r);
Assert.Null(r.Messages);
});
}
}
}