diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionInfo.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionInfo.cs index fdaa7381..576c7b27 100644 --- a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionInfo.cs +++ b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionInfo.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Data.Common; using Microsoft.Kusto.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.Utility; @@ -27,6 +26,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection ConnectionDetails = details; ConnectionId = Guid.NewGuid(); IntellisenseMetrics = new InteractionMetrics(new int[] {50, 100, 200, 500, 1000, 2000}); + _connectionTypeToConnectionMap = new ConcurrentDictionary(); } /// @@ -54,13 +54,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection /// this ConnectionInfo's OwnerUri. /// This is internal for testing access only /// - internal readonly ConcurrentDictionary ConnectionTypeToConnectionMap = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _connectionTypeToConnectionMap; /// /// Intellisense Metrics /// - public InteractionMetrics IntellisenseMetrics { get; private set; } + public InteractionMetrics IntellisenseMetrics { get; } /// /// Returns true if the db connection is to any cloud instance @@ -79,7 +78,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection { get { - return ConnectionTypeToConnectionMap.Values; + return _connectionTypeToConnectionMap.Values; } } @@ -91,14 +90,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection { get { - return ConnectionTypeToConnectionMap.Keys; + return _connectionTypeToConnectionMap.Keys; } } public bool HasConnectionType(string connectionType) { connectionType = connectionType ?? ConnectionType.Default; - return ConnectionTypeToConnectionMap.ContainsKey(connectionType); + return _connectionTypeToConnectionMap.ContainsKey(connectionType); } /// @@ -108,7 +107,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection { get { - return ConnectionTypeToConnectionMap.Count; + return _connectionTypeToConnectionMap.Count; } } @@ -121,7 +120,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection public bool TryGetConnection(string connectionType, out ReliableDataSourceConnection connection) { Validate.IsNotNullOrEmptyString("Connection Type", connectionType); - return ConnectionTypeToConnectionMap.TryGetValue(connectionType, out connection); + return _connectionTypeToConnectionMap.TryGetValue(connectionType, out connection); } /// @@ -133,7 +132,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection public void AddConnection(string connectionType, ReliableDataSourceConnection connection) { Validate.IsNotNullOrEmptyString("Connection Type", connectionType); - ConnectionTypeToConnectionMap.TryAdd(connectionType, connection); + _connectionTypeToConnectionMap.TryAdd(connectionType, connection); } /// @@ -144,7 +143,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection { Validate.IsNotNullOrEmptyString("Connection Type", connectionType); ReliableDataSourceConnection connection; - ConnectionTypeToConnectionMap.TryRemove(connectionType, out connection); + _connectionTypeToConnectionMap.TryRemove(connectionType, out connection); } /// @@ -155,7 +154,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection foreach (var type in AllConnectionTypes) { ReliableDataSourceConnection connection; - ConnectionTypeToConnectionMap.TryRemove(type, out connection); + _connectionTypeToConnectionMap.TryRemove(type, out connection); } } } diff --git a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs index fc98d834..b9221b0c 100644 --- a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs +++ b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs @@ -97,7 +97,7 @@ namespace Microsoft.Kusto.ServiceLayer AdminService.Instance.InitializeService(serviceHost, ConnectionService.Instance); serviceProvider.RegisterSingleService(AdminService.Instance); - MetadataService.Instance.InitializeService(serviceHost); + MetadataService.Instance.InitializeService(serviceHost, ConnectionService.Instance); serviceProvider.RegisterSingleService(MetadataService.Instance); InitializeHostedServices(serviceProvider, serviceHost); diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/BindingQueue.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/BindingQueue.cs index cf5635ed..c7fbb9c5 100644 --- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/BindingQueue.cs +++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/BindingQueue.cs @@ -22,19 +22,17 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// public class BindingQueue : IDisposable where T : IBindingContext, new() { - internal const int QueueThreadStackSize = 5 * 1024 * 1024; + private CancellationTokenSource _processQueueCancelToken; - private CancellationTokenSource processQueueCancelToken = null; + private readonly ManualResetEvent _itemQueuedEvent; - private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false); + private readonly object _bindingQueueLock; - private object bindingQueueLock = new object(); + private readonly LinkedList _bindingQueue; - private LinkedList bindingQueue = new LinkedList(); + private readonly object _bindingContextLock ; - private object bindingContextLock = new object(); - - private Task queueProcessorTask; + private Task _queueProcessorTask; public delegate void UnhandledExceptionDelegate(string connectionKey, Exception ex); @@ -44,22 +42,27 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// Map from context keys to binding context instances /// Internal for testing purposes only /// - internal Dictionary BindingContextMap { get; set; } + internal Dictionary BindingContextMap { get; } - internal Dictionary BindingContextTasks { get; set; } = new Dictionary(); + private Dictionary BindingContextTasks { get; } /// /// Constructor for a binding queue instance /// - public BindingQueue() + internal BindingQueue() { - this.BindingContextMap = new Dictionary(); - this.StartQueueProcessor(); + BindingContextMap = new Dictionary(); + _itemQueuedEvent = new ManualResetEvent(initialState: false); + _bindingQueueLock = new object(); + _bindingQueue = new LinkedList(); + _bindingContextLock = new object(); + BindingContextTasks = new Dictionary(); + StartQueueProcessor(); } - public void StartQueueProcessor() + private void StartQueueProcessor() { - this.queueProcessorTask = StartQueueProcessorAsync(); + this._queueProcessorTask = StartQueueProcessorAsync(); } /// @@ -68,8 +71,8 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// public bool StopQueueProcessor(int timeout) { - this.processQueueCancelToken.Cancel(); - return this.queueProcessorTask.Wait(timeout); + this._processQueueCancelToken.Cancel(); + return this._queueProcessorTask.Wait(timeout); } /// @@ -80,14 +83,14 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices { get { - return this.processQueueCancelToken.IsCancellationRequested; + return this._processQueueCancelToken.IsCancellationRequested; } } /// /// Queue a binding request item /// - public virtual QueueItem QueueBindingOperation( + public QueueItem QueueBindingOperation( string key, Func bindOperation, Func timeoutOperation = null, @@ -111,12 +114,12 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices WaitForLockTimeout = waitForLockTimeout }; - lock (this.bindingQueueLock) + lock (this._bindingQueueLock) { - this.bindingQueue.AddLast(queueItem); + this._bindingQueue.AddLast(queueItem); } - this.itemQueuedEvent.Set(); + this._itemQueuedEvent.Set(); return queueItem; } @@ -127,7 +130,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// public bool IsBindingContextConnected(string key) { - lock (this.bindingContextLock) + lock (this._bindingContextLock) { IBindingContext context; if (this.BindingContextMap.TryGetValue(key, out context)) @@ -142,7 +145,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// Gets or creates a binding context for the provided context key /// /// - protected IBindingContext GetOrCreateBindingContext(string key) + internal IBindingContext GetOrCreateBindingContext(string key) { // use a default binding context for disconnected requests if (string.IsNullOrWhiteSpace(key)) @@ -150,7 +153,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices key = "disconnected_binding_context"; } - lock (this.bindingContextLock) + lock (this._bindingContextLock) { if (!this.BindingContextMap.ContainsKey(key)) { @@ -171,7 +174,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices keyPrefix = "disconnected_binding_context"; } - lock (this.bindingContextLock) + lock (this._bindingContextLock) { return this.BindingContextMap.Where(x => x.Key.StartsWith(keyPrefix)).Select(v => v.Value); } @@ -182,7 +185,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// protected bool BindingContextExists(string key) { - lock (this.bindingContextLock) + lock (this._bindingContextLock) { return this.BindingContextMap.ContainsKey(key); } @@ -193,7 +196,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// protected void RemoveBindingContext(string key) { - lock (this.bindingContextLock) + lock (this._bindingContextLock) { if (this.BindingContextMap.ContainsKey(key)) { @@ -216,13 +219,13 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices } } - public bool HasPendingQueueItems + private bool HasPendingQueueItems { get { - lock (this.bindingQueueLock) + lock (this._bindingQueueLock) { - return this.bindingQueue.Count > 0; + return this._bindingQueue.Count > 0; } } } @@ -232,15 +235,15 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// private QueueItem GetNextQueueItem() { - lock (this.bindingQueueLock) + lock (this._bindingQueueLock) { - if (this.bindingQueue.Count == 0) + if (this._bindingQueue.Count == 0) { return null; } - QueueItem queueItem = this.bindingQueue.First.Value; - this.bindingQueue.RemoveFirst(); + QueueItem queueItem = this._bindingQueue.First.Value; + this._bindingQueue.RemoveFirst(); return queueItem; } } @@ -250,15 +253,15 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// private Task StartQueueProcessorAsync() { - if (this.processQueueCancelToken != null) + if (this._processQueueCancelToken != null) { - this.processQueueCancelToken.Dispose(); + this._processQueueCancelToken.Dispose(); } - this.processQueueCancelToken = new CancellationTokenSource(); + this._processQueueCancelToken = new CancellationTokenSource(); return Task.Factory.StartNew( ProcessQueue, - this.processQueueCancelToken.Token, + this._processQueueCancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } @@ -266,13 +269,12 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// /// The core queue processing method /// - /// private void ProcessQueue() { - CancellationToken token = this.processQueueCancelToken.Token; + CancellationToken token = this._processQueueCancelToken.Token; WaitHandle[] waitHandles = new WaitHandle[2] { - this.itemQueuedEvent, + this._itemQueuedEvent, token.WaitHandle }; @@ -443,13 +445,13 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices } finally { - lock (this.bindingQueueLock) + lock (this._bindingQueueLock) { // verify the binding queue is still empty - if (this.bindingQueue.Count == 0) + if (this._bindingQueue.Count == 0) { // reset the item queued event since we've processed all the pending items - this.itemQueuedEvent.Reset(); + this._itemQueuedEvent.Reset(); } } } @@ -461,25 +463,25 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// public void ClearQueuedItems() { - lock (this.bindingQueueLock) + lock (this._bindingQueueLock) { - if (this.bindingQueue.Count > 0) + if (this._bindingQueue.Count > 0) { - this.bindingQueue.Clear(); + this._bindingQueue.Clear(); } } } public void Dispose() { - if (this.processQueueCancelToken != null) + if (this._processQueueCancelToken != null) { - this.processQueueCancelToken.Dispose(); + this._processQueueCancelToken.Dispose(); } - if (itemQueuedEvent != null) + if (_itemQueuedEvent != null) { - itemQueuedEvent.Dispose(); + _itemQueuedEvent.Dispose(); } if (this.BindingContextMap != null) diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/DiagnosticsHelper.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/DiagnosticsHelper.cs index 163c366f..d8d78f00 100644 --- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/DiagnosticsHelper.cs +++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/DiagnosticsHelper.cs @@ -77,7 +77,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// /// /// - internal static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker) + private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker) { return new Diagnostic { @@ -103,7 +103,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices /// Map ScriptFileMarker severity to Diagnostic severity /// /// - internal static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel) + private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel) { switch (markerLevel) { diff --git a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs index 61582a3b..e14aa695 100644 --- a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs +++ b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs @@ -24,35 +24,18 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata public static MetadataService Instance => LazyInstance.Value; - private static ConnectionService connectionService = null; - - /// - /// Internal for testing purposes only - /// - internal static ConnectionService ConnectionServiceInstance - { - get - { - if (connectionService == null) - { - connectionService = ConnectionService.Instance; - } - return connectionService; - } - - set - { - connectionService = value; - } - } + private static ConnectionService _connectionService; + + internal Task MetadataListTask { get; private set; } /// /// Initializes the Metadata Service instance /// /// - /// - public void InitializeService(IProtocolEndpoint serviceHost) + /// + public void InitializeService(IProtocolEndpoint serviceHost, ConnectionService connectionService) { + _connectionService = connectionService; serviceHost.SetRequestHandler(MetadataListRequest.Type, HandleMetadataListRequest); } @@ -68,7 +51,7 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata Func requestHandler = async () => { ConnectionInfo connInfo; - MetadataService.ConnectionServiceInstance.TryFindConnection(metadataParams.OwnerUri, out connInfo); + _connectionService.TryFindConnection(metadataParams.OwnerUri, out connInfo); var metadata = new List(); if (connInfo != null) @@ -102,6 +85,6 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata } } - internal Task MetadataListTask { get; set; } + } } diff --git a/src/Microsoft.Kusto.ServiceLayer/Properties/AssemblyInfo.cs b/src/Microsoft.Kusto.ServiceLayer/Properties/AssemblyInfo.cs index d46d1be4..48e37032 100644 --- a/src/Microsoft.Kusto.ServiceLayer/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Kusto.ServiceLayer/Properties/AssemblyInfo.cs @@ -41,10 +41,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0.0")] -[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.UnitTests")] -[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.IntegrationTests")] -[assembly: InternalsVisibleTo("Microsoft.SqlTools.ManagedBatchParser.UnitTests")] -[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.Test.Common")] +[assembly: InternalsVisibleTo("Microsoft.Kusto.ServiceLayer.UnitTests")] // Allowing internals visible access to Moq library to help testing [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionInfoTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionInfoTests.cs new file mode 100644 index 00000000..a1405c49 --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionInfoTests.cs @@ -0,0 +1,89 @@ +using System; +using Microsoft.Kusto.ServiceLayer.Connection; +using Microsoft.Kusto.ServiceLayer.Connection.Contracts; +using Microsoft.Kusto.ServiceLayer.DataSource; +using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.Connection +{ + public class ConnectionInfoTests + { + [TestCase("")] + [TestCase(null)] + public void AddConnection_Throws_Exception_For_Invalid_ConnectionType(string connectionType) + { + var datasourceFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(datasourceFactoryMock.Object, "", new ConnectionDetails()); + Assert.Throws(() => connectionInfo.AddConnection(connectionType, null)); + } + + [TestCase("")] + [TestCase(null)] + public void GetConnection_Throws_Exception_For_Invalid_ConnectionType(string connectionType) + { + var datasourceFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(datasourceFactoryMock.Object, "", new ConnectionDetails()); + Assert.Throws(() => connectionInfo.TryGetConnection(connectionType, out var connection)); + } + + [Test] + public void AddConnection_And_GetConnection_AddAndGet() + { + var connectionFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails()); + + var dataSourceFactoryMock = new Mock(); + var reliableDataSource = new ReliableDataSourceConnection("", RetryPolicyFactory.NoRetryPolicy, + RetryPolicyFactory.NoRetryPolicy, "", dataSourceFactoryMock.Object); + connectionInfo.AddConnection("ConnectionType", reliableDataSource); + + connectionInfo.TryGetConnection("ConnectionType", out var connection); + Assert.AreEqual(reliableDataSource, connection); + } + + [TestCase("")] + [TestCase(null)] + public void RemoveConnection_Throws_Exception_For_Invalid_ConnectionType(string connectionType) + { + var datasourceFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(datasourceFactoryMock.Object, "", new ConnectionDetails()); + Assert.Throws(() => connectionInfo.RemoveConnection(connectionType)); + } + + [Test] + public void RemoveConnection_Removes_Connection() + { + var connectionFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails()); + + var dataSourceFactoryMock = new Mock(); + var reliableDataSource = new ReliableDataSourceConnection("", RetryPolicyFactory.NoRetryPolicy, + RetryPolicyFactory.NoRetryPolicy, "", dataSourceFactoryMock.Object); + connectionInfo.AddConnection("ConnectionType", reliableDataSource); + + connectionInfo.RemoveConnection("ConnectionType"); + + connectionInfo.TryGetConnection("ConnectionType", out var connection); + Assert.IsNull(connection); + } + + [Test] + public void RemoveAllConnections_RemovesAllConnections() + { + var connectionFactoryMock = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails()); + + var dataSourceFactoryMock = new Mock(); + var reliableDataSource = new ReliableDataSourceConnection("", RetryPolicyFactory.NoRetryPolicy, + RetryPolicyFactory.NoRetryPolicy, "", dataSourceFactoryMock.Object); + connectionInfo.AddConnection("ConnectionType", reliableDataSource); + + connectionInfo.RemoveAllConnections(); + + connectionInfo.TryGetConnection("ConnectionType", out var connection); + Assert.IsNull(connection); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionProviderOptionsHelperTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionProviderOptionsHelperTests.cs new file mode 100644 index 00000000..fa6f7f2b --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/ConnectionProviderOptionsHelperTests.cs @@ -0,0 +1,15 @@ +using Microsoft.Kusto.ServiceLayer.Connection; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.Connection +{ + public class ConnectionProviderOptionsHelperTests + { + [Test] + public void BuildConnectionProviderOptions_Returns_31_Options() + { + var providerOptions = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(); + Assert.AreEqual(31, providerOptions.Options.Length); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/DataSourceConnectionFactoryTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/DataSourceConnectionFactoryTests.cs new file mode 100644 index 00000000..64ebab23 --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Connection/DataSourceConnectionFactoryTests.cs @@ -0,0 +1,20 @@ +using Microsoft.Kusto.ServiceLayer.Connection; +using Microsoft.Kusto.ServiceLayer.DataSource; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.Connection +{ + public class DataSourceConnectionFactoryTests + { + [Test] + public void CreateDataSourceConnection_Returns_Connection() + { + var dataSourceFactoryMock = new Mock(); + var connectionFactory = new DataSourceConnectionFactory(dataSourceFactoryMock.Object); + var connection = connectionFactory.CreateDataSourceConnection("", ""); + + Assert.IsNotNull(connection); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs new file mode 100644 index 00000000..f674a32c --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs @@ -0,0 +1,71 @@ +using System.Linq; +using Kusto.Language.Editor; +using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense; +using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion; +using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts; +using Microsoft.Kusto.ServiceLayer.Workspace.Contracts; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource.DataSourceIntellisense +{ + public class KustoIntellisenseHelperTests + { + [TestCase(CompletionKind.Syntax, CompletionItemKind.Module)] + [TestCase(CompletionKind.Column, CompletionItemKind.Field)] + [TestCase(CompletionKind.Variable, CompletionItemKind.Variable)] + [TestCase(CompletionKind.Table, CompletionItemKind.File)] + [TestCase(CompletionKind.Database, CompletionItemKind.Method)] + [TestCase(CompletionKind.LocalFunction, CompletionItemKind.Function)] + [TestCase(CompletionKind.DatabaseFunction, CompletionItemKind.Function)] + [TestCase(CompletionKind.BuiltInFunction, CompletionItemKind.Function)] + [TestCase(CompletionKind.AggregateFunction, CompletionItemKind.Function)] + [TestCase(CompletionKind.Unknown, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Keyword, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Punctuation, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Identifier, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Example, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.ScalarPrefix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.TabularPrefix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.TabularSuffix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.QueryPrefix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.CommandPrefix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.ScalarInfix, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.RenderChart, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Parameter, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.Cluster, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.MaterialiedView, CompletionItemKind.Keyword)] + [TestCase(CompletionKind.ScalarType, CompletionItemKind.Keyword)] + public void CreateCompletionItemKind_Returns_Kind(CompletionKind completionKind, CompletionItemKind expected) + { + var result = KustoIntellisenseHelper.CreateCompletionItemKind(completionKind); + Assert.AreEqual(expected, result); + } + + [Test] + public void GetDefaultKeywords_Returns_Keywords() + { + 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 = KustoIntellisenseHelper.GetDefaultKeywords(documentInfo, position); + Assert.AreEqual(13, completionItems.Length); + } + + [Test] + public void GetDefaultDiagnostics_Returns_Diagnostics() + { + var parseInfo = new ScriptParseInfo(); + var scriptFile = new ScriptFile("", "", ""); + var queryText = ".show databases"; + var completionItems = KustoIntellisenseHelper.GetDefaultDiagnostics(parseInfo, scriptFile, queryText); + + Assert.AreEqual(6, completionItems.Length); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoQueryUtilsTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoQueryUtilsTests.cs new file mode 100644 index 00000000..710dd29f --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoQueryUtilsTests.cs @@ -0,0 +1,188 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Kusto.ServiceLayer.DataSource; +using Microsoft.Kusto.ServiceLayer.DataSource.Metadata; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource +{ + public class KustoQueryUtilsTests + { + [TestCase("[@Table With Spaces", "[@Table With Spaces")] + [TestCase("*", "*")] + [TestCase("TableName", "TableName")] + [TestCase("and", "[@\"and\"]")] + [TestCase("anomalychart", "[@\"anomalychart\"]")] + [TestCase("areachart", "[@\"areachart\"]")] + [TestCase("asc", "[@\"asc\"]")] + [TestCase("barchart", "[@\"barchart\"]")] + [TestCase("between", "[@\"between\"]")] + [TestCase("bool", "[@\"bool\"]")] + [TestCase("boolean", "[@\"boolean\"]")] + [TestCase("by", "[@\"by\"]")] + [TestCase("columnchart", "[@\"columnchart\"]")] + [TestCase("consume", "[@\"consume\"]")] + [TestCase("contains", "[@\"contains\"]")] + [TestCase("containscs", "[@\"containscs\"]")] + [TestCase("count", "[@\"count\"]")] + [TestCase("date", "[@\"date\"]")] + [TestCase("datetime", "[@\"datetime\"]")] + [TestCase("default", "[@\"default\"]")] + [TestCase("desc", "[@\"desc\"]")] + [TestCase("distinct", "[@\"distinct\"]")] + [TestCase("double", "[@\"double\"]")] + [TestCase("dynamic", "[@\"dynamic\"]")] + [TestCase("endswith", "[@\"endswith\"]")] + [TestCase("evaluate", "[@\"evaluate\"]")] + [TestCase("extend", "[@\"extend\"]")] + [TestCase("false", "[@\"false\"]")] + [TestCase("filter", "[@\"filter\"]")] + [TestCase("find", "[@\"find\"]")] + [TestCase("first", "[@\"first\"]")] + [TestCase("flags", "[@\"flags\"]")] + [TestCase("float", "[@\"float\"]")] + [TestCase("getschema", "[@\"getschema\"]")] + [TestCase("has", "[@\"has\"]")] + [TestCase("hasprefix", "[@\"hasprefix\"]")] + [TestCase("hassuffix", "[@\"hassuffix\"]")] + [TestCase("in", "[@\"in\"]")] + [TestCase("int", "[@\"int\"]")] + [TestCase("join", "[@\"join\"]")] + [TestCase("journal", "[@\"journal\"]")] + [TestCase("kind", "[@\"kind\"]")] + [TestCase("ladderchart", "[@\"ladderchart\"]")] + [TestCase("last", "[@\"last\"]")] + [TestCase("like", "[@\"like\"]")] + [TestCase("limit", "[@\"limit\"]")] + [TestCase("linechart", "[@\"linechart\"]")] + [TestCase("long", "[@\"long\"]")] + [TestCase("materialize", "[@\"materialize\"]")] + [TestCase("mvexpand", "[@\"mvexpand\"]")] + [TestCase("notcontains", "[@\"notcontains\"]")] + [TestCase("notlike", "[@\"notlike\"]")] + [TestCase("of", "[@\"of\"]")] + [TestCase("or", "[@\"or\"]")] + [TestCase("order", "[@\"order\"]")] + [TestCase("parse", "[@\"parse\"]")] + [TestCase("piechart", "[@\"piechart\"]")] + [TestCase("pivotchart", "[@\"pivotchart\"]")] + [TestCase("print", "[@\"print\"]")] + [TestCase("project", "[@\"project\"]")] + [TestCase("queries", "[@\"queries\"]")] + [TestCase("real", "[@\"real\"]")] + [TestCase("regex", "[@\"regex\"]")] + [TestCase("sample", "[@\"sample\"]")] + [TestCase("scatterchart", "[@\"scatterchart\"]")] + [TestCase("search", "[@\"search\"]")] + [TestCase("set", "[@\"set\"]")] + [TestCase("sort", "[@\"sort\"]")] + [TestCase("stacked", "[@\"stacked\"]")] + [TestCase("stacked100", "[@\"stacked100\"]")] + [TestCase("stackedareachart", "[@\"stackedareachart\"]")] + [TestCase("startswith", "[@\"startswith\"]")] + [TestCase("string", "[@\"string\"]")] + [TestCase("summarize", "[@\"summarize\"]")] + [TestCase("take", "[@\"take\"]")] + [TestCase("time", "[@\"time\"]")] + [TestCase("timechart", "[@\"timechart\"]")] + [TestCase("timeline", "[@\"timeline\"]")] + [TestCase("timepivot", "[@\"timepivot\"]")] + [TestCase("timespan", "[@\"timespan\"]")] + [TestCase("to", "[@\"to\"]")] + [TestCase("top", "[@\"top\"]")] + [TestCase("toscalar", "[@\"toscalar\"]")] + [TestCase("true", "[@\"true\"]")] + [TestCase("union", "[@\"union\"]")] + [TestCase("unstacked", "[@\"unstacked\"]")] + [TestCase("viewers", "[@\"viewers\"]")] + [TestCase("where", "[@\"where\"]")] + [TestCase("withsource", "[@\"withsource\"]")] + public void EscapeName_Returns_Name(string name, string expected) + { + var result = KustoQueryUtils.EscapeName(name); + Assert.AreEqual(expected, result); + } + + [TestCase(".show databases", true)] + [TestCase(".show schema", true)] + [TestCase(".show tables", false)] + public void IsClusterLevelQuery_Returns_Result(string query, bool expected) + { + var result = KustoQueryUtils.IsClusterLevelQuery(query); + Assert.AreEqual(expected, result); + } + + [Test] + public void SafeAdd_AddsRecord_ExistingKey() + { + string key = "FolderName"; + + var dictionary = new Dictionary> + { + [key] = new Dictionary() + }; + var existingRecord = new DataSourceObjectMetadata + { + Name = "Folder 1" + }; + dictionary[key].Add(key, existingRecord); + + var newRecord = new DataSourceObjectMetadata + { + Name = "Folder 2" + }; + dictionary.SafeAdd(key, newRecord); + + Assert.AreEqual(2, dictionary[key].Count); + } + + [Test] + public void SafeAdd_AddsRecord_NewKey() + { + string key = "FolderName"; + + var dictionary = new Dictionary> + { + [key] = new Dictionary() + }; + + var newRecord = new DataSourceObjectMetadata + { + Name = "Folder 2" + }; + dictionary.SafeAdd(key, newRecord); + + Assert.AreEqual(1, dictionary[key].Count); + } + + [Test] + public void AddRange_Keeps_Existing_Records_And_Order() + { + var key = "DatabaseName"; + + var existingObjectMetadata = new DataSourceObjectMetadata + { + PrettyName = "Ball Table" + }; + + var dictionary = new ConcurrentDictionary> + { + [key] = new List {existingObjectMetadata} + }; + + var newMetadata = new DataSourceObjectMetadata + { + PrettyName = "Apple Table" + }; + + dictionary.AddRange(key, new List {newMetadata}); + + Assert.AreEqual(2, dictionary[key].Count()); + + // ensure order by clause + Assert.AreEqual(newMetadata.PrettyName, dictionary[key].First().PrettyName); + Assert.AreEqual(existingObjectMetadata.PrettyName, dictionary[key].Last().PrettyName); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/BindingQueueTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/BindingQueueTests.cs new file mode 100644 index 00000000..1095d147 --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/BindingQueueTests.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using Microsoft.Kusto.ServiceLayer.LanguageServices; +using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices +{ + public class BindingQueueTests + { + [Test] + public void QueueBindingOperation_Returns_Null_For_NullBindOperation() + { + var bindingQueue = new BindingQueue(); + var queueItem = bindingQueue.QueueBindingOperation("", null); + Assert.IsNull(queueItem); + } + + [Test] + public void QueueBindingOperation_Returns_QueueItem() + { + var key = "key"; + var bindOperation = new Func((context, token) => new Hover()); + Func timeoutOperation = (context) => LanguageService.HoverTimeout; + Func errorHandler = exception => new Exception(); + var bindingTimeout = 30; + var waitForLockTimeout = 45; + + var bindingQueue = new BindingQueue(); + var queueItem = bindingQueue.QueueBindingOperation(key, + bindOperation, + timeoutOperation, + errorHandler, + bindingTimeout, + waitForLockTimeout); + + Assert.AreEqual(key, queueItem.Key); + Assert.AreEqual(bindOperation, queueItem.BindOperation); + Assert.AreEqual(timeoutOperation, queueItem.TimeoutOperation); + Assert.AreEqual(errorHandler, queueItem.ErrorHandler); + Assert.AreEqual(bindingTimeout, queueItem.BindingTimeout); + Assert.AreEqual(waitForLockTimeout, queueItem.WaitForLockTimeout); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs new file mode 100644 index 00000000..581e1feb --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using Microsoft.Kusto.ServiceLayer.Connection; +using Microsoft.Kusto.ServiceLayer.Connection.Contracts; +using Microsoft.Kusto.ServiceLayer.DataSource; +using Microsoft.Kusto.ServiceLayer.LanguageServices; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.SqlParser.MetadataProvider; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices +{ + public class ConnectedBindingQueueTests + { + private static IEnumerable> ConnectionDetailsSource() + { + var results = new List>(); + + var details1 = new ConnectionDetails + { + ServerName = "ServerName", + DatabaseName = "DatabaseName", + UserName = "UserName", + AuthenticationType = "AuthenticationType", + DatabaseDisplayName = "DisplayName", + GroupId = "GroupId" + }; + + results.Add(new Tuple(details1, "ServerName_DatabaseName_UserName_AuthenticationType_DisplayName_GroupId")); + + var details2 = new ConnectionDetails + { + ServerName = null, + DatabaseName = null, + UserName = null, + AuthenticationType = null, + DatabaseDisplayName = "", + GroupId = "" + }; + + results.Add(new Tuple(details2, "NULL_NULL_NULL_NULL")); + + var details3 = new ConnectionDetails + { + ServerName = null, + DatabaseName = null, + UserName = null, + AuthenticationType = null, + DatabaseDisplayName = null, + GroupId = null + }; + + results.Add(new Tuple(details3, "NULL_NULL_NULL_NULL")); + + return results; + } + + [TestCaseSource(nameof(ConnectionDetailsSource))] + public void GetConnectionContextKey_Returns_Key(Tuple tuple) + { + var contextKey = ConnectedBindingQueue.GetConnectionContextKey(tuple.Item1); + Assert.AreEqual(tuple.Item2, contextKey); + } + + [Test] + public void AddConnectionContext_Returns_EmptyString_For_NullConnectionInfo() + { + var connectionOpenerMock = new Mock(); + var dataSourceFactory = new Mock(); + var connectedBindingQueue = new ConnectedBindingQueue(connectionOpenerMock.Object, dataSourceFactory.Object); + var connectionKey = connectedBindingQueue.AddConnectionContext(null, false); + + Assert.AreEqual(string.Empty, connectionKey); + } + + [Test] + public void AddConnectionContext_Returns_ConnectionKey() + { + var connectionDetails = new ConnectionDetails(); + var connectionFactory = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactory.Object, "ownerUri", connectionDetails); + + var connectionOpenerMock = new Mock(); + var dataSourceFactory = new Mock(); + var connectedBindingQueue = new ConnectedBindingQueue(connectionOpenerMock.Object, dataSourceFactory.Object); + var connectionKey = connectedBindingQueue.AddConnectionContext(connectionInfo, false, "featureName"); + + Assert.AreEqual("NULL_NULL_NULL_NULL", connectionKey); + } + + [TestCase(false)] + public void AddConnectionContext_Sets_BindingContext(bool needsMetadata) + { + var connectionDetails = new ConnectionDetails(); + var connectionFactory = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactory.Object, "ownerUri", connectionDetails); + + var connectionOpenerMock = new Mock(); + var fakeServerConnection = new ServerConnection(); + connectionOpenerMock + .Setup(x => x.OpenServerConnection(It.IsAny(), It.IsAny())) + .Returns(fakeServerConnection); + + var dataSourceFactory = new Mock(); + var dataSourceMock = new Mock(); + dataSourceFactory + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(dataSourceMock.Object); + + var connectedBindingQueue = + new ConnectedBindingQueue(connectionOpenerMock.Object, dataSourceFactory.Object); + var connectionKey = + connectedBindingQueue.AddConnectionContext(connectionInfo, needsMetadata, "featureName"); + var bindingContext = connectedBindingQueue.GetOrCreateBindingContext(connectionKey); + + Assert.AreEqual(fakeServerConnection, bindingContext.ServerConnection); + Assert.AreEqual(dataSourceMock.Object, bindingContext.DataSource); + Assert.AreEqual(500, bindingContext.BindingTimeout); + Assert.AreEqual(true, bindingContext.IsConnected); + Assert.AreEqual(CasingStyle.Uppercase, bindingContext.MetadataDisplayInfoProvider.BuiltInCasing); + Assert.IsNull(bindingContext.SmoMetadataProvider); + Assert.IsNull(bindingContext.Binder); + } + + [Test] + public void RemoveBindingContext_Removes_Context() + { + var connectionDetails = new ConnectionDetails(); + var connectionFactory = new Mock(); + var connectionInfo = new ConnectionInfo(connectionFactory.Object, "ownerUri", connectionDetails); + + var connectionOpenerMock = new Mock(); + var dataSourceFactory = new Mock(); + var connectedBindingQueue = new ConnectedBindingQueue(connectionOpenerMock.Object, dataSourceFactory.Object); + var connectionKey = connectedBindingQueue.AddConnectionContext(connectionInfo, false, "featureName"); + + + connectedBindingQueue.RemoveBindingContext(connectionInfo); + + Assert.IsFalse(connectedBindingQueue.BindingContextMap.ContainsKey(connectionKey)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/DiagnosticsHelperTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/DiagnosticsHelperTests.cs new file mode 100644 index 00000000..4acd7e11 --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/DiagnosticsHelperTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Kusto.ServiceLayer.LanguageServices; +using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts; +using Microsoft.Kusto.ServiceLayer.Workspace.Contracts; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices +{ + public class DiagnosticsHelperTests + { + [TestCase("")] + [TestCase(null)] + public void ClearScriptDiagnostics_Throws_Exception_InvalidUri(string uri) + { + Assert.ThrowsAsync(() => DiagnosticsHelper.ClearScriptDiagnostics(uri, new EventContext())); + } + + [Test] + public void ClearScriptDiagnostics_Throws_Exception_InvalidEventContext() + { + Assert.ThrowsAsync(() => DiagnosticsHelper.ClearScriptDiagnostics("uri", null)); + } + + [Test] + public void ClearScriptDiagnostics_SendsEvent_ValidParams() + { + var uri = "uri"; + var eventContextMock = new Mock(); + var task = DiagnosticsHelper.ClearScriptDiagnostics(uri, eventContextMock.Object); + task.Wait(); + + eventContextMock.Verify( + e => e.SendEvent(PublishDiagnosticsNotification.Type, + It.Is(x => x.Uri == uri && x.Diagnostics.Length == 0)), Times.Once); + } + + [TestCase(ScriptFileMarkerLevel.Error, DiagnosticSeverity.Error)] + [TestCase(ScriptFileMarkerLevel.Warning, DiagnosticSeverity.Warning)] + [TestCase(ScriptFileMarkerLevel.Information, DiagnosticSeverity.Information)] + public async Task PublishScriptDiagnostics_Maps_Severity(ScriptFileMarkerLevel markerLevel, DiagnosticSeverity expected) + { + var uri = "uri"; + var scriptFile = new ScriptFile("", uri, ""); + ScriptFileMarker[] semanticMarkers = + { + new ScriptFileMarker + { + Level = markerLevel, + ScriptRegion = new ScriptRegion + { + StartLineNumber = 1, + StartColumnNumber = 1, + EndLineNumber = 2, + EndColumnNumber = 2 + } + } + }; + + var actualEventType = new EventType(); + var actualNotification = new PublishDiagnosticsNotification(); + var eventContextMock = new Mock(); + eventContextMock.Setup(x => x.SendEvent(It.IsAny>(), + It.IsAny())) + .Callback, PublishDiagnosticsNotification>( + (eventType, notification) => + { + actualEventType = eventType; + actualNotification = notification; + }) + .Returns(Task.FromResult(0)); + await DiagnosticsHelper.PublishScriptDiagnostics(scriptFile, semanticMarkers, eventContextMock.Object); + + eventContextMock.Verify(e => e.SendEvent(PublishDiagnosticsNotification.Type, + It.IsAny()), Times.Once); + + Assert.AreEqual(PublishDiagnosticsNotification.Type.MethodName, actualEventType.MethodName); + Assert.AreEqual(uri, actualNotification.Uri); + Assert.AreEqual(1, actualNotification.Diagnostics.Length); + + var diagnostic = actualNotification.Diagnostics.First(); + Assert.AreEqual(expected, diagnostic.Severity); + } + + [Test] + public async Task PublishScriptDiagnostics_Creates_Diagnostic() + { + var uri = "uri"; + var scriptFile = new ScriptFile("", uri, ""); + var fileMarker = new ScriptFileMarker + { + Message = "Message", + Level = ScriptFileMarkerLevel.Information, + ScriptRegion = new ScriptRegion + { + StartLineNumber = 1, + StartColumnNumber = 1, + EndLineNumber = 2, + EndColumnNumber = 2 + } + }; + + var actualEventType = new EventType(); + var actualNotification = new PublishDiagnosticsNotification(); + var eventContextMock = new Mock(); + eventContextMock.Setup(x => x.SendEvent(It.IsAny>(), + It.IsAny())) + .Callback, PublishDiagnosticsNotification>( + (eventType, notification) => + { + actualEventType = eventType; + actualNotification = notification; + }) + .Returns(Task.FromResult(0)); + + await DiagnosticsHelper.PublishScriptDiagnostics(scriptFile, new[] {fileMarker}, eventContextMock.Object); + + Assert.AreEqual(PublishDiagnosticsNotification.Type.MethodName, actualEventType.MethodName); + Assert.AreEqual(uri, actualNotification.Uri); + Assert.AreEqual(1, actualNotification.Diagnostics.Length); + + var diagnostic = actualNotification.Diagnostics.First(); + Assert.AreEqual(null, diagnostic.Code); + Assert.AreEqual(fileMarker.Message, diagnostic.Message); + Assert.AreEqual(0, diagnostic.Range.Start.Character); + Assert.AreEqual(0, diagnostic.Range.Start.Line); + Assert.AreEqual(1, diagnostic.Range.End.Character); + Assert.AreEqual(1, diagnostic.Range.End.Line); + Assert.AreEqual(DiagnosticSeverity.Information, diagnostic.Severity); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs new file mode 100644 index 00000000..2a115fb2 --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs @@ -0,0 +1,35 @@ +using Microsoft.Kusto.ServiceLayer.Connection; +using Microsoft.Kusto.ServiceLayer.Connection.Contracts; +using Microsoft.Kusto.ServiceLayer.Metadata; +using Microsoft.Kusto.ServiceLayer.Metadata.Contracts; +using Microsoft.SqlTools.Hosting.Protocol; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.Metadata +{ + public class MetadataServiceTests + { + [Test] + public void HandleMetadataListRequest_Sets_MetadataListTask() + { + var serviceHostMock = new Mock(); + var connectionServiceMock = new Mock(); + var connectionFactoryMock = new Mock(); + + var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails()); + connectionServiceMock.Setup(x => x.TryFindConnection(It.IsAny(), out connectionInfo)); + + var metadataService = new MetadataService(); + metadataService.InitializeService(serviceHostMock.Object, connectionServiceMock.Object); + + Assert.IsNull(metadataService.MetadataListTask); + + var task = metadataService.HandleMetadataListRequest(new MetadataQueryParams(), + new RequestContext()); + task.Wait(); + + Assert.IsNotNull(metadataService.MetadataListTask); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Scripting/ScripterTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Scripting/ScripterTests.cs new file mode 100644 index 00000000..144a99bc --- /dev/null +++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Scripting/ScripterTests.cs @@ -0,0 +1,57 @@ +using Microsoft.Kusto.ServiceLayer.DataSource; +using Microsoft.Kusto.ServiceLayer.Scripting; +using Microsoft.Kusto.ServiceLayer.Scripting.Contracts; +using Microsoft.SqlServer.Management.Sdk.Sfc; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Kusto.ServiceLayer.UnitTests.Scripting +{ + public class ScripterTests + { + [Test] + public void SelectFromTableOrView_Returns_SelectQuery() + { + var mockDataSource = new Mock(); + var urn = new Urn(@"Server[@Name = 'SERVER']/Database[@Name = 'quoted''db']/Table[@Name = 'quoted''Name' and @Schema = 'quoted''Schema']"); + var scripter = new Scripter(); + var result = scripter.SelectFromTableOrView(mockDataSource.Object, urn); + + Assert.AreEqual("[@\"quoted'Name\"]\n | limit 1000", result); + } + + [Test] + public void AlterFunction() + { + var expected = "AlterScript"; + var mockDataSource = new Mock(); + mockDataSource.Setup(x => x.GenerateAlterFunctionScript(It.IsAny())).Returns(expected); + + var scriptingObject = new ScriptingObject + { + Name = "Name(a:int, b: int)" + }; + var scripter = new Scripter(); + var result = scripter.AlterFunction(mockDataSource.Object, scriptingObject); + + Assert.AreEqual(expected, result); + } + + [Test] + public void ExecuteFunction() + { + var expected = "ExecuteScript"; + var mockDataSource = new Mock(); + mockDataSource.Setup(x => x.GenerateExecuteFunctionScript(It.IsAny())).Returns(expected); + + var scriptingObject = new ScriptingObject + { + Name = "Name(a:int, b: int)" + }; + var scripter = new Scripter(); + var result = scripter.ExecuteFunction(mockDataSource.Object, scriptingObject); + + Assert.AreEqual(expected, result); + } + } +} \ No newline at end of file