// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using Microsoft.SqlTools.DataProtocol.Contracts; using Microsoft.SqlTools.Hosting.Channels; using Microsoft.SqlTools.Hosting.Contracts; using Microsoft.SqlTools.Hosting.Contracts.Internal; using Microsoft.SqlTools.Hosting.Protocol; using Moq; using Newtonsoft.Json.Linq; using NUnit.Framework; namespace Microsoft.SqlTools.Hosting.UnitTests.ServiceHostTests { [TestFixture] public class ServiceHostTests { #region Construction Tests [Test] public void ServiceHostConstructDefaultServer() { // If: I construct a default server service host var sh = ServiceHost.CreateDefaultServer(); var jh = sh.jsonRpcHost as JsonRpcHost; Assert.NotNull(jh); Assert.That(jh.protocolChannel, Is.InstanceOf(), "The underlying json rpc host should be using the stdio server channel"); Assert.False(jh.protocolChannel.IsConnected); } [Test] public void ServiceHostNullParameter() { // If: I create a service host with missing parameters // Then: I should get an exception Assert.Throws(() => new ServiceHost(null)); } #endregion #region IServiceHost Tests [Test] public void RegisterInitializeTask() { // Setup: Create mock initialize handler var mockHandler = new Mock>().Object; // If: I register a couple initialize tasks with the service host var sh = GetServiceHost(); sh.RegisterInitializeTask(mockHandler); sh.RegisterInitializeTask(mockHandler); // Then: There should be two initialize tasks registered Assert.AreEqual(2, sh.initCallbacks.Count); Assert.True(sh.initCallbacks.SequenceEqual(new[] {mockHandler, mockHandler})); } [Test] public void RegisterInitializeTaskNullHandler() { // If: I register a null initialize task // Then: I should get an exception var sh = GetServiceHost(); Assert.Throws(() => sh.RegisterInitializeTask(null)); } [Test] public void RegisterShutdownTask() { // Setup: Create mock initialize handler var mockHandler = new Mock>().Object; // If: I register a couple shutdown tasks with the service host var sh = GetServiceHost(); sh.RegisterShutdownTask(mockHandler); sh.RegisterShutdownTask(mockHandler); // Then: There should be two initialize tasks registered Assert.AreEqual(2, sh.shutdownCallbacks.Count); Assert.True(sh.shutdownCallbacks.SequenceEqual(new[] {mockHandler, mockHandler})); } [Test] public void RegisterShutdownTaskNullHandler() { // If: I register a null initialize task // Then: I should get an exception var sh = GetServiceHost(); Assert.Throws(() => sh.RegisterShutdownTask(null)); } #endregion #region IJsonRpcHost Tests [Test] public void SendEvent() { // If: I send an event var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.SendEvent(CommonObjects.EventType, CommonObjects.TestMessageContents.DefaultInstance); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SendEvent(CommonObjects.EventType, CommonObjects.TestMessageContents.DefaultInstance), Times.Once); } [Test] public async Task SendRequest() { // If: I send a request var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); await sh.SendRequest(CommonObjects.RequestType, CommonObjects.TestMessageContents.DefaultInstance); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SendRequest(CommonObjects.RequestType, CommonObjects.TestMessageContents.DefaultInstance), Times.Once); } [Test] public void SetAsyncEventHandler() { // If: I set an event handler var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.SetAsyncEventHandler(CommonObjects.EventType, null, true); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SetAsyncEventHandler(CommonObjects.EventType, null, true), Times.Once); } [Test] public void SetSyncEventHandler() { // If: I set an event handler var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.SetEventHandler(CommonObjects.EventType, null, true); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SetEventHandler(CommonObjects.EventType, null, true), Times.Once); } [Test] public void SetAsyncRequestHandler() { // If: I set a request handler var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.SetAsyncRequestHandler(CommonObjects.RequestType, null, true); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SetAsyncRequestHandler(CommonObjects.RequestType, null, true), Times.Once); } [Test] public void SetSyncRequestHandler() { // If: I set a request handler var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.SetRequestHandler(CommonObjects.RequestType, null, true); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.SetRequestHandler(CommonObjects.RequestType, null, true), Times.Once); } [Test] public void Start() { // If: I start a service host var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.Start(); // Then: // ... The underlying json rpc host should have handled it jh.Verify(o => o.Start(), Times.Once); } [Test] public void Stop() { // If: I stop a service host var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.Stop(); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.Stop(), Times.Once); } [Test] public void WaitForExit() { // If: I wait for service host to exit var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.WaitForExit(); // Then: The underlying json rpc host should have handled it jh.Verify(o => o.WaitForExit()); } #endregion #region Request Handling Tests [Test] public void HandleExitNotification() { // If: I handle an exit notification var jh = GetJsonRpcHostMock(); var sh = GetServiceHost(jh.Object); sh.HandleExitNotification(null, null); // Then: The json rpc host should have been stopped jh.Verify(o => o.Stop(), Times.Once); } [Test] public async Task HandleInitializeRequest() { // Setup: // ... Create an initialize handler and register it in the service host var mockHandler = new Mock>(); mockHandler.Setup(f => f(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(true)); var sh = GetServiceHost(); sh.RegisterInitializeTask(mockHandler.Object); sh.RegisterInitializeTask(mockHandler.Object); // ... Set a dummy value to return as the initialize response var ir = new InitializeResponse(); // ... Create a mock request that will handle sending a result // TODO: Replace with event flow validation var initParams = new InitializeParameters(); var bc = new BlockingCollection(); var mockContext = new RequestContext(Message.CreateRequest(InitializeRequest.Type, CommonObjects.MessageId, initParams), bc); // If: I handle an initialize request await sh.HandleInitializeRequest(initParams, mockContext); // Then: // ... The mock handler should have been called twice mockHandler.Verify(h => h(initParams, mockContext), Times.Exactly(2)); var outgoing = bc.ToArray(); Assert.That(outgoing.Select(m => m.Id), Is.EqualTo(new[] { CommonObjects.MessageId }), "There should have been a response sent"); Assert.AreEqual(JToken.FromObject(ir), JToken.FromObject(ir)); } [Test] public async Task HandleShutdownRequest() { // Setup: // ... Create a shutdown handler and register it in the service host var mockHandler = new Mock>(); mockHandler.Setup(f => f(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(true)); var sh = GetServiceHost(); sh.RegisterShutdownTask(mockHandler.Object); sh.RegisterShutdownTask(mockHandler.Object); // ... Create a mock request that will handle sending a result // TODO: Replace with the event flow validation var shutdownParams = new object(); var bc = new BlockingCollection(); var mockContext = new RequestContext(Message.CreateRequest(ShutdownRequest.Type, CommonObjects.MessageId, shutdownParams), bc); // If: I handle a shutdown request await sh.HandleShutdownRequest(shutdownParams, mockContext); mockHandler.Verify(h => h(shutdownParams, mockContext), Times.Exactly(2), "The mock handler should have been called twice"); Assert.That(bc.Count, Is.EqualTo(1), "There should have been a response sent"); } #endregion private static ServiceHost GetServiceHost(IJsonRpcHost jsonRpcHost = null) { return new ServiceHost { jsonRpcHost = jsonRpcHost }; } private static Mock GetJsonRpcHostMock() { var anyEventType = It.IsAny>(); var anyRequestType = It.IsAny>(); var anyParams = It.IsAny(); var mock = new Mock(); mock.Setup(jh => jh.SendEvent(anyEventType, anyParams)); mock.Setup(jh => jh.SendRequest(anyRequestType, anyParams)).ReturnsAsync(null); mock.Setup(jh => jh.SetAsyncEventHandler(anyEventType, It.IsAny>(), It.IsAny())); mock.Setup(jh => jh.SetEventHandler(anyEventType, It.IsAny>(), It.IsAny())); mock.Setup(jh => jh.SetAsyncRequestHandler(anyRequestType, It.IsAny, Task>>(), It.IsAny())); mock.Setup(jh => jh.SetRequestHandler(anyRequestType, It.IsAny>>(), It.IsAny())); mock.Setup(jh => jh.Start()); mock.Setup(jh => jh.Stop()); mock.Setup(jh => jh.WaitForExit()); return mock; } } }