mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-17 01:25:40 -05:00
Move Save As to ResultSet (#181)
It's an overhaul of the Save As mechanism to utilize the file reader/writer classes to better align with the patterns laid out by the rest of the query execution. Why make this change? This change makes our code base more uniform and adherent to the patterns/paradigms we've set up. This change also helps with the encapsulation of the classes to "separate the concerns" of each component of the save as function.
* Replumbing the save as execution to pass the call down the query stack as QueryExecutionService->Query->Batch->ResultSet
* Each layer performs it's own parameter checking
* QueryExecutionService checks if the query exists
* Query checks if the batch exists
* Batch checks if the result set exists
* ResultSet checks if the row counts are valid and if the result set has been executed
* Success/Failure delegates are passed down the chain as well
* Determination of whether a save request is a "selection" moved to the SaveResultsRequest class to eliminate duplication of code and creation of utility classes
* Making the IFileStream* classes more generic
* Removing the requirements of max characters to store from the GetWriter method, and moving it into the constructor for the temporary buffer writer - the values have been moved to the settings and given defaults
* Removing the individual type writers from IFileStreamWriter
* Removing the individual type writers from IFIleStreamReader
* Adding a new overload for WriteRow to IFileStreamWriter that will write out data, given a row's worth of data and the list of columns
* Creating a new IFileStreamFactory that creates a reader/writer pair for reading from the temporary files and writing to CSV files
* Creating a new IFileStreamFactory that creates a reader/writer pair for reading from the temporary files and writing to JSON files
* Dramatically simplified the CSV encoding functionality
* Removed duplicated logic for saving in different types and condensed down to a single chain that only differs based on what type of factory is provided
* Removing the logic for managing the list of save as tasks, since the ResultSet now performs the actual saving work, there's no real need to expose the internals of the ResultSet
* Adding new strings to the sr.strings file for save as error messages
* Completely rewriting the unit tests for the save as mechanism. Very fine grained unit tests now that should cover majority of cases (aside from race conditions)
* Refactoring maxchars params into settings and out of file stream factory
* Removing write*/read* methods from file stream readers/writers
* Migrating the CSV save as to the resultset
* Tweaks to unit testing to eliminate writing files to disk
* WIP, moving to a base class for save results writers
* Everything is wired up and compiles
* Adding unit tests for CSV encoding
* Adding unit tests for CSV and Json writers
* Adding tests to the result set for saving
* Refactor to throw exceptions on errors instead of calling failure handler
* Unit tests for batch/query argument in range
* Unit tests
* Adding service integration unit tests
* Final polish, copyright notices, etc
* Adding NULL logic
* Fixing issue of unicode to utf8
* Fixing issues as per @kburtram code review comments
* Adding files that got broken?
This commit is contained in:
@@ -123,9 +123,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
});
|
||||
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
|
||||
.Returns<string>(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output])));
|
||||
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns<string, int, int>((output, chars, xml) => new ServiceBufferFileStreamWriter(
|
||||
new MemoryStream(storage[output]), chars, xml));
|
||||
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>()))
|
||||
.Returns<string>(output => new ServiceBufferFileStreamWriter(new MemoryStream(storage[output]), 1024, 1024));
|
||||
|
||||
return mock.Object;
|
||||
}
|
||||
@@ -188,10 +187,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
#region Service Mocking
|
||||
|
||||
public static QueryExecutionService GetPrimedExecutionService(Dictionary<string, string>[][] data, bool isConnected, bool throwOnRead, WorkspaceService<SqlToolsSettings> workspaceService)
|
||||
public static QueryExecutionService GetPrimedExecutionService(Dictionary<string, string>[][] data,
|
||||
bool isConnected, bool throwOnRead, WorkspaceService<SqlToolsSettings> workspaceService,
|
||||
out Dictionary<string, byte[]> storage)
|
||||
{
|
||||
// Create a place for the temp "files" to be written
|
||||
Dictionary<string, byte[]> storage = new Dictionary<string, byte[]>();
|
||||
storage = new Dictionary<string, byte[]>();
|
||||
|
||||
// Create the connection factory with the dataset
|
||||
var factory = CreateTestConnectionInfo(data, throwOnRead).Factory;
|
||||
@@ -205,7 +206,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.OutCallback((string owner, out ConnectionInfo connInfo) => connInfo = isConnected ? ci : null)
|
||||
.Returns(isConnected);
|
||||
|
||||
return new QueryExecutionService(connectionService.Object, workspaceService) {BufferFileStreamFactory = GetFileStreamFactory(storage)};
|
||||
return new QueryExecutionService(connectionService.Object, workspaceService) { BufferFileStreamFactory = GetFileStreamFactory(storage) };
|
||||
}
|
||||
|
||||
public static QueryExecutionService GetPrimedExecutionService(Dictionary<string, string>[][] data, bool isConnected, bool throwOnRead, WorkspaceService<SqlToolsSettings> workspaceService)
|
||||
{
|
||||
Dictionary<string, byte[]> storage;
|
||||
return GetPrimedExecutionService(data, isConnected, throwOnRead, workspaceService, out storage);
|
||||
}
|
||||
|
||||
public static WorkspaceService<SqlToolsSettings> GetPrimedWorkspaceService(string query)
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class SaveAsCsvFileStreamWriterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Something\rElse")]
|
||||
[InlineData("Something\nElse")]
|
||||
[InlineData("Something\"Else")]
|
||||
[InlineData("Something,Else")]
|
||||
[InlineData("\tSomething")]
|
||||
[InlineData("Something\t")]
|
||||
[InlineData(" Something")]
|
||||
[InlineData("Something ")]
|
||||
[InlineData(" \t\r\n\",\r\n\"\r ")]
|
||||
public void EncodeCsvFieldShouldWrap(string field)
|
||||
{
|
||||
// If: I CSV encode a field that has forbidden characters in it
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(field);
|
||||
|
||||
// Then: It should wrap it in quotes
|
||||
Assert.True(Regex.IsMatch(output, "^\".*")
|
||||
&& Regex.IsMatch(output, ".*\"$"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Something")]
|
||||
[InlineData("Something valid.")]
|
||||
[InlineData("Something\tvalid")]
|
||||
public void EncodeCsvFieldShouldNotWrap(string field)
|
||||
{
|
||||
// If: I CSV encode a field that does not have forbidden characters in it
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(field);
|
||||
|
||||
// Then: It should not wrap it in quotes
|
||||
Assert.False(Regex.IsMatch(output, "^\".*\"$"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodeCsvFieldReplace()
|
||||
{
|
||||
// If: I CSV encode a field that has a double quote in it,
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField("Some\"thing");
|
||||
|
||||
// Then: It should be replaced with double double quotes
|
||||
Assert.Equal("\"Some\"\"thing\"", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodeCsvFieldNull()
|
||||
{
|
||||
// If: I CSV encode a null
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(null);
|
||||
|
||||
// Then: there should be a string version of null returned
|
||||
Assert.Equal("NULL", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithoutColumnSelectionOrHeader()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams();
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then: It should write one line with 2 items, comma delimited
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
|
||||
Assert.Equal(1, lines.Length);
|
||||
string[] values = lines[0].Split(',');
|
||||
Assert.Equal(2, values.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithHeader()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made, headers should be printed
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
IncludeHeaders = true
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... It should have written two lines
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
Assert.Equal(2, lines.Length);
|
||||
|
||||
// ... It should have written a header line with two, comma separated names
|
||||
string[] headerValues = lines[0].Split(',');
|
||||
Assert.Equal(2, headerValues.Length);
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
Assert.Equal(columns[i].ColumnName, headerValues[i]);
|
||||
}
|
||||
|
||||
// Note: No need to check values, it is done as part of the previous test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that selects n-1 columns from the front and back
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
ColumnStartIndex = 1,
|
||||
ColumnEndIndex = 2,
|
||||
RowStartIndex = 0, // Including b/c it is required to be a "save selection"
|
||||
RowEndIndex = 10,
|
||||
IncludeHeaders = true // Including headers to test both column selection logic
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" },
|
||||
new DbCellValue { DisplayValue = "item3" },
|
||||
new DbCellValue { DisplayValue = "item4" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2")),
|
||||
new DbColumnWrapper(new TestDbColumn("column3")),
|
||||
new DbColumnWrapper(new TestDbColumn("column4"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... It should have written two lines
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
Assert.Equal(2, lines.Length);
|
||||
|
||||
// ... It should have written a header line with two, comma separated names
|
||||
string[] headerValues = lines[0].Split(',');
|
||||
Assert.Equal(2, headerValues.Length);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.Equal(columns[i].ColumnName, headerValues[i-1]);
|
||||
}
|
||||
|
||||
// ... The second line should have two, comma separated values
|
||||
string[] dataValues = lines[1].Split(',');
|
||||
Assert.Equal(2, dataValues.Length);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.Equal(data[i].DisplayValue, dataValues[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// 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.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class SaveAsJsonFileStreamWriterTests
|
||||
{
|
||||
[Fact]
|
||||
public void ArrayWrapperTest()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create storage for the output
|
||||
byte[] output = new byte[8192];
|
||||
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams();
|
||||
|
||||
// If:
|
||||
// ... I create and then destruct a json writer
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
jsonWriter.Dispose();
|
||||
|
||||
// Then:
|
||||
// ... The output should be an empty array
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0');
|
||||
object[] outputArray = JsonConvert.DeserializeObject<object[]>(outputString);
|
||||
Assert.Equal(0, outputArray.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithoutColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made
|
||||
// ... Create a set of data to write
|
||||
// ... Create storage for the output
|
||||
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams();
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue {DisplayValue = "item1", RawObject = "item1"},
|
||||
new DbCellValue {DisplayValue = "null", RawObject = null}
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If:
|
||||
// ... I write two rows
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
using (jsonWriter)
|
||||
{
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... Upon deserialization to an array of dictionaries
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0');
|
||||
Dictionary<string, string>[] outputObject =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, string>[]>(outputString);
|
||||
|
||||
// ... There should be 2 items in the array,
|
||||
// ... The item should have two fields, and two values, assigned appropriately
|
||||
Assert.Equal(2, outputObject.Length);
|
||||
foreach (var item in outputObject)
|
||||
{
|
||||
Assert.Equal(2, item.Count);
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
Assert.True(item.ContainsKey(columns[i].ColumnName));
|
||||
Assert.Equal(data[i].RawObject == null ? null : data[i].DisplayValue, item[columns[i].ColumnName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that selects n-1 columns from the front and back
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
ColumnStartIndex = 1,
|
||||
ColumnEndIndex = 3,
|
||||
RowStartIndex = 0, // Including b/c it is required to be a "save selection"
|
||||
RowEndIndex = 10
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1", RawObject = "item1"},
|
||||
new DbCellValue { DisplayValue = "item2", RawObject = "item2"},
|
||||
new DbCellValue { DisplayValue = "null", RawObject = null},
|
||||
new DbCellValue { DisplayValue = "null", RawObject = null}
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2")),
|
||||
new DbColumnWrapper(new TestDbColumn("column3")),
|
||||
new DbColumnWrapper(new TestDbColumn("column4"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write two rows
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
using (jsonWriter)
|
||||
{
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... Upon deserialization to an array of dictionaries
|
||||
string outputString = Encoding.UTF8.GetString(output).Trim('\0');
|
||||
Dictionary<string, string>[] outputObject =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, string>[]>(outputString);
|
||||
|
||||
// ... There should be 2 items in the array
|
||||
// ... The items should have 2 fields and values
|
||||
Assert.Equal(2, outputObject.Length);
|
||||
foreach (var item in outputObject)
|
||||
{
|
||||
Assert.Equal(2, item.Count);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.True(item.ContainsKey(columns[i].ColumnName));
|
||||
Assert.Equal(data[i].RawObject == null ? null : data[i].DisplayValue, item[columns[i].ColumnName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Test.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Test.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// 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 Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.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,
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
Assert.Throws<ArgumentNullException>(() => rs.SaveAs(
|
||||
null,
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()),
|
||||
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,
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
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,
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
Assert.Throws<InvalidOperationException>(() => rs.SaveAs(
|
||||
new SaveResultsRequestParams(),
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()),
|
||||
null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveAsFailedExistingTaskInProgress()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a result set that has been executed
|
||||
ResultSet rs = new ResultSet(
|
||||
GetReader(new[] { Common.StandardTestData }, false, Common.StandardQuery),
|
||||
Common.Ordinal, Common.Ordinal,
|
||||
Common.GetFileStreamFactory(new Dictionary<string, byte[]>()));
|
||||
|
||||
// ... Insert a non-started task into the save as tasks
|
||||
rs.SaveTasks.AddOrUpdate(Common.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 = Common.OwnerUri};
|
||||
Assert.Throws<InvalidOperationException>(() => rs.SaveAs(
|
||||
requestParams, GetMockFactory(GetMockWriter().Object, null),
|
||||
null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SaveAsWithoutRowSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a fake place to store data
|
||||
Dictionary<string, byte[]> mockFs = new Dictionary<string, byte[]>();
|
||||
|
||||
// ... Create a mock reader/writer for reading the result
|
||||
IFileStreamFactory resultFactory = Common.GetFileStreamFactory(mockFs);
|
||||
|
||||
// ... Create a result set with dummy data and read to the end
|
||||
ResultSet rs = new ResultSet(
|
||||
GetReader(new[] {Common.StandardTestData}, false, Common.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 = Common.OwnerUri}, saveFactory, null, null);
|
||||
Assert.True(rs.SaveTasks.ContainsKey(Common.OwnerUri));
|
||||
await rs.SaveTasks[Common.OwnerUri];
|
||||
|
||||
// Then:
|
||||
// ... The task should have completed successfully
|
||||
Assert.Equal(TaskStatus.RanToCompletion, rs.SaveTasks[Common.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 fake place to store data
|
||||
Dictionary<string, byte[]> mockFs = new Dictionary<string, byte[]>();
|
||||
|
||||
// ... Create a mock reader/writer for reading the result
|
||||
IFileStreamFactory resultFactory = Common.GetFileStreamFactory(mockFs);
|
||||
|
||||
// ... Create a result set with dummy data and read to the end
|
||||
ResultSet rs = new ResultSet(
|
||||
GetReader(new[] { Common.StandardTestData }, false, Common.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 = Common.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(Common.OwnerUri));
|
||||
await rs.SaveTasks[Common.OwnerUri];
|
||||
|
||||
// Then:
|
||||
// ... The task should have completed successfully
|
||||
Assert.Equal(TaskStatus.RanToCompletion, rs.SaveTasks[Common.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(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
//
|
||||
// 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.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
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.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 = Common.OwnerUri // Won't exist because nothing has executed
|
||||
};
|
||||
object error = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(null)
|
||||
.AddErrorHandling(o => error = o);
|
||||
await qes.HandleSaveResultsAsCsvRequest(saveParams, requestContext.Object);
|
||||
|
||||
// Then:
|
||||
// ... An error event should have been fired
|
||||
// ... No success event should have been fired
|
||||
VerifyResponseCalls(requestContext, false, true);
|
||||
Assert.IsType<SaveResultRequestError>(error);
|
||||
Assert.NotNull(error);
|
||||
Assert.NotNull(((SaveResultRequestError)error).message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SaveResultAsCsvFailure()
|
||||
{
|
||||
// Given:
|
||||
// ... A working query and workspace service
|
||||
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
Dictionary<string, byte[]> storage;
|
||||
QueryExecutionService qes = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false, ws, out storage);
|
||||
|
||||
// ... The query execution service has executed a query with results
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await qes.ActiveQueries[Common.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 = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
ColumnStartIndex = -1,
|
||||
ColumnEndIndex = 100,
|
||||
RowStartIndex = 0,
|
||||
RowEndIndex = 5
|
||||
};
|
||||
qes.CsvFileFactory = GetCsvStreamFactory(storage, saveParams);
|
||||
object error = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(null)
|
||||
.AddErrorHandling(e => error = e);
|
||||
await qes.HandleSaveResultsAsCsvRequest(saveParams, requestContext.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
|
||||
VerifyResponseCalls(requestContext, false, true);
|
||||
Assert.IsType<SaveResultRequestError>(error);
|
||||
Assert.NotNull(error);
|
||||
Assert.NotNull(((SaveResultRequestError)error).message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SaveResultsAsCsvSuccess()
|
||||
{
|
||||
// Given:
|
||||
// ... A working query and workspace service
|
||||
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
Dictionary<string, byte[]> storage;
|
||||
QueryExecutionService qes = Common.GetPrimedExecutionService(new[] {Common.StandardTestData}, true, false, ws, out storage);
|
||||
|
||||
// ... The query execution service has executed a query with results
|
||||
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await qes.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
||||
|
||||
// If: I attempt to save a result set from a query
|
||||
SaveResultsAsCsvRequestParams saveParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
FilePath = "qqq",
|
||||
BatchIndex = 0,
|
||||
ResultSetIndex = 0
|
||||
};
|
||||
qes.CsvFileFactory = GetCsvStreamFactory(storage, saveParams);
|
||||
SaveResultRequestResult result = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(r => result = r);
|
||||
await qes.HandleSaveResultsAsCsvRequest(saveParams, requestContext.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
|
||||
VerifyResponseCalls(requestContext, true, false);
|
||||
Assert.NotNull(result);
|
||||
Assert.Null(result.Messages);
|
||||
}
|
||||
|
||||
#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 = Common.OwnerUri // Won't exist because nothing has executed
|
||||
};
|
||||
object error = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(null)
|
||||
.AddErrorHandling(o => error = o);
|
||||
await qes.HandleSaveResultsAsJsonRequest(saveParams, requestContext.Object);
|
||||
|
||||
// Then:
|
||||
// ... An error event should have been fired
|
||||
// ... No success event should have been fired
|
||||
VerifyResponseCalls(requestContext, false, true);
|
||||
Assert.IsType<SaveResultRequestError>(error);
|
||||
Assert.NotNull(error);
|
||||
Assert.NotNull(((SaveResultRequestError)error).message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SaveResultAsJsonFailure()
|
||||
{
|
||||
// Given:
|
||||
// ... A working query and workspace service
|
||||
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
Dictionary<string, byte[]> storage;
|
||||
QueryExecutionService qes = Common.GetPrimedExecutionService(new[] { Common.StandardTestData }, true, false, ws, out storage);
|
||||
|
||||
// ... The query execution service has executed a query with results
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await qes.ActiveQueries[Common.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 = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
ColumnStartIndex = -1,
|
||||
ColumnEndIndex = 100,
|
||||
RowStartIndex = 0,
|
||||
RowEndIndex = 5
|
||||
};
|
||||
qes.JsonFileFactory = GetJsonStreamFactory(storage, saveParams);
|
||||
object error = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(null)
|
||||
.AddErrorHandling(e => error = e);
|
||||
await qes.HandleSaveResultsAsJsonRequest(saveParams, requestContext.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
|
||||
VerifyResponseCalls(requestContext, false, true);
|
||||
Assert.IsType<SaveResultRequestError>(error);
|
||||
Assert.NotNull(error);
|
||||
Assert.NotNull(((SaveResultRequestError)error).message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SaveResultsAsJsonSuccess()
|
||||
{
|
||||
// Given:
|
||||
// ... A working query and workspace service
|
||||
WorkspaceService<SqlToolsSettings> ws = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
Dictionary<string, byte[]> storage;
|
||||
QueryExecutionService qes = Common.GetPrimedExecutionService(new[] { Common.StandardTestData }, true, false, ws, out storage);
|
||||
|
||||
// ... The query execution service has executed a query with results
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await qes.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await qes.ActiveQueries[Common.OwnerUri].ExecutionTask;
|
||||
|
||||
// If: I attempt to save a result set from a query
|
||||
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
FilePath = "qqq",
|
||||
BatchIndex = 0,
|
||||
ResultSetIndex = 0
|
||||
};
|
||||
qes.JsonFileFactory = GetJsonStreamFactory(storage, saveParams);
|
||||
SaveResultRequestResult result = null;
|
||||
var requestContext = RequestContextMocks.Create<SaveResultRequestResult>(r => result = r);
|
||||
await qes.HandleSaveResultsAsJsonRequest(saveParams, requestContext.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
|
||||
VerifyResponseCalls(requestContext, true, false);
|
||||
Assert.NotNull(result);
|
||||
Assert.Null(result.Messages);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
|
||||
private static void VerifyResponseCalls(Mock<RequestContext<SaveResultRequestResult>> requestContext, bool successCalled, bool errorCalled)
|
||||
{
|
||||
requestContext.Verify(rc => rc.SendResult(It.IsAny<SaveResultRequestResult>()),
|
||||
successCalled ? Times.Once() : Times.Never());
|
||||
requestContext.Verify(rc => rc.SendError(It.IsAny<object>()),
|
||||
errorCalled ? Times.Once() : Times.Never());
|
||||
}
|
||||
|
||||
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])));
|
||||
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])));
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for saving a result set to a file
|
||||
/// </summary>
|
||||
public class SaveResultsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test save results to a file as CSV with correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsCsvSuccessTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as csv with correct parameters
|
||||
var saveParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_1.csv",
|
||||
IncludeHeaders = true
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.Null(r.Messages);
|
||||
}).Complete();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
|
||||
// Expect to see a file successfully created in filepath and a success message
|
||||
saveRequest.Validate();
|
||||
Assert.True(File.Exists(saveParams.FilePath));
|
||||
|
||||
// Delete temp file after test
|
||||
if (File.Exists(saveParams.FilePath))
|
||||
{
|
||||
File.Delete(saveParams.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test save results to a file as CSV with a selection of cells and correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsCsvWithSelectionSuccessTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as csv with correct parameters
|
||||
var saveParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_2.csv",
|
||||
IncludeHeaders = true,
|
||||
RowStartIndex = 0,
|
||||
RowEndIndex = 0,
|
||||
ColumnStartIndex = 0,
|
||||
ColumnEndIndex = 0
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.Null(r.Messages);
|
||||
}).Complete();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
Task saveTask = selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
await saveTask;
|
||||
|
||||
// Expect to see a file successfully created in filepath and a success message
|
||||
saveRequest.Validate();
|
||||
Assert.True(File.Exists(saveParams.FilePath));
|
||||
|
||||
// Delete temp file after test
|
||||
if (File.Exists(saveParams.FilePath))
|
||||
{
|
||||
File.Delete(saveParams.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test handling exception in saving results to CSV file
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsCsvExceptionTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as csv with incorrect filepath
|
||||
var saveParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.csv" : "/test.csv"
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddErrorValidation<SaveResultRequestError>(e =>
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(e.message));
|
||||
}).Complete();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
|
||||
// Expect to see error message
|
||||
saveRequest.Validate();
|
||||
Assert.False(File.Exists(saveParams.FilePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test saving results to CSV file when the requested result set is no longer active
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SaveResultsAsCsvQueryNotFoundTest()
|
||||
{
|
||||
// Create a query execution service
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
||||
|
||||
// Request to save the results as csv with query that is no longer active
|
||||
var saveParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
OwnerUri = "falseuri",
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_3.csv"
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.NotNull(r.Messages);
|
||||
}).Complete();
|
||||
await queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object);
|
||||
|
||||
// Expect message that save failed
|
||||
saveRequest.Validate();
|
||||
Assert.False(File.Exists(saveParams.FilePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test save results to a file as JSON with correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsJsonSuccessTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as json with correct parameters
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_4.json"
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.Null(r.Messages);
|
||||
}).Complete();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
|
||||
// Expect to see a file successfully created in filepath and a success message
|
||||
saveRequest.Validate();
|
||||
Assert.True(File.Exists(saveParams.FilePath));
|
||||
|
||||
// Delete temp file after test
|
||||
if (File.Exists(saveParams.FilePath))
|
||||
{
|
||||
File.Delete(saveParams.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test save results to a file as JSON with a selection of cells and correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsJsonWithSelectionSuccessTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as json with correct parameters
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_5.json",
|
||||
RowStartIndex = 0,
|
||||
RowEndIndex = 1,
|
||||
ColumnStartIndex = 0,
|
||||
ColumnEndIndex = 1
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.Null(r.Messages);
|
||||
}).Complete();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
|
||||
// Expect to see a file successfully created in filepath and a success message
|
||||
saveRequest.Validate();
|
||||
Assert.True(File.Exists(saveParams.FilePath));
|
||||
|
||||
// Delete temp file after test
|
||||
if (File.Exists(saveParams.FilePath))
|
||||
{
|
||||
File.Delete(saveParams.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test handling exception in saving results to JSON file
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void SaveResultsAsJsonExceptionTest()
|
||||
{
|
||||
// Execute a query
|
||||
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 executeRequest = RequestContextMocks.Create<QueryExecuteResult>(null);
|
||||
await Common.AwaitExecution(queryService, executeParams, executeRequest.Object);
|
||||
|
||||
// Request to save the results as json with incorrect filepath
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
OwnerUri = Common.OwnerUri,
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "G:\\test.json" : "/test.json"
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddErrorValidation<SaveResultRequestError>(e =>
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(e.message));
|
||||
}).Complete();
|
||||
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
|
||||
|
||||
// Call save results and wait on the save task
|
||||
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
|
||||
ResultSet selectedResultSet = queryService.ActiveQueries[saveParams.OwnerUri].Batches[saveParams.BatchIndex].ResultSets[saveParams.ResultSetIndex];
|
||||
await selectedResultSet.GetSaveTask(saveParams.FilePath);
|
||||
|
||||
// Expect to see error message
|
||||
saveRequest.Validate();
|
||||
Assert.False(File.Exists(saveParams.FilePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test saving results to JSON file when the requested result set is no longer active
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SaveResultsAsJsonQueryNotFoundTest()
|
||||
{
|
||||
// Create a query service
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Common.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
||||
|
||||
// Request to save the results as json with query that is no longer active
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
OwnerUri = "falseuri",
|
||||
ResultSetIndex = 0,
|
||||
BatchIndex = 0,
|
||||
FilePath = "testwrite_6.json"
|
||||
};
|
||||
var saveRequest = new EventFlowValidator<SaveResultRequestResult>()
|
||||
.AddResultValidation(r =>
|
||||
{
|
||||
Assert.Equal("Failed to save results, ID not found.", r.Messages);
|
||||
}).Complete();
|
||||
await queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object);
|
||||
|
||||
// Expect message that save failed
|
||||
saveRequest.Validate();
|
||||
Assert.False(File.Exists(saveParams.FilePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user