mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-24 17:24:14 -05:00
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:
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the CommandOptions class
|
||||
/// </summary>
|
||||
public class CommandOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void LoggingEnabledWhenFlagProvided()
|
||||
{
|
||||
var args = new string[] {"--enable-logging"};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.True(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoggingDisabledWhenFlagNotProvided()
|
||||
{
|
||||
var args = new string[] {};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsageIsShownWhenHelpFlagProvided()
|
||||
{
|
||||
var args = new string[] {"--help"};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.True(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsageIsShownWhenBadArgumentsProvided()
|
||||
{
|
||||
var args = new string[] {"--unknown-argument", "/bad-argument"};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.True(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultValuesAreUsedWhenNoArgumentsAreProvided()
|
||||
{
|
||||
var args = new string[] {};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("en")]
|
||||
[InlineData("es")]
|
||||
public void LocaleSetWhenProvided(string locale)
|
||||
{
|
||||
var args = new string[] {"--locale " + locale};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, locale);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldExitSetWhenInvalidLocale()
|
||||
{
|
||||
string locale = "invalid";
|
||||
var args = new string[] { "--locale " + locale };
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.ShouldExit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LocaleNotSetWhenNotProvided()
|
||||
{
|
||||
var args = new string[] {};
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class EventFlowValidator<TRequestContext>
|
||||
{
|
||||
private readonly List<ExpectedEvent> expectedEvents = new List<ExpectedEvent>();
|
||||
private readonly List<ReceivedEvent> receivedEvents = new List<ReceivedEvent>();
|
||||
private readonly Mock<RequestContext<TRequestContext>> requestContext;
|
||||
private bool completed;
|
||||
|
||||
public EventFlowValidator()
|
||||
{
|
||||
requestContext = new Mock<RequestContext<TRequestContext>>(MockBehavior.Strict);
|
||||
}
|
||||
|
||||
public RequestContext<TRequestContext> Object
|
||||
{
|
||||
get { return requestContext.Object; }
|
||||
}
|
||||
|
||||
public EventFlowValidator<TRequestContext> AddEventValidation<TParams>(EventType<TParams> expectedEvent, Action<TParams> paramValidation)
|
||||
{
|
||||
expectedEvents.Add(new ExpectedEvent
|
||||
{
|
||||
EventType = EventTypes.Event,
|
||||
ParamType = typeof(TParams),
|
||||
Validator = paramValidation
|
||||
});
|
||||
|
||||
requestContext.Setup(rc => rc.SendEvent(expectedEvent, It.IsAny<TParams>()))
|
||||
.Callback<EventType<TParams>, TParams>((et, p) =>
|
||||
{
|
||||
receivedEvents.Add(new ReceivedEvent
|
||||
{
|
||||
EventObject = p,
|
||||
EventType = EventTypes.Event
|
||||
});
|
||||
})
|
||||
.Returns(Task.FromResult(0));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventFlowValidator<TRequestContext> AddResultValidation(Action<TRequestContext> paramValidation)
|
||||
{
|
||||
// Add the expected event
|
||||
expectedEvents.Add(new ExpectedEvent
|
||||
{
|
||||
EventType = EventTypes.Result,
|
||||
ParamType = typeof(TRequestContext),
|
||||
Validator = paramValidation
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventFlowValidator<TRequestContext> AddErrorValidation<TParams>(Action<TParams> paramValidation)
|
||||
{
|
||||
// Add the expected result
|
||||
expectedEvents.Add(new ExpectedEvent
|
||||
{
|
||||
EventType = EventTypes.Error,
|
||||
ParamType = typeof(TParams),
|
||||
Validator = paramValidation
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventFlowValidator<TRequestContext> Complete()
|
||||
{
|
||||
// Add general handler for result handling
|
||||
requestContext.Setup(rc => rc.SendResult(It.IsAny<TRequestContext>()))
|
||||
.Callback<TRequestContext>(r => receivedEvents.Add(new ReceivedEvent
|
||||
{
|
||||
EventObject = r,
|
||||
EventType = EventTypes.Result
|
||||
}))
|
||||
.Returns(Task.FromResult(0));
|
||||
|
||||
// Add general handler for error event
|
||||
requestContext.AddErrorHandling(o =>
|
||||
{
|
||||
receivedEvents.Add(new ReceivedEvent
|
||||
{
|
||||
EventObject = o,
|
||||
EventType = EventTypes.Error
|
||||
});
|
||||
});
|
||||
|
||||
completed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
// Make sure the handlers have been added
|
||||
if (!completed)
|
||||
{
|
||||
throw new Exception("EventFlowValidator must be completed before it can be validated.");
|
||||
}
|
||||
|
||||
// Iterate over the two lists in sync to see if they are the same
|
||||
for (int i = 0; i < Math.Max(expectedEvents.Count, receivedEvents.Count); i++)
|
||||
{
|
||||
// Step 0) Make sure both events exist
|
||||
if (i >= expectedEvents.Count)
|
||||
{
|
||||
throw new Exception($"Unexpected event received: [{receivedEvents[i].EventType}] {receivedEvents[i].EventObject}");
|
||||
}
|
||||
ExpectedEvent expected = expectedEvents[i];
|
||||
|
||||
if (i >= receivedEvents.Count)
|
||||
{
|
||||
throw new Exception($"Expected additional events: [{expectedEvents[i].EventType}] {expectedEvents[i].ParamType}");
|
||||
}
|
||||
ReceivedEvent received = receivedEvents[i];
|
||||
|
||||
// Step 1) Make sure the event type matches
|
||||
Assert.Equal(expected.EventType, received.EventType);
|
||||
|
||||
// Step 2) Make sure the param type matches
|
||||
Assert.Equal(expected.ParamType, received.EventObject.GetType());
|
||||
|
||||
// Step 3) Run the validator on the param object
|
||||
Assert.NotNull(received.EventObject);
|
||||
expected.Validator?.DynamicInvoke(received.EventObject);
|
||||
}
|
||||
}
|
||||
|
||||
private enum EventTypes
|
||||
{
|
||||
Result,
|
||||
Error,
|
||||
Event
|
||||
}
|
||||
|
||||
private class ExpectedEvent
|
||||
{
|
||||
public EventTypes EventType { get; set; }
|
||||
public Type ParamType { get; set; }
|
||||
public Delegate Validator { get; set; }
|
||||
}
|
||||
|
||||
private class ReceivedEvent
|
||||
{
|
||||
public object EventObject { get; set; }
|
||||
public EventTypes EventType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the LongList class
|
||||
/// </summary>
|
||||
public class LongListTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Add and remove and item in a LongList
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LongListTest()
|
||||
{
|
||||
var longList = new LongList<char>();
|
||||
longList.Add('.');
|
||||
Assert.True(longList.Count == 1);
|
||||
longList.RemoveAt(0);
|
||||
Assert.True(longList.Count == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add and remove and item in a LongList causing an expansion
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LongListExpandTest()
|
||||
{
|
||||
var longList = new LongList<int>();
|
||||
longList.ExpandListSize = 3;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
longList.Add(i);
|
||||
}
|
||||
Assert.Equal(longList.Count, 6);
|
||||
Assert.NotNull(longList.GetItem(4));
|
||||
|
||||
bool didEnum = false;
|
||||
foreach (var j in longList)
|
||||
{
|
||||
didEnum = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Assert.True(didEnum);
|
||||
|
||||
longList.RemoveAt(4);
|
||||
Assert.Equal(longList.Count, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Reflection;
|
||||
using Moq.Language;
|
||||
using Moq.Language.Flow;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public static class MoqExtensions
|
||||
{
|
||||
public delegate void OutAction<TOut>(out TOut outVal);
|
||||
|
||||
public delegate void OutAction<in T1, TOut>(T1 arg1, out TOut outVal);
|
||||
|
||||
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(
|
||||
this ICallback<TMock, TReturn> mock, OutAction<TOut> action) where TMock : class
|
||||
{
|
||||
return OutCallbackInternal(mock, action);
|
||||
}
|
||||
|
||||
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(
|
||||
this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action) where TMock : class
|
||||
{
|
||||
return OutCallbackInternal(mock, action);
|
||||
}
|
||||
|
||||
private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(
|
||||
ICallback<TMock, TReturn> mock, object action) where TMock : class
|
||||
{
|
||||
typeof(ICallback<TMock, TReturn>).GetTypeInfo()
|
||||
.Assembly.GetType("Moq.MethodCall")
|
||||
.GetMethod("SetCallbackWithArguments",
|
||||
BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.Invoke(mock, new[] { action });
|
||||
return mock as IReturnsThrows<TMock, TReturn>;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public static class RequestContextMocks
|
||||
{
|
||||
|
||||
public static Mock<RequestContext<TResponse>> Create<TResponse>(Action<TResponse> resultCallback)
|
||||
{
|
||||
var requestContext = new Mock<RequestContext<TResponse>>();
|
||||
|
||||
// Setup the mock for SendResult
|
||||
var sendResultFlow = requestContext
|
||||
.Setup(rc => rc.SendResult(It.IsAny<TResponse>()))
|
||||
.Returns(Task.FromResult(0));
|
||||
if (resultCallback != null)
|
||||
{
|
||||
sendResultFlow.Callback(resultCallback);
|
||||
}
|
||||
return requestContext;
|
||||
}
|
||||
|
||||
public static Mock<RequestContext<TResponse>> AddEventHandling<TResponse, TParams>(
|
||||
this Mock<RequestContext<TResponse>> mock,
|
||||
EventType<TParams> expectedEvent,
|
||||
Action<EventType<TParams>, TParams> eventCallback)
|
||||
{
|
||||
var flow = mock.Setup(rc => rc.SendEvent(
|
||||
It.Is<EventType<TParams>>(m => m == expectedEvent),
|
||||
It.IsAny<TParams>()))
|
||||
.Returns(Task.FromResult(0));
|
||||
if (eventCallback != null)
|
||||
{
|
||||
flow.Callback(eventCallback);
|
||||
}
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
public static Mock<RequestContext<TResponse>> AddErrorHandling<TResponse>(
|
||||
this Mock<RequestContext<TResponse>> mock,
|
||||
Action<object> errorCallback)
|
||||
{
|
||||
// Setup the mock for SendError
|
||||
var sendErrorFlow = mock.Setup(rc => rc.SendError(It.IsAny<object>()))
|
||||
.Returns(Task.FromResult(0));
|
||||
if (errorCallback != null)
|
||||
{
|
||||
sendErrorFlow.Callback(errorCallback);
|
||||
}
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
public static Mock<RequestContext<TResponse>> SetupRequestContextMock<TResponse, TParams>(
|
||||
Action<TResponse> resultCallback,
|
||||
EventType<TParams> expectedEvent,
|
||||
Action<EventType<TParams>, TParams> eventCallback,
|
||||
Action<object> errorCallback)
|
||||
{
|
||||
return Create(resultCallback)
|
||||
.AddEventHandling(expectedEvent, eventCallback)
|
||||
.AddErrorHandling(errorCallback);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
//
|
||||
// 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.Text.RegularExpressions;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
|
||||
{
|
||||
public class SqlScriptFormatterTests
|
||||
{
|
||||
#region Format Identifier Tests
|
||||
|
||||
[Fact]
|
||||
public void FormatIdentifierNull()
|
||||
{
|
||||
// If: I attempt to format null as an identifier
|
||||
// Then: I should get an exception thrown
|
||||
Assert.Throws<ArgumentNullException>(() => SqlScriptFormatter.FormatIdentifier(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test", "[test]")] // No escape characters
|
||||
[InlineData("]test", "[]]test]")] // Escape character at beginning
|
||||
[InlineData("te]st", "[te]]st]")] // Escape character in middle
|
||||
[InlineData("test]", "[test]]]")] // Escape character at end
|
||||
[InlineData("t]]est", "[t]]]]est]")] // Multiple escape characters
|
||||
public void FormatIdentifierTest(string value, string expectedOutput)
|
||||
{
|
||||
// If: I attempt to format a value as an identifier
|
||||
string output = SqlScriptFormatter.FormatIdentifier(value);
|
||||
|
||||
// Then: The output should match the expected output
|
||||
Assert.Equal(expectedOutput, output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test", "[test]")] // No splits, no escape characters
|
||||
[InlineData("test.test", "[test].[test]")] // One split, no escape characters
|
||||
[InlineData("test.te]st", "[test].[te]]st]")] // One split, one escape character
|
||||
[InlineData("test.test.test", "[test].[test].[test]")] // Two splits, no escape characters
|
||||
public void FormatMultipartIdentifierTest(string value, string expectedOutput)
|
||||
{
|
||||
// If: I attempt to format a value as a multipart identifier
|
||||
string output = SqlScriptFormatter.FormatMultipartIdentifier(value);
|
||||
|
||||
// Then: The output should match the expected output
|
||||
Assert.Equal(expectedOutput, output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetMultipartIdentifierArrays))]
|
||||
public void FormatMultipartIdentifierArrayTest(string expectedOutput, string[] splits)
|
||||
{
|
||||
// If: I attempt to format a value as a multipart identifier
|
||||
string output = SqlScriptFormatter.FormatMultipartIdentifier(splits);
|
||||
|
||||
// Then: The output should match the expected output
|
||||
Assert.Equal(expectedOutput, output);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetMultipartIdentifierArrays
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[] {"[test]", new[] {"test"}}; // No splits, no escape characters
|
||||
yield return new object[] {"[test].[test]", new[] {"test", "test"}}; // One split, no escape characters
|
||||
yield return new object[] {"[test].[te]]st]", new[] {"test", "te]st"}}; // One split, one escape character
|
||||
yield return new object[] {"[test].[test].[test]", new[] {"test", "test", "test"}}; // Two splits, no escape characters
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FormatValue Tests
|
||||
|
||||
[Fact]
|
||||
public void NullDbCellTest()
|
||||
{
|
||||
// If: I attempt to format a null db cell
|
||||
// Then: It should throw
|
||||
Assert.Throws<ArgumentNullException>(() => SqlScriptFormatter.FormatValue(null, new FormatterTestDbColumn(null)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullDbColumnTest()
|
||||
{
|
||||
// If: I attempt to format a null db column
|
||||
// Then: It should throw
|
||||
Assert.Throws<ArgumentNullException>(() => SqlScriptFormatter.FormatValue(new DbCellValue(), null));
|
||||
}
|
||||
|
||||
public void UnsupportedColumnTest()
|
||||
{
|
||||
// If: I attempt to format an unsupported datatype
|
||||
// Then: It should throw
|
||||
DbColumn column = new FormatterTestDbColumn("unsupported");
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => SqlScriptFormatter.FormatValue(new DbCellValue(), column));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullTest()
|
||||
{
|
||||
// If: I attempt to format a db cell that contains null
|
||||
// Then: I should get the null string back
|
||||
string formattedString = SqlScriptFormatter.FormatValue(new DbCellValue(), new FormatterTestDbColumn(null));
|
||||
Assert.Equal(SqlScriptFormatter.NullString, formattedString);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("BIGINT")]
|
||||
[InlineData("INT")]
|
||||
[InlineData("SMALLINT")]
|
||||
[InlineData("TINYINT")]
|
||||
public void IntegerNumericTest(string dataType)
|
||||
{
|
||||
// Setup: Build a column and cell for the integer type column
|
||||
DbColumn column = new FormatterTestDbColumn(dataType);
|
||||
DbCellValue cell = new DbCellValue { RawObject = (long)123 };
|
||||
|
||||
// If: I attempt to format an integer type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a long
|
||||
Assert.Equal(cell.RawObject, long.Parse(output));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("MONEY", "MONEY", null, null)]
|
||||
[InlineData("SMALLMONEY", "SMALLMONEY", null, null)]
|
||||
[InlineData("NUMERIC", @"NUMERIC\(\d+, \d+\)", 18, 0)]
|
||||
[InlineData("DECIMAL", @"DECIMAL\(\d+, \d+\)", 18, 0)]
|
||||
public void DecimalTest(string dataType, string regex, int? precision, int? scale)
|
||||
{
|
||||
// Setup: Build a column and cell for the decimal type column
|
||||
DbColumn column = new FormatterTestDbColumn(dataType, precision, scale);
|
||||
DbCellValue cell = new DbCellValue { RawObject = 123.45m };
|
||||
|
||||
// If: I attempt to format a decimal type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: It should match a something like CAST(123.45 AS MONEY)
|
||||
Regex castRegex = new Regex($@"CAST\([\d\.]+ AS {regex}", RegexOptions.IgnoreCase);
|
||||
Assert.True(castRegex.IsMatch(output));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoubleTest()
|
||||
{
|
||||
// Setup: Build a column and cell for the approx numeric type column
|
||||
DbColumn column = new FormatterTestDbColumn("FLOAT");
|
||||
DbCellValue cell = new DbCellValue { RawObject = 3.14159d };
|
||||
|
||||
// If: I attempt to format a approx numeric type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a double
|
||||
Assert.Equal(cell.RawObject, double.Parse(output));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FloatTest()
|
||||
{
|
||||
// Setup: Build a column and cell for the approx numeric type column
|
||||
DbColumn column = new FormatterTestDbColumn("REAL");
|
||||
DbCellValue cell = new DbCellValue { RawObject = (float)3.14159 };
|
||||
|
||||
// If: I attempt to format a approx numeric type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a double
|
||||
Assert.Equal(cell.RawObject, float.Parse(output));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("SMALLDATETIME")]
|
||||
[InlineData("DATETIME")]
|
||||
[InlineData("DATETIME2")]
|
||||
[InlineData("DATE")]
|
||||
public void DateTimeTest(string dataType)
|
||||
{
|
||||
// Setup: Build a column and cell for the datetime type column
|
||||
DbColumn column = new FormatterTestDbColumn(dataType);
|
||||
DbCellValue cell = new DbCellValue { RawObject = DateTime.Now };
|
||||
|
||||
// If: I attempt to format a datetime type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a datetime
|
||||
Regex dateTimeRegex = new Regex("N'(.*)'");
|
||||
DateTime outputDateTime;
|
||||
Assert.True(DateTime.TryParse(dateTimeRegex.Match(output).Groups[1].Value, out outputDateTime));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DateTimeOffsetTest()
|
||||
{
|
||||
// Setup: Build a column and cell for the datetime offset type column
|
||||
DbColumn column = new FormatterTestDbColumn("DATETIMEOFFSET");
|
||||
DbCellValue cell = new DbCellValue { RawObject = DateTimeOffset.Now };
|
||||
|
||||
// If: I attempt to format a datetime offset type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a datetime offset
|
||||
Regex dateTimeRegex = new Regex("N'(.*)'");
|
||||
DateTimeOffset outputDateTime;
|
||||
Assert.True(DateTimeOffset.TryParse(dateTimeRegex.Match(output).Groups[1].Value, out outputDateTime));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TimeTest()
|
||||
{
|
||||
// Setup: Build a column and cell for the time type column
|
||||
DbColumn column = new FormatterTestDbColumn("TIME");
|
||||
DbCellValue cell = new DbCellValue { RawObject = TimeSpan.FromHours(12) };
|
||||
|
||||
// If: I attempt to format a time type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be able to be converted back into a timespan
|
||||
Regex dateTimeRegex = new Regex("N'(.*)'");
|
||||
TimeSpan outputDateTime;
|
||||
Assert.True(TimeSpan.TryParse(dateTimeRegex.Match(output).Groups[1].Value, out outputDateTime));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "N''")] // Make sure empty string works
|
||||
[InlineData(" \t\r\n", "N' \t\r\n'")] // Test for whitespace
|
||||
[InlineData("some text \x9152", "N'some text \x9152'")] // Test unicode (UTF-8 and UTF-16)
|
||||
[InlineData("'", "N''''")] // Test with escaped character
|
||||
public void StringFormattingTest(string input, string expectedOutput)
|
||||
{
|
||||
// Setup: Build a column and cell for the string type column
|
||||
// NOTE: We're using VARCHAR because it's very general purpose.
|
||||
DbColumn column = new FormatterTestDbColumn("VARCHAR");
|
||||
DbCellValue cell = new DbCellValue { RawObject = input };
|
||||
|
||||
// If: I attempt to format a string type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should be quoted and escaped properly
|
||||
Assert.Equal(expectedOutput, output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("CHAR")]
|
||||
[InlineData("NCHAR")]
|
||||
[InlineData("VARCHAR")]
|
||||
[InlineData("TEXT")]
|
||||
[InlineData("NTEXT")]
|
||||
[InlineData("XML")]
|
||||
public void StringTypeTest(string datatype)
|
||||
{
|
||||
// Setup: Build a column and cell for the string type column
|
||||
DbColumn column = new FormatterTestDbColumn(datatype);
|
||||
DbCellValue cell = new DbCellValue { RawObject = "test string" };
|
||||
|
||||
// If: I attempt to format a string type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should match the output string
|
||||
Assert.Equal("N'test string'", output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("BINARY")]
|
||||
[InlineData("VARBINARY")]
|
||||
[InlineData("IMAGE")]
|
||||
public void BinaryTest(string datatype)
|
||||
{
|
||||
// Setup: Build a column and cell for the string type column
|
||||
DbColumn column = new FormatterTestDbColumn(datatype);
|
||||
DbCellValue cell = new DbCellValue
|
||||
{
|
||||
RawObject = new byte[] { 0x42, 0x45, 0x4e, 0x49, 0x53, 0x43, 0x4f, 0x4f, 0x4c }
|
||||
};
|
||||
|
||||
// If: I attempt to format a string type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should match the output string
|
||||
Regex regex = new Regex("0x[0-9A-F]+", RegexOptions.IgnoreCase);
|
||||
Assert.True(regex.IsMatch(output));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GuidTest()
|
||||
{
|
||||
// Setup: Build a column and cell for the string type column
|
||||
DbColumn column = new FormatterTestDbColumn("UNIQUEIDENTIFIER");
|
||||
DbCellValue cell = new DbCellValue { RawObject = Guid.NewGuid() };
|
||||
|
||||
// If: I attempt to format a string type column
|
||||
string output = SqlScriptFormatter.FormatValue(cell, column);
|
||||
|
||||
// Then: The output string should match the output string
|
||||
Regex regex = new Regex(@"N'[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}'", RegexOptions.IgnoreCase);
|
||||
Assert.True(regex.IsMatch(output));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class FormatterTestDbColumn : DbColumn
|
||||
{
|
||||
public FormatterTestDbColumn(string dataType, int? precision = null, int? scale = null)
|
||||
{
|
||||
DataTypeName = dataType;
|
||||
NumericPrecision = precision;
|
||||
NumericScale = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class SrTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Add and remove and item in a LongList
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SrPropertiesTest()
|
||||
{
|
||||
Assert.NotNull(ServiceLayer.SR.QueryServiceSubsetBatchNotCompleted);
|
||||
Assert.NotNull(ServiceLayer.SR.QueryServiceFileWrapperWriteOnly);
|
||||
Assert.NotNull(ServiceLayer.SR.QueryServiceFileWrapperNotInitialized);
|
||||
Assert.NotNull(ServiceLayer.SR.QueryServiceColumnNull);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// 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.Data.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class TestDbColumn : DbColumn
|
||||
{
|
||||
public TestDbColumn(string columnName)
|
||||
{
|
||||
base.IsLong = false;
|
||||
base.ColumnName = columnName;
|
||||
base.ColumnSize = 128;
|
||||
base.AllowDBNull = true;
|
||||
base.DataType = typeof(string);
|
||||
base.DataTypeName = "nvarchar";
|
||||
}
|
||||
|
||||
public TestDbColumn(string columnName, string columnType)
|
||||
: this(columnName)
|
||||
{
|
||||
base.DataTypeName = columnType;
|
||||
}
|
||||
|
||||
public TestDbColumn(string columnName, string columnType, Type columnDataType)
|
||||
{
|
||||
base.IsLong = false;
|
||||
base.ColumnName = columnName;
|
||||
base.ColumnSize = 128;
|
||||
base.AllowDBNull = true;
|
||||
base.DataType = columnDataType;
|
||||
base.DataTypeName = columnType;
|
||||
}
|
||||
|
||||
public TestDbColumn(string columnName, string columnType, int scale)
|
||||
: this(columnName, columnType)
|
||||
{
|
||||
base.NumericScale = scale;
|
||||
}
|
||||
|
||||
public TestDbColumn(string columnName, bool isAutoIncrement)
|
||||
: this(columnName)
|
||||
{
|
||||
base.IsAutoIncrement = isAutoIncrement;
|
||||
base.IsIdentity = isAutoIncrement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class TestDbDataReader : DbDataReader, IDbColumnSchemaGenerator
|
||||
{
|
||||
|
||||
#region Test Specific Implementations
|
||||
|
||||
private IEnumerable<TestResultSet> Data { get; }
|
||||
|
||||
public IEnumerator<TestResultSet> ResultSetEnumerator { get; }
|
||||
|
||||
private IEnumerator<object[]> RowEnumerator { get; set; }
|
||||
|
||||
public TestDbDataReader(IEnumerable<TestResultSet> data)
|
||||
{
|
||||
Data = data;
|
||||
if (Data != null)
|
||||
{
|
||||
ResultSetEnumerator = Data.GetEnumerator();
|
||||
ResultSetEnumerator.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public override int FieldCount => ResultSetEnumerator?.Current.Columns.Count ?? 0;
|
||||
|
||||
public override bool HasRows => ResultSetEnumerator?.Current.Rows.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Mimicks the behavior of SqlDbDataReader
|
||||
/// </summary>
|
||||
public override int RecordsAffected => RowEnumerator != null ? -1 : 1;
|
||||
|
||||
public override object this[int ordinal] => RowEnumerator.Current[ordinal];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implemented Methods
|
||||
|
||||
/// <summary>
|
||||
/// If the row enumerator hasn't been initialized for the current result set, the
|
||||
/// enumerator for the current result set is defined. Increments the enumerator
|
||||
/// </summary>
|
||||
/// <returns>True if tere were more rows, false otherwise</returns>
|
||||
public override bool Read()
|
||||
{
|
||||
if (RowEnumerator == null)
|
||||
{
|
||||
RowEnumerator = ResultSetEnumerator.Current.GetEnumerator();
|
||||
}
|
||||
return RowEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the result set enumerator and initializes the row enumerator
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool NextResult()
|
||||
{
|
||||
if (Data == null || !ResultSetEnumerator.MoveNext())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RowEnumerator = ResultSetEnumerator.Current.GetEnumerator();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value for the cell of the current row in the given column
|
||||
/// </summary>
|
||||
/// <param name="ordinal">Ordinal of the column</param>
|
||||
/// <returns>The object in the cell</returns>
|
||||
public override object GetValue(int ordinal)
|
||||
{
|
||||
return this[ordinal];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the values of all cells in this row in the given object array
|
||||
/// </summary>
|
||||
/// <param name="values">Destination for all cell values</param>
|
||||
/// <returns>Number of cells in the current row</returns>
|
||||
public override int GetValues(object[] values)
|
||||
{
|
||||
for (int i = 0; i < RowEnumerator.Current.Count(); i++)
|
||||
{
|
||||
values[i] = this[i];
|
||||
}
|
||||
return RowEnumerator.Current.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not a given cell in the current row is null
|
||||
/// </summary>
|
||||
/// <param name="ordinal">Ordinal of the column</param>
|
||||
/// <returns>True if the cell is null, false otherwise</returns>
|
||||
public override bool IsDBNull(int ordinal)
|
||||
{
|
||||
return this[ordinal] == null;
|
||||
}
|
||||
|
||||
/// <returns>Collection of test columns in the current result set</returns>
|
||||
public ReadOnlyCollection<DbColumn> GetColumnSchema()
|
||||
{
|
||||
if (ResultSetEnumerator?.Current == null || ResultSetEnumerator.Current.Rows.Count <= 0)
|
||||
{
|
||||
return new ReadOnlyCollection<DbColumn>(new List<DbColumn>());
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<DbColumn>(ResultSetEnumerator.Current.Columns);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Not Implemented
|
||||
|
||||
public override bool GetBoolean(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override byte GetByte(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override char GetChar(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
|
||||
{
|
||||
char[] allChars = ((string) RowEnumerator.Current[ordinal]).ToCharArray();
|
||||
int outLength = allChars.Length;
|
||||
if (buffer != null)
|
||||
{
|
||||
Array.Copy(allChars, (int) dataOffset, buffer, bufferOffset, outLength);
|
||||
}
|
||||
return outLength;
|
||||
}
|
||||
|
||||
public override string GetDataTypeName(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override DateTime GetDateTime(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override decimal GetDecimal(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override double GetDouble(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int GetOrdinal(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string GetName(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long GetInt64(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int GetInt32(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override short GetInt16(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Guid GetGuid(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetFloat(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Type GetFieldType(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string GetString(int ordinal)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IEnumerator GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override int Depth { get { throw new NotImplementedException(); } }
|
||||
|
||||
public override bool IsClosed { get { throw new NotImplementedException(); } }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class TestResultSet : IEnumerable<object[]>
|
||||
{
|
||||
public List<DbColumn> Columns;
|
||||
public List<object[]> Rows;
|
||||
|
||||
public TestResultSet(int columns, int rows)
|
||||
{
|
||||
Columns = Enumerable.Range(0, columns).Select(i => new TestDbColumn($"Col{i}")).Cast<DbColumn>().ToList();
|
||||
Rows = new List<object[]>(rows);
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
var row = Enumerable.Range(0, columns).Select(j => $"Cell{i}.{j}").Cast<object>().ToArray();
|
||||
Rows.Add(row);
|
||||
}
|
||||
}
|
||||
|
||||
public TestResultSet(IEnumerable<DbColumn> columns, IEnumerable<object[]> rows)
|
||||
{
|
||||
Columns = new List<DbColumn>(columns);
|
||||
Rows = new List<object[]>(rows);
|
||||
}
|
||||
|
||||
#region IEnumerable<object[]> Impementation
|
||||
|
||||
public IEnumerator<object[]> GetEnumerator()
|
||||
{
|
||||
return (IEnumerator<object[]>) Rows.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// 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.Data;
|
||||
using System.Data.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the ServiceHost Connection Service tests
|
||||
/// </summary>
|
||||
public class TestObjects
|
||||
{
|
||||
|
||||
public const string ScriptUri = "file://some/file.sql";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test connection service
|
||||
/// </summary>
|
||||
public static ConnectionService GetTestConnectionService()
|
||||
{
|
||||
// use mock database connection
|
||||
return new ConnectionService(new TestSqlConnectionFactory());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test connection info instance.
|
||||
/// </summary>
|
||||
public static ConnectionInfo GetTestConnectionInfo()
|
||||
{
|
||||
return new ConnectionInfo(
|
||||
new TestSqlConnectionFactory(),
|
||||
ScriptUri,
|
||||
GetTestConnectionDetails());
|
||||
}
|
||||
|
||||
public static ConnectParams GetTestConnectionParams()
|
||||
{
|
||||
return new ConnectParams()
|
||||
{
|
||||
OwnerUri = ScriptUri,
|
||||
Connection = GetTestConnectionDetails()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test connection details object
|
||||
/// </summary>
|
||||
public static ConnectionDetails GetTestConnectionDetails()
|
||||
{
|
||||
return new ConnectionDetails()
|
||||
{
|
||||
UserName = "user",
|
||||
Password = "password",
|
||||
DatabaseName = "databaseName",
|
||||
ServerName = "serverName"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a test language service instance
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static LanguageService GetTestLanguageService()
|
||||
{
|
||||
return new LanguageService();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a dummy TextDocumentPosition
|
||||
/// </summary>
|
||||
public static TextDocumentPosition GetTestDocPosition()
|
||||
{
|
||||
return new TextDocumentPosition
|
||||
{
|
||||
TextDocument = new TextDocumentIdentifier { Uri = ScriptUri },
|
||||
Position = new Position
|
||||
{
|
||||
Line = 0,
|
||||
Character = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test mock class for IDbCommand
|
||||
/// </summary>
|
||||
public class TestSqlCommand : DbCommand
|
||||
{
|
||||
internal TestSqlCommand(TestResultSet[] data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
internal TestResultSet[] Data { get; set; }
|
||||
|
||||
public override void Cancel()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int ExecuteNonQuery()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object ExecuteScalar()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Prepare()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string CommandText { get; set; }
|
||||
public override int CommandTimeout { get; set; }
|
||||
public override CommandType CommandType { get; set; }
|
||||
public override UpdateRowSource UpdatedRowSource { get; set; }
|
||||
protected override DbConnection DbConnection { get; set; }
|
||||
protected override DbParameterCollection DbParameterCollection { get; }
|
||||
protected override DbTransaction DbTransaction { get; set; }
|
||||
public override bool DesignTimeVisible { get; set; }
|
||||
|
||||
protected override DbParameter CreateDbParameter()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
|
||||
{
|
||||
return new TestDbDataReader(Data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test mock class for SqlConnection wrapper
|
||||
/// </summary>
|
||||
public class TestSqlConnection : DbConnection
|
||||
{
|
||||
internal TestSqlConnection(TestResultSet[] data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
internal TestResultSet[] Data { get; set; }
|
||||
|
||||
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public override void Open()
|
||||
{
|
||||
// No Op, unless credentials are bad
|
||||
if(ConnectionString.Contains("invalidUsername"))
|
||||
{
|
||||
throw new Exception("Invalid credentials provided");
|
||||
}
|
||||
}
|
||||
|
||||
public override string ConnectionString { get; set; }
|
||||
public override string Database { get; }
|
||||
public override ConnectionState State { get; }
|
||||
public override string DataSource { get; }
|
||||
public override string ServerVersion { get; }
|
||||
|
||||
protected override DbCommand CreateDbCommand()
|
||||
{
|
||||
return new TestSqlCommand(Data);
|
||||
}
|
||||
|
||||
public override void ChangeDatabase(string databaseName)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test mock class for SqlConnection factory
|
||||
/// </summary>
|
||||
public class TestSqlConnectionFactory : ISqlConnectionFactory
|
||||
{
|
||||
public DbConnection CreateSqlConnection(string connectionString)
|
||||
{
|
||||
return new TestSqlConnection(null)
|
||||
{
|
||||
ConnectionString = connectionString
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public static class TestUtils
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Wait for a condition to be true for a limited amount of time.
|
||||
/// </summary>
|
||||
/// <param name="condition">Function that returns a boolean on a condition</param>
|
||||
/// <param name="intervalMilliseconds">Number of milliseconds to wait between test intervals.</param>
|
||||
/// <param name="intervalCount">Number of test intervals to perform before giving up.</param>
|
||||
/// <returns>True if the condition was met before the test interval limit.</returns>
|
||||
public static bool WaitFor(Func<bool> condition, int intervalMilliseconds = 10, int intervalCount = 200)
|
||||
{
|
||||
int count = 0;
|
||||
while (count++ < intervalCount && !condition.Invoke())
|
||||
{
|
||||
Thread.Sleep(intervalMilliseconds);
|
||||
}
|
||||
|
||||
return (count < intervalCount);
|
||||
}
|
||||
|
||||
|
||||
public static async Task RunAndVerify<T>(Func<RequestContext<T>, Task> test, Action<T> verify)
|
||||
{
|
||||
T result = default(T);
|
||||
var contextMock = RequestContextMocks.Create<T>(r => result = r).AddErrorHandling(null);
|
||||
await test(contextMock.Object);
|
||||
VerifyResult(contextMock, verify, result);
|
||||
}
|
||||
|
||||
public static void VerifyErrorSent<T>(Mock<RequestContext<T>> contextMock)
|
||||
{
|
||||
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Never);
|
||||
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
public static void VerifyResult<T, U>(Mock<RequestContext<T>> contextMock, U expected, U actual)
|
||||
{
|
||||
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
|
||||
Assert.Equal(expected, actual);
|
||||
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
public static void VerifyResult<T>(Mock<RequestContext<T>> contextMock, Action<T> verify, T actual)
|
||||
{
|
||||
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
|
||||
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
|
||||
verify(actual);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the TextUtilitiesTests class
|
||||
/// </summary>
|
||||
public class TextUtilitiesTests
|
||||
{
|
||||
[Fact]
|
||||
public void PositionOfCursorFirstLine()
|
||||
{
|
||||
string sql = "EXEC sys.fn_isrolemember ";
|
||||
|
||||
int prevNewLine;
|
||||
int cursorPosition = TextUtilities.PositionOfCursor(sql, 0, sql.Length, out prevNewLine);
|
||||
|
||||
Assert.Equal(prevNewLine, 0);
|
||||
Assert.Equal(cursorPosition, sql.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PositionOfCursorSecondLine()
|
||||
{
|
||||
string sql = "--lineone\nEXEC sys.fn_isrolemember ";
|
||||
|
||||
int prevNewLine;
|
||||
int cursorPosition = TextUtilities.PositionOfCursor(sql, 1, 15, out prevNewLine);
|
||||
|
||||
Assert.Equal(prevNewLine, 10);
|
||||
Assert.Equal(cursorPosition, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
{
|
||||
public class ValidateTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsWithinRangeTest()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Validate.IsWithinRange("parameterName", 1, 2, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsLessThanTest()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Validate.IsLessThan("parameterName", 2, 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNotEqualTest()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Validate.IsNotEqual<int>("parameterName", 1, 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNullOrWhiteSpaceTest()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Validate.IsNotNullOrWhitespaceString("parameterName", null));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user