3278 Kusto Unit Tests - Part 1 (#1057)

* 3278 Added unit tests in MetadataFactoryTests and Microsoft.Kusto.ServiceLayer.UnitTests project

* 3278 Removed todo and changed unit test to validate megabytes

* 3278 Added file and unit tests in AutoCompleteHelperTests.cs

* 3278 Removed unused functions from Kusto > ScriptAsScriptingOperation

* 3278 Added unit tests for DataSourceFactory

* 3278 Refactored AdminService to pass in ConnectionService rather than through instance variable. Added unit test for AdminServiceTests

* 3278 Refactored DataSourceFactory to not have static functions for future unit tests

* 3278 Re-added properties that were flagged as unused but are being used by ADS in ReliableDataSourceConnection.cs

* 3278 Re-added properties that were flagged as unused but are being used by ADS in ReliableDataSourceConnection.cs

* adding pipeline to execute tests (#1062)

* 3278 Converted GetDefaultAutoComplete and GetDefaultSemanticMarkers to static functions in DataSourceFactory. Removed unused constructor in ScriptFile. Added positive unit tests for both functions

* undoing release version bump

* adding additional configs

* adressing feedback

* Correcting path in csproj

Co-authored-by: Jorge Berumen <52225468+joberume@users.noreply.github.com>
Co-authored-by: joberume <jberumen3@miners.utep.edu>
This commit is contained in:
Justin M
2020-08-31 11:11:12 -07:00
committed by GitHub
parent 07700560a6
commit 1577177153
26 changed files with 565 additions and 379 deletions

View File

@@ -36,5 +36,7 @@ install:
script:
- dotnet build src/Microsoft.SqlTools.ServiceLayer
- dotnet test test/Microsoft.SqlTools.ServiceLayer.UnitTests
- dotnet build src/Microsoft.Kusto.ServiceLayer
- dotnet test test/Microsoft.Kusto.ServiceLayer.UnitTests
- dotnet build src/Microsoft.SqlTools.CoreServices
- dotnet test test/Microsoft.SqlTools.Hosting.UnitTests

View File

@@ -17,10 +17,12 @@ before_build:
build_script:
- dotnet build src/Microsoft.SqlTools.ServiceLayer
- dotnet build src/Microsoft.Kusto.ServiceLayer
- dotnet build src/Microsoft.SqlTools.CoreServices
test_script:
- dotnet test test/Microsoft.SqlTools.ServiceLayer.UnitTests
- dotnet test test/Microsoft.Kusto.ServiceLayer.UnitTests
- dotnet test test/Microsoft.SqlTools.Hosting.UnitTests
after_test:

View File

@@ -45,17 +45,35 @@ steps:
projects: '$(Build.SourcesDirectory)/src/Microsoft.SqlTools.ServiceLayer'
arguments: '--configfile $(Build.SourcesDirectory)/nuget.config'
- task: DotNetCoreCLI@1
displayName: 'dotnet restore src/Microsoft.Kusto.ServiceLayer'
inputs:
command: restore
projects: '$(Build.SourcesDirectory)/src/Microsoft.Kusto.ServiceLayer'
arguments: '--configfile $(Build.SourcesDirectory)/nuget.config'
- task: DotNetCoreCLI@2
displayName: 'dotnet build src/Microsoft.SqlTools.ServiceLayer'
inputs:
projects: '$(Build.SourcesDirectory)/src/Microsoft.SqlTools.ServiceLayer'
- task: DotNetCoreCLI@2
displayName: 'dotnet build src/Microsoft.Kusto.ServiceLayer'
inputs:
projects: '$(Build.SourcesDirectory)/src/Microsoft.Kusto.ServiceLayer'
- task: DotNetCoreCLI@2
displayName: 'dotnet build src/Microsoft.SqlTools.ServiceLayer --configuration Release'
inputs:
projects: '$(Build.SourcesDirectory)/src/Microsoft.SqlTools.ServiceLayer '
arguments: '--configuration Release'
- task: DotNetCoreCLI@2
displayName: 'dotnet build src/Microsoft.Kusto.ServiceLayer --configuration Release'
inputs:
projects: '$(Build.SourcesDirectory)/src/Microsoft.Kusto.ServiceLayer '
arguments: '--configuration Release'
- task: BatchScript@1
displayName: 'Run script build.cmd'
inputs:
@@ -69,6 +87,12 @@ steps:
command: restore
projects: test/Microsoft.SqlTools.ServiceLayer.UnitTests
- task: DotNetCoreCLI@1
displayName: 'dotnet restore test/Microsoft.Kusto.ServiceLayer.UnitTests'
inputs:
command: restore
projects: test/Microsoft.Kusto.ServiceLayer.UnitTests
- task: DotNetCoreCLI@1
displayName: 'dotnet test test/Microsoft.SqlTools.ServiceLayer.UnitTests'
inputs:
@@ -76,6 +100,13 @@ steps:
projects: test/Microsoft.SqlTools.ServiceLayer.UnitTests
arguments: '--logger "trx;LogFileName=xunit.trx"'
- task: DotNetCoreCLI@1
displayName: 'dotnet test test/Microsoft.Kusto.ServiceLayer.UnitTests'
inputs:
command: test
projects: test/Microsoft.Kusto.ServiceLayer.UnitTests
arguments: '--logger "trx;LogFileName=xunit.trx"'
- task: Npm@1
displayName: 'npm install -g gulp-cli'
inputs:
@@ -221,4 +252,4 @@ schedules:
displayName: Mon-Fri at 5:00AM
branches:
include:
- main
- main

View File

@@ -64,4 +64,4 @@ schedules:
displayName: Mon-Fri at 7:00UTC
branches:
include:
- main
- main

View File

@@ -10,15 +10,19 @@
"TestProjects": {
"Microsoft.SqlTools.ServiceLayer.UnitTests": [
"netcoreapp3.1"
]
],
"Microsoft.Kusto.ServiceLayer.UnitTests": [
"netcoreapp3.1"
]
},
"Frameworks": [
"Frameworks": [
"netcoreapp3.1"
],
"MainProjects": [
"MainProjects": [
"Microsoft.SqlTools.Credentials",
"Microsoft.SqlTools.ResourceProvider",
"Microsoft.SqlTools.ServiceLayer"
"Microsoft.SqlTools.ServiceLayer",
"Microsoft.Kusto.ServiceLayer"
],
"PackageProjects": [
"Microsoft.SqlTools.CoreServices",
@@ -26,4 +30,4 @@
"Microsoft.SqlTools.Hosting.Contracts",
"Microsoft.SqlTools.Hosting.v2"
]
}
}

View File

@@ -112,6 +112,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "azure-pipelines", "azure-pi
azure-pipelines\release.yml = azure-pipelines\release.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Kusto.ServiceLayer.UnitTests", "test\Microsoft.Kusto.ServiceLayer.UnitTests\Microsoft.Kusto.ServiceLayer.UnitTests.csproj", "{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -257,6 +259,12 @@ Global
{E0C941C8-91F2-4BE1-8B79-AC88EDB78729}.Integration|Any CPU.Build.0 = Debug|Any CPU
{E0C941C8-91F2-4BE1-8B79-AC88EDB78729}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0C941C8-91F2-4BE1-8B79-AC88EDB78729}.Release|Any CPU.Build.0 = Release|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Integration|Any CPU.Build.0 = Debug|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -287,6 +295,7 @@ Global
{D3696EFA-FB1E-4848-A726-FF7B168AFB96} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
{E0C941C8-91F2-4BE1-8B79-AC88EDB78729} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
{AFCDED82-B659-4BE1-86ED-0F4F8BC661AE} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B31CDF4B-2851-45E5-8C5F-BE97125D9DD8}

View File

@@ -21,50 +21,28 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
/// </summary>
public class AdminService
{
private static readonly Lazy<AdminService> instance = new Lazy<AdminService>(() => new AdminService());
private static readonly Lazy<AdminService> _instance = new Lazy<AdminService>(() => new AdminService());
private static ConnectionService connectionService = null;
/// <summary>
/// Internal for testing purposes only
/// </summary>
internal static ConnectionService ConnectionServiceInstance
{
get
{
if (AdminService.connectionService == null)
{
AdminService.connectionService = ConnectionService.Instance;
}
return AdminService.connectionService;
}
set
{
AdminService.connectionService = value;
}
}
private static ConnectionService _connectionService;
/// <summary>
/// Gets the singleton instance object
/// </summary>
public static AdminService Instance
{
get { return instance.Value; }
}
public static AdminService Instance => _instance.Value;
/// <summary>
/// Initializes the service instance
/// </summary>
public void InitializeService(ServiceHost serviceHost)
public void InitializeService(ServiceHost serviceHost, ConnectionService connectionService)
{
serviceHost.SetRequestHandler(GetDatabaseInfoRequest.Type, HandleGetDatabaseInfoRequest);
_connectionService = connectionService;
}
/// <summary>
/// Handle get database info request
/// </summary>
internal async Task HandleGetDatabaseInfoRequest(
private async Task HandleGetDatabaseInfoRequest(
GetDatabaseInfoParams databaseParams,
RequestContext<GetDatabaseInfoResponse> requestContext)
{
@@ -72,10 +50,7 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
{
Func<Task> requestHandler = async () =>
{
ConnectionInfo connInfo;
AdminService.ConnectionServiceInstance.TryFindConnection(
databaseParams.OwnerUri,
out connInfo);
_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo);
DatabaseInfo info = null;
if (connInfo != null)
@@ -100,29 +75,31 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
await requestContext.SendError(ex.ToString());
}
}
/// <summary>
/// Return database info for a specific database
/// </summary>
/// <param name="connInfo"></param>
/// <returns></returns>
internal DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
public DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
{
if(!string.IsNullOrEmpty(connInfo.ConnectionDetails.DatabaseName)){
ReliableDataSourceConnection connection;
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
List<DataSourceObjectMetadata> metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
return databaseInfo.ElementAtOrDefault(0);
if (string.IsNullOrEmpty(connInfo.ConnectionDetails.DatabaseName))
{
return null;
}
ReliableDataSourceConnection connection;
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
DataSourceObjectMetadata objectMetadata =
MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
return null;
List<DataSourceObjectMetadata> metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
return databaseInfo.ElementAtOrDefault(0);
}
}
}

View File

@@ -59,6 +59,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
private IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Map from script URIs to ConnectionInfo objects
/// This is internal for testing access only
@@ -397,7 +399,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
DataSourceObjectMetadata clusterMetadata = MetadataFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
DiagnosticsInfo clusterDiagnostics = dataSource.GetDiagnostics(clusterMetadata);
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerinfoFormat(DataSourceType.Kusto, clusterDiagnostics);
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerInfoFormat(DataSourceType.Kusto, clusterDiagnostics);
response.ServerInfo = new ServerInfo
{
@@ -789,10 +791,11 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
}
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
IConnectedBindingQueue connectedBindingQueue)
IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
{
ServiceHost = serviceHost;
_dataSourceConnectionFactory = dataSourceConnectionFactory;
_dataSourceFactory = dataSourceFactory;
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
LockedDatabaseManager.ConnectionService = this;
@@ -1411,7 +1414,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
string connectionString = BuildConnectionString(connInfo.ConnectionDetails);
// TODOKusto: Pass in type of DataSource needed to make this generic. Hard coded to Kusto right now.
return DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
return _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
}
catch (Exception ex)
{

View File

@@ -17,6 +17,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
[Export(typeof(IDataSourceConnectionFactory))]
public class DataSourceConnectionFactory : IDataSourceConnectionFactory
{
private readonly IDataSourceFactory _dataSourceFactory;
[ImportingConstructor]
public DataSourceConnectionFactory(IDataSourceFactory dataSourceFactory)
{
_dataSourceFactory = dataSourceFactory;
}
/// <summary>
/// Creates a new SqlConnection object
/// </summary>
@@ -24,7 +32,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
RetryPolicy connectionRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
RetryPolicy commandRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
return new ReliableDataSourceConnection(connectionString, connectionRetryPolicy, commandRetryPolicy, azureAccountToken);
return new ReliableDataSourceConnection(connectionString, connectionRetryPolicy, commandRetryPolicy, azureAccountToken, _dataSourceFactory);
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense;
@@ -9,9 +10,10 @@ using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion;
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
public class DataSourceFactory
[Export(typeof(IDataSourceFactory))]
public class DataSourceFactory : IDataSourceFactory
{
public static IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken)
public IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken)
{
ValidationUtils.IsArgumentNotNullOrWhiteSpace(connectionString, nameof(connectionString));
ValidationUtils.IsArgumentNotNullOrWhiteSpace(azureAccountToken, nameof(azureAccountToken));
@@ -57,15 +59,16 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
}
}
public static ReliableConnectionHelper.ServerInfo ConvertToServerinfoFormat(DataSourceType dataSourceType, DiagnosticsInfo clusterDiagnostics)
public static ReliableConnectionHelper.ServerInfo ConvertToServerInfoFormat(DataSourceType dataSourceType, DiagnosticsInfo clusterDiagnostics)
{
switch (dataSourceType)
{
case DataSourceType.Kusto:
{
ReliableConnectionHelper.ServerInfo serverInfo = new ReliableConnectionHelper.ServerInfo();
serverInfo.Options = new Dictionary<string, object>(clusterDiagnostics.Options);
return serverInfo;
return new ReliableConnectionHelper.ServerInfo
{
Options = new Dictionary<string, object>(clusterDiagnostics.Options)
};
}
default:

View File

@@ -0,0 +1,7 @@
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
public interface IDataSourceFactory
{
IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken);
}
}

View File

@@ -43,6 +43,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
private readonly string _connectionString;
private readonly string _azureAccountToken;
private readonly IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Initializes a new instance of the ReliableKustoClient class with a given connection string
@@ -52,11 +53,15 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// <param name="connectionString">The connection string used to open the SQL Azure database.</param>
/// <param name="connectionRetryPolicy">The retry policy defining whether to retry a request if a connection fails to be established.</param>
/// <param name="commandRetryPolicy">The retry policy defining whether to retry a request if a command fails to be executed.</param>
public ReliableDataSourceConnection(string connectionString, RetryPolicy connectionRetryPolicy, RetryPolicy commandRetryPolicy, string azureAccountToken)
/// <param name="azureAccountToken"></param>
/// <param name="dataSourceFactory"></param>
public ReliableDataSourceConnection(string connectionString, RetryPolicy connectionRetryPolicy,
RetryPolicy commandRetryPolicy, string azureAccountToken, IDataSourceFactory dataSourceFactory)
{
_connectionString = connectionString;
_azureAccountToken = azureAccountToken;
_dataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccountToken);
_dataSourceFactory = dataSourceFactory;
_dataSource = dataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccountToken);
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
@@ -112,42 +117,45 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Gets the policy which decides whether to retry a connection request, based on how many
/// times the request has been made and the reason for the last failure.
/// <summary>
/// Gets the policy which decides whether to retry a connection request, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
// ReSharper disable once UnusedMember.Global
public RetryPolicy ConnectionRetryPolicy
{
get { return _connectionRetryPolicy; }
}
{
get { return _connectionRetryPolicy; }
}
/// <summary>
/// Gets the policy which decides whether to retry a command, based on how many
/// times the request has been made and the reason for the last failure.
/// <summary>
/// Gets the policy which decides whether to retry a command, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
public RetryPolicy CommandRetryPolicy
{
get { return _commandRetryPolicy; }
set
{
Validate.IsNotNull(nameof(value), value);
// ReSharper disable once UnusedMember.Global
public RetryPolicy CommandRetryPolicy
{
get { return _commandRetryPolicy; }
set
{
Validate.IsNotNull(nameof(value), value);
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
_commandRetryPolicy = value;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
}
_commandRetryPolicy = value;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
}
/// <summary>
/// Gets the server name from the underlying connection.
/// </summary>
public string ClusterName
{
get { return _dataSource.ClusterName; }
/// <summary>
/// Gets the server name from the underlying connection.
/// </summary>
// ReSharper disable once UnusedMember.Global
public string ClusterName
{
get { return _dataSource.ClusterName; }
}
/// <summary>
@@ -182,7 +190,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
_connectionRetryPolicy.ExecuteAction(() =>
{
_dataSource = DataSourceFactory.Create(DataSourceType.Kusto, _connectionString, _azureAccountToken);
_dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, _connectionString, _azureAccountToken);
});
}
}
@@ -219,14 +227,15 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
public void Close()
{
}
/// <summary>
/// Gets the time to wait while trying to establish a connection before terminating
/// the attempt and generating an error.
/// </summary>
/// <summary>
/// Gets the time to wait while trying to establish a connection before terminating
/// the attempt and generating an error.
/// </summary>
// ReSharper disable once UnusedMember.Global
public int ConnectionTimeout
{
get { return 30; }
{
get { return 30; }
}
/// <summary>
@@ -237,14 +246,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
get { return _dataSource.DatabaseName; }
}
private void VerifyConnectionOpen(ReliableDataSourceConnection conn)
{
if(conn.GetUnderlyingConnection() == null)
{
conn.Open();
}
}
}
}

View File

@@ -71,6 +71,7 @@ namespace Microsoft.Kusto.ServiceLayer
var scripter = serviceProvider.GetService<IScripter>();
var dataSourceConnectionFactory = serviceProvider.GetService<IDataSourceConnectionFactory>();
var connectedBindingQueue = serviceProvider.GetService<IConnectedBindingQueue>();
var dataSourceFactory = serviceProvider.GetService<IDataSourceFactory>();
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
@@ -78,10 +79,10 @@ namespace Microsoft.Kusto.ServiceLayer
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(WorkspaceService<SqlToolsSettings>.Instance);
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue);
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue, dataSourceFactory);
serviceProvider.RegisterSingleService(LanguageService.Instance);
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue);
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue, dataSourceFactory);
serviceProvider.RegisterSingleService(ConnectionService.Instance);
CredentialService.Instance.InitializeService(serviceHost);
@@ -90,10 +91,10 @@ namespace Microsoft.Kusto.ServiceLayer
QueryExecutionService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(QueryExecutionService.Instance);
ScriptingService.Instance.InitializeService(serviceHost, scripter);
ScriptingService.Instance.InitializeService(serviceHost, scripter, dataSourceFactory);
serviceProvider.RegisterSingleService(ScriptingService.Instance);
AdminService.Instance.InitializeService(serviceHost);
AdminService.Instance.InitializeService(serviceHost, ConnectionService.Instance);
serviceProvider.RegisterSingleService(AdminService.Instance);
MetadataService.Instance.InitializeService(serviceHost);

View File

@@ -24,6 +24,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
internal const int DefaultBindingTimeout = 500;
private readonly ISqlConnectionOpener _connectionOpener;
private readonly IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Gets the current settings
@@ -34,9 +35,10 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
}
[ImportingConstructor]
public ConnectedBindingQueue(ISqlConnectionOpener sqlConnectionOpener)
public ConnectedBindingQueue(ISqlConnectionOpener sqlConnectionOpener, IDataSourceFactory dataSourceFactory)
{
_connectionOpener = sqlConnectionOpener;
_dataSourceFactory = dataSourceFactory;
}
/// <summary>
@@ -160,7 +162,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
bindingContext.ServerConnection = _connectionOpener.OpenServerConnection(connInfo, featureName);
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
bindingContext.DataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
bindingContext.DataSource = _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
if (needMetadata)
{

View File

@@ -209,7 +209,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
/// <param name="context"></param>
/// <param name="dataSourceFactory"></param>
/// <param name="connectedBindingQueue"></param>
public void InitializeService(ServiceHost serviceHost, IConnectedBindingQueue connectedBindingQueue)
public void InitializeService(ServiceHost serviceHost, IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
{
_bindingQueue = connectedBindingQueue;
// Register the requests that this service will handle
@@ -854,7 +854,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
if (scriptParseInfo == null)
{
var scriptDocInfo = ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile);
resultCompletionItems = resultCompletionItems = DataSourceFactory.GetDefaultAutoComplete(DataSourceType.Kusto, scriptDocInfo, textDocumentPosition.Position); //TODO_KUSTO: DataSourceFactory.GetDefaultAutoComplete 1st param should get the datasource type generically instead of hard coded DataSourceType.Kusto
resultCompletionItems = DataSourceFactory.GetDefaultAutoComplete(DataSourceType.Kusto, scriptDocInfo, textDocumentPosition.Position); //TODO_KUSTO: DataSourceFactory.GetDefaultAutoComplete 1st param should get the datasource type generically instead of hard coded DataSourceType.Kusto
return resultCompletionItems;
}

View File

@@ -11,11 +11,7 @@ using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using System.Collections.Specialized;
using System.Text;
using System.Globalization;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using System.Diagnostics;
@@ -28,19 +24,10 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
{
private readonly IScripter _scripter;
private static readonly Dictionary<string, SqlServerVersion> scriptCompatibilityMap = LoadScriptCompatibilityMap();
/// <summary>
/// Left delimiter for an named object
/// </summary>
public const char LeftDelimiter = '[';
/// <summary>
/// right delimiter for a named object
/// </summary>
public const char RightDelimiter = ']';
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken, IScripter scripter) : base(parameters)
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken, IScripter scripter, IDataSourceFactory dataSourceFactory) : base(parameters, dataSourceFactory)
{
DataSource = DataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString,
DataSource = _dataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString,
azureAccountToken);
_scripter = scripter;
}
@@ -177,200 +164,6 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
}
/// <summary>
/// Generate a schema qualified name (e.g. [schema].[objectName]) for an object if the option for SchemaQualify is true
/// </summary>
/// <param name="schema">The schema name. May be null or empty in which case it will be ignored</param>
/// <param name="objectName">The object name.</param>
/// <param name="schemaQualify">Whether to schema qualify the object or not</param>
/// <returns>The object name, quoted as appropriate and schema-qualified if the option is set</returns>
private static string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
{
var qualifiedName = new StringBuilder();
if (schemaQualify && !String.IsNullOrEmpty(schema))
{
// schema.name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}.{1}", GetDelimitedString(schema), GetDelimitedString(objectName));
}
else
{
// name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}", GetDelimitedString(objectName));
}
return qualifiedName.ToString();
}
/// <summary>
/// getting delimited string
/// </summary>
/// <param name="str">string</param>
/// <returns>string</returns>
static private string GetDelimitedString(string str)
{
if (string.IsNullOrEmpty(str))
{
return String.Empty;
}
else
{
StringBuilder qualifiedName = new StringBuilder();
qualifiedName.AppendFormat("{0}{1}{2}",
LeftDelimiter,
QuoteObjectName(str),
RightDelimiter);
return qualifiedName.ToString();
}
}
/// <summary>
/// turn a smo datatype object into a type that can be inserted into tsql, e.g. nvarchar(20)
/// </summary>
/// <param name="type"></param>
/// <param name="options"></param>
/// <returns></returns>
internal static string GetDatatype(DataType type, ScriptingOptions options)
{
// string we'll return.
string rv = string.Empty;
string dataType = type.Name;
switch (type.SqlDataType)
{
// char, nchar, nchar, nvarchar, varbinary, nvarbinary are all displayed as type(length)
// length of -1 is taken to be type(max). max isn't localizable.
case SqlDataType.Char:
case SqlDataType.NChar:
case SqlDataType.VarChar:
case SqlDataType.NVarChar:
case SqlDataType.Binary:
case SqlDataType.VarBinary:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.MaximumLength);
break;
case SqlDataType.VarCharMax:
case SqlDataType.NVarCharMax:
case SqlDataType.VarBinaryMax:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}(max)",
dataType);
break;
// numeric and decimal are displayed as type precision,scale
case SqlDataType.Numeric:
case SqlDataType.Decimal:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1},{2})",
dataType,
type.NumericPrecision,
type.NumericScale);
break;
//time, datetimeoffset and datetime2 are displayed as type scale
case SqlDataType.Time:
case SqlDataType.DateTimeOffset:
case SqlDataType.DateTime2:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.NumericScale);
break;
// anything else is just type.
case SqlDataType.Xml:
if (type.Schema != null && type.Schema.Length > 0 && dataType != null && dataType.Length > 0)
{
rv = String.Format(CultureInfo.InvariantCulture
, "xml ({0}{2}{1}.{0}{3}{1})"
, LeftDelimiter
, RightDelimiter
, QuoteObjectName(type.Schema)
, QuoteObjectName(dataType));
}
else
{
rv = "xml";
}
break;
case SqlDataType.UserDefinedDataType:
case SqlDataType.UserDefinedTableType:
case SqlDataType.UserDefinedType:
//User defined types may be in a non-DBO schema so append it if necessary
rv = GenerateSchemaQualifiedName(type.Schema, dataType, options.SchemaQualify);
break;
default:
rv = dataType;
break;
}
return rv;
}
/// <summary>
/// Double quotes certain characters in object name
/// </summary>
/// <param name="sqlObject"></param>
public static string QuoteObjectName(string sqlObject)
{
int len = sqlObject.Length;
StringBuilder result = new StringBuilder(sqlObject.Length);
for (int i = 0; i < len; i++)
{
if (sqlObject[i] == ']')
{
result.Append(']');
}
result.Append(sqlObject[i]);
}
return result.ToString();
}
private static void WriteUseDatabase(Database parentObject, StringBuilder stringBuilder , ScriptingOptions options)
{
if (options.IncludeDatabaseContext)
{
string useDb = string.Format(CultureInfo.InvariantCulture, "USE {0}", CommonConstants.DefaultBatchSeperator);
if (!options.NoCommandTerminator)
{
stringBuilder.Append(useDb);
}
else
{
stringBuilder.Append(useDb);
stringBuilder.Append(Environment.NewLine);
}
}
}
private string GetScript(ScriptingOptions options, StringCollection stringCollection)
{
StringBuilder sb = new StringBuilder();
foreach (var item in stringCollection)
{
sb.Append(item);
if (options != null && !options.NoCommandTerminator)
{
//Ensure the batch separator is always on a new line (to avoid syntax errors)
//but don't write an extra if we already have one as this can affect definitions
//of objects such as Stored Procedures (see TFS#9125366)
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}{2}",
item.EndsWith(Environment.NewLine) ? string.Empty : Environment.NewLine,
CommonConstants.DefaultBatchSeperator,
Environment.NewLine);
}
else
{
sb.AppendFormat(CultureInfo.InvariantCulture, Environment.NewLine);
}
}
return sb.ToString();
}
private UrnCollection CreateUrns(IDataSource dataSource)
{
IEnumerable<ScriptingObject> selectedObjects = new List<ScriptingObject>(this.Parameters.ScriptingObjects);
@@ -533,27 +326,5 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
scriptingOptions.AnsiPadding = true;
}
}
private void ScripterScriptingError(object sender, ScriptingErrorEventArgs e)
{
this.CancellationToken.ThrowIfCancellationRequested();
Logger.Write(
TraceEventType.Verbose,
string.Format(
"Sending scripting error progress event, Urn={0}, OperationId={1}, Completed={2}, Error={3}",
e.Current,
this.OperationId,
false,
e?.InnerException?.ToString() ?? "null"));
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
{
ScriptingObject = e.Current?.ToScriptingObject(),
Status = "Failed",
ErrorMessage = e?.InnerException?.Message,
ErrorDetails = e?.InnerException?.ToString(),
});
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
private string azureAccessToken;
public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken) : base(parameters)
public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken, IDataSourceFactory dataSourceFactory) : base(parameters, dataSourceFactory)
{
this.azureAccessToken = azureAccessToken;
}

View File

@@ -36,6 +36,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
private bool disposed;
private IScripter _scripter;
private IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Internal for testing purposes only
@@ -66,9 +67,10 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// </summary>
/// <param name="serviceHost"></param>
/// <param name="context"></param>
public void InitializeService(ServiceHost serviceHost, IScripter scripter)
public void InitializeService(ServiceHost serviceHost, IScripter scripter, IDataSourceFactory dataSourceFactory)
{
_scripter = scripter;
_dataSourceFactory = dataSourceFactory;
serviceHost.SetRequestHandler(ScriptingRequest.Type, this.HandleScriptExecuteRequest);
serviceHost.SetRequestHandler(ScriptingCancelRequest.Type, this.HandleScriptCancelRequest);
serviceHost.SetRequestHandler(ScriptingListObjectsRequest.Type, this.HandleListObjectsRequest);
@@ -131,11 +133,11 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
if (!ShouldCreateScriptAsOperation(parameters))
{
operation = new ScriptingScriptOperation(parameters, accessToken);
operation = new ScriptingScriptOperation(parameters, accessToken, _dataSourceFactory);
}
else
{
operation = new ScriptAsScriptingOperation(parameters, accessToken, _scripter);
operation = new ScriptAsScriptingOperation(parameters, accessToken, _scripter, _dataSourceFactory);
}
operation.PlanNotification += (sender, e) => requestContext.SendEvent(ScriptingPlanNotificationEvent.Type, e).Wait();

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Management.Common;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlTools.Utility;
@@ -22,10 +20,12 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// </summary>
public abstract class SmoScriptingOperation : ScriptingOperation
{
protected readonly IDataSourceFactory _dataSourceFactory;
private bool _disposed;
public SmoScriptingOperation(ScriptingParams parameters)
protected SmoScriptingOperation(ScriptingParams parameters, IDataSourceFactory dataSourceFactory)
{
_dataSourceFactory = dataSourceFactory;
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
@@ -77,7 +77,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
{
string serverName = string.Empty;
using(var dataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccessToken))
using(var dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccessToken))
{
serverName = dataSource.ClusterName;
}

View File

@@ -98,14 +98,6 @@ namespace Microsoft.Kusto.ServiceLayer.Workspace.Contracts
#region Constructors
/// <summary>
/// Add a default constructor for testing
/// </summary>
public ScriptFile()
{
ClientUri = "test.sql";
}
/// <summary>
/// Creates a new ScriptFile instance by reading file contents from
/// the given TextReader.

View File

@@ -33,6 +33,7 @@
<ProjectReference Include="../Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
<ProjectReference Include="../Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
<ProjectReference Include="../Microsoft.SqlTools.ManagedBatchParser/Microsoft.SqlTools.ManagedBatchParser.csproj" />
<ProjectReference Include="../Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgMicrosoft_SqlServer_DacFx)\lib\netstandard2.0\Microsoft.Data.Tools.Schema.SqlTasks.targets">
@@ -48,20 +49,10 @@
<!-- this target enables dependency files to be copied as part of the output of ProjectReference.
https://github.com/dotnet/sdk/issues/1675
-->
<Target Name="AddRuntimeDependenciesToContent"
Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'"
BeforeTargets="GetCopyToOutputDirectoryItems"
DependsOnTargets="GenerateBuildDependencyFile;
GenerateBuildRuntimeConfigurationFiles">
<Target Name="AddRuntimeDependenciesToContent" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;&#xD;&#xA; GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)"
Condition="'$(GenerateDependencyFile)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)"
Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectRuntimeConfigFileName)" />
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" Condition="'$(GenerateDependencyFile)' == 'true'" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" />
</ItemGroup>
</Target>
</Project>

View File

@@ -0,0 +1,27 @@
using Microsoft.Kusto.ServiceLayer.Admin;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
using Moq;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.Admin.Contracts
{
public class AdminServiceTests
{
[TestCase(null)]
[TestCase("")]
public void GetDatabaseInfo_Returns_Null_For_Invalid_DatabaseName(string databaseName)
{
var dataSourceConnectionFactory = new Mock<IDataSourceConnectionFactory>();
var connectionDetails = new ConnectionDetails
{
DatabaseName = databaseName
};
var connectionInfo = new ConnectionInfo(dataSourceConnectionFactory.Object, "", connectionDetails);
var adminService = new AdminService();
var databaseInfo = adminService.GetDatabaseInfo(connectionInfo);
Assert.IsNull(databaseInfo);
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
{
public class DataSourceFactoryTests
{
[TestCase(typeof(ArgumentNullException), "", "AzureAccountToken")]
[TestCase(typeof(ArgumentNullException), "ConnectionString", "")]
[TestCase(typeof(ArgumentException), "ConnectionString", "AzureAccountToken")]
public void Create_Throws_Exceptions_For_InvalidParams(Type exceptionType,
string connectionString,
string azureAccountToken)
{
var dataSourceFactory = new DataSourceFactory();
Assert.Throws(exceptionType,
() => dataSourceFactory.Create(DataSourceType.None, connectionString, azureAccountToken));
}
[Test]
public void GetDefaultAutoComplete_Throws_ArgumentException_For_InvalidDataSourceType()
{
Assert.Throws<ArgumentException>(() =>
DataSourceFactory.GetDefaultAutoComplete(DataSourceType.None, null, null));
}
[Test]
public void GetDefaultAutoComplete_Returns_CompletionItems()
{
var textDocumentPosition = new TextDocumentPosition
{
Position = new Position()
};
var scriptFile = new ScriptFile("", "", "");
var scriptParseInfo = new ScriptParseInfo();
var documentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);
var position = new Position();
var completionItems = DataSourceFactory.GetDefaultAutoComplete(DataSourceType.Kusto, documentInfo, position);
Assert.AreNotEqual(0, completionItems.Length);
}
[Test]
public void GetDefaultSemanticMarkers_Throws_ArgumentException_For_InvalidDataSourceType()
{
Assert.Throws<ArgumentException>(() =>
DataSourceFactory.GetDefaultSemanticMarkers(DataSourceType.None, null, null, null));
}
[Test]
public void GetDefaultSemanticMarkers_Returns_ScriptFileMarker()
{
var parseInfo = new ScriptParseInfo();
var file = new ScriptFile("", "", "");
var queryText = ".show databases";
var semanticMarkers = DataSourceFactory.GetDefaultSemanticMarkers(DataSourceType.Kusto, parseInfo, file, queryText);
Assert.AreNotEqual(0, semanticMarkers.Length);
}
[Test]
public void ConvertToServerInfoFormat_Throws_ArgumentException_For_InvalidDataSourceType()
{
Assert.Throws<ArgumentException>(() =>
DataSourceFactory.ConvertToServerInfoFormat(DataSourceType.None, null));
}
[Test]
public void ConvertToServerInfoFormat_Returns_ServerInfo_With_Options()
{
var diagnosticsInfo = new DiagnosticsInfo
{
Options = new Dictionary<string, object>
{
{"Key", "Object"}
}
};
var serverInfo = DataSourceFactory.ConvertToServerInfoFormat(DataSourceType.Kusto, diagnosticsInfo);
Assert.IsNotNull(serverInfo.Options);
Assert.AreEqual(diagnosticsInfo.Options["Key"], serverInfo.Options["Key"]);
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource.Metadata
{
public class MetadataFactoryTests
{
[Test]
public void CreateClusterMetadata_ThrowsNullException_For_NullClusterName()
{
Assert.Throws<ArgumentNullException>(() => MetadataFactory.CreateClusterMetadata(null));
}
[Test]
[TestCase("")]
[TestCase(null)]
[TestCase(" ")]
public void CreateDatabaseMetadata_ThrowsNullException_For_InvalidDatabaseName(string databaseName)
{
var testMetadata = new DataSourceObjectMetadata
{
MetadataType = DataSourceMetadataType.Cluster
};
Assert.Throws<ArgumentNullException>(() => MetadataFactory.CreateDatabaseMetadata(testMetadata, databaseName));
}
[Test]
public void CreateDatabaseMetadata_ThrowsNullException_For_InvalidMetadataType()
{
var testMetadata = new DataSourceObjectMetadata
{
MetadataType = DataSourceMetadataType.Database
};
Assert.Throws<ArgumentException>(() => MetadataFactory.CreateDatabaseMetadata(testMetadata, "FakeDatabaseName"));
}
[Test]
public void CreateFolderMetadata_ThrowsNullException_For_NullMetadata()
{
Assert.Throws<InvalidOperationException>(() => MetadataFactory.CreateFolderMetadata(null, "", ""));
}
[Test]
public void CreateClusterMetadata_Returns_DataSourceObjectMetadata()
{
string clusterName = "FakeClusterName";
var objectMetadata = MetadataFactory.CreateClusterMetadata(clusterName);
Assert.AreEqual(DataSourceMetadataType.Cluster, objectMetadata.MetadataType);
Assert.AreEqual(DataSourceMetadataType.Cluster.ToString(), objectMetadata.MetadataTypeName);
Assert.AreEqual(clusterName, objectMetadata.Name);
Assert.AreEqual(clusterName, objectMetadata.PrettyName);
Assert.AreEqual(clusterName, objectMetadata.Urn);
}
[Test]
public void CreateDatabaseMetadata_Returns_DataSourceObjectMetadata()
{
string databaseName = "FakeDatabaseName";
var clusterMetadata = new DataSourceObjectMetadata
{
MetadataType = DataSourceMetadataType.Cluster,
Name = "FakeClusterName",
Urn = "FakeClusterName"
};
var objectMetadata = MetadataFactory.CreateDatabaseMetadata(clusterMetadata, databaseName);
Assert.AreEqual(DataSourceMetadataType.Database, objectMetadata.MetadataType);
Assert.AreEqual(DataSourceMetadataType.Database.ToString(), objectMetadata.MetadataTypeName);
Assert.AreEqual(databaseName, objectMetadata.Name);
Assert.AreEqual(databaseName, objectMetadata.PrettyName);
Assert.AreEqual($"{clusterMetadata.Urn}.{databaseName}", objectMetadata.Urn);
}
[Test]
public void CreateFolderMetadata_Returns_FolderMetadata()
{
string path = "FakeCluster.FakeDatabase.FakeFolder";
string name = "FakeFolderName";
var parentMetadata = new DataSourceObjectMetadata();
var objectMetadata = MetadataFactory.CreateFolderMetadata(parentMetadata, path, name);
Assert.AreEqual(DataSourceMetadataType.Folder, objectMetadata.MetadataType);
Assert.AreEqual(DataSourceMetadataType.Folder.ToString(), objectMetadata.MetadataTypeName);
Assert.AreEqual(name, objectMetadata.Name);
Assert.AreEqual(name, objectMetadata.PrettyName);
Assert.AreEqual($"{path}.{name}", objectMetadata.Urn);
Assert.AreEqual(parentMetadata, objectMetadata.ParentMetadata);
}
[Test]
public void ConvertToDatabaseInfo_Returns_EmptyList_For_NonDatabaseMetadata()
{
var inputList = new List<DataSourceObjectMetadata>
{
new DataSourceObjectMetadata
{
Name = "FakeClusterName"
}
};
var databaseInfos = MetadataFactory.ConvertToDatabaseInfo(inputList);
Assert.AreEqual(0, databaseInfos.Count);
}
[Test]
public void ConvertToDatabaseInfo_Returns_DatabaseInfoList()
{
var databaseMetadata = new DatabaseMetadata
{
Name = "FakeDatabaseName",
SizeInMB = "2097152" // stored in bytes
};
var inputList = new List<DatabaseMetadata>
{
databaseMetadata
};
var databaseInfos = MetadataFactory.ConvertToDatabaseInfo(inputList);
Assert.AreEqual(1, databaseInfos.Count);
var databaseInfo = databaseInfos.Single();
Assert.AreEqual(databaseMetadata.Name, databaseInfo.Options["name"]);
Assert.AreEqual("2", databaseInfo.Options["sizeInMB"]);
}
[Test]
public void ConvertToObjectMetadata_Returns_ListObjectMetadata()
{
var databaseMetadata = new DataSourceObjectMetadata
{
PrettyName = "FakeDatabaseName",
MetadataTypeName = "Table"
};
var inputList = new List<DataSourceObjectMetadata>
{
databaseMetadata
};
var objectMetadatas = MetadataFactory.ConvertToObjectMetadata(inputList);
Assert.AreEqual(1, objectMetadatas.Count);
var objectMetadata = objectMetadatas.Single();
Assert.AreEqual(databaseMetadata.PrettyName, objectMetadata.Name);
Assert.AreEqual(databaseMetadata.MetadataTypeName, objectMetadata.MetadataTypeName);
Assert.AreEqual(MetadataType.Table, objectMetadata.MetadataType);
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Linq;
using Microsoft.Kusto.ServiceLayer.LanguageServices;
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices
{
public class AutoCompleteHelperTests
{
[Test]
public void CreateCompletionItem_Returns_CompletionItem()
{
string label = "";
string detail = "";
string insertText = "";
var itemKind = CompletionItemKind.Method;
int row = 1;
int startColumn = 2;
int endColumn = 3;
var completionItem = AutoCompleteHelper.CreateCompletionItem(label, detail, insertText, itemKind, row, startColumn, endColumn);
Assert.IsNotNull(completionItem);
Assert.AreEqual(label, completionItem.Label);
Assert.AreEqual(itemKind, completionItem.Kind);
Assert.AreEqual(detail, completionItem.Detail);
Assert.AreEqual(insertText, completionItem.InsertText);
Assert.IsNotNull(completionItem.TextEdit);
Assert.AreEqual(insertText, completionItem.TextEdit.NewText);
Assert.IsNotNull(completionItem.TextEdit.Range);
Assert.IsNotNull(completionItem.TextEdit.Range.Start);
Assert.AreEqual(row, completionItem.TextEdit.Range.Start.Line);
Assert.AreEqual(startColumn, completionItem.TextEdit.Range.Start.Character);
Assert.IsNotNull(completionItem.TextEdit.Range.End);
Assert.AreEqual(row, completionItem.TextEdit.Range.End.Line);
Assert.AreEqual(endColumn, completionItem.TextEdit.Range.End.Character);
}
[Test]
public void ConvertQuickInfoToHover_Returns_Null_For_Null_QuickInfoText()
{
var hover = AutoCompleteHelper.ConvertQuickInfoToHover(null, "", 0, 0, 0);
Assert.IsNull(hover);
}
[Test]
public void ConvertQuickInfoToHover_Returns_Hover()
{
string quickInfoText = "";
string language = "";
int row = 0;
int startColumn = 0;
int endColumn = 0;
var hover = AutoCompleteHelper.ConvertQuickInfoToHover(quickInfoText, language, row, startColumn, endColumn);
Assert.IsNotNull(hover);
Assert.AreEqual(1, hover.Contents.Length);
var content = hover.Contents.First();
Assert.AreEqual(language, content.Language);
Assert.AreEqual(quickInfoText, content.Value);
Assert.IsNotNull(hover.Range);
Assert.IsNotNull(hover.Range.Value.Start);
Assert.AreEqual(row, hover.Range.Value.Start.Line);
Assert.AreEqual(startColumn, hover.Range.Value.Start.Character);
Assert.IsNotNull(hover.Range.Value.End);
Assert.AreEqual(row, hover.Range.Value.End.Line);
Assert.AreEqual(endColumn, hover.Range.Value.End.Character);
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="nunit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Kusto.ServiceLayer\Microsoft.Kusto.ServiceLayer.csproj" />
</ItemGroup>
</Project>