/* * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using System; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication.Data; using SafeExamBrowser.Contracts.Communication.Events; using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.UserInterface.MessageBox; using SafeExamBrowser.Runtime.Communication; namespace SafeExamBrowser.Runtime.UnitTests.Communication { [TestClass] public class RuntimeHostTests { private Mock hostObject; private Mock hostObjectFactory; private Mock logger; private RuntimeHost sut; [TestInitialize] public void Initialize() { hostObject = new Mock(); hostObjectFactory = new Mock(); logger = new Mock(); hostObjectFactory.Setup(f => f.CreateObject(It.IsAny(), It.IsAny())).Returns(hostObject.Object); sut = new RuntimeHost("net:pipe://some/address", hostObjectFactory.Object, logger.Object, 0); } [TestMethod] public void MustAllowConnectionIfTokenIsValid() { var token = Guid.NewGuid(); sut.AllowConnection = true; sut.AuthenticationToken = token; var response = sut.Connect(token); Assert.IsNotNull(response); Assert.IsTrue(response.ConnectionEstablished); } [TestMethod] public void MustRejectConnectionIfTokenInvalid() { var token = Guid.NewGuid(); sut.AllowConnection = true; sut.AuthenticationToken = token; var response = sut.Connect(Guid.NewGuid()); Assert.IsNotNull(response); Assert.IsFalse(response.ConnectionEstablished); } [TestMethod] public void MustRejectConnectionIfNoAuthenticationTokenSet() { var token = Guid.Empty; sut.AllowConnection = true; var response = sut.Connect(token); Assert.IsNotNull(response); Assert.IsFalse(response.ConnectionEstablished); } [TestMethod] public void MustOnlyAllowOneConcurrentConnection() { var token = Guid.NewGuid(); sut.AllowConnection = true; sut.AuthenticationToken = token; var response1 = sut.Connect(token); var response2 = sut.Connect(token); var response3 = sut.Connect(token); Assert.IsNotNull(response1); Assert.IsNotNull(response2); Assert.IsNotNull(response3); Assert.IsNotNull(response1.CommunicationToken); Assert.IsNull(response2.CommunicationToken); Assert.IsNull(response3.CommunicationToken); Assert.IsTrue(response1.ConnectionEstablished); Assert.IsFalse(response2.ConnectionEstablished); Assert.IsFalse(response3.ConnectionEstablished); } [TestMethod] public void MustCorrectlyDisconnectClient() { var disconnected = false; var token = Guid.NewGuid(); sut.AllowConnection = true; sut.AuthenticationToken = token; sut.ClientDisconnected += () => disconnected = true; var connectionResponse = sut.Connect(token); var message = new DisconnectionMessage { CommunicationToken = connectionResponse.CommunicationToken.Value, Interlocutor = Interlocutor.Client }; var response = sut.Disconnect(message); Assert.IsNotNull(response); Assert.IsTrue(disconnected); Assert.IsTrue(response.ConnectionTerminated); } [TestMethod] public void MustAllowReconnectionAfterDisconnection() { var token = Guid.NewGuid(); sut.AllowConnection = true; sut.AuthenticationToken = token; var response = sut.Connect(token); sut.Disconnect(new DisconnectionMessage { CommunicationToken = response.CommunicationToken.Value }); sut.AllowConnection = true; sut.AuthenticationToken = token = Guid.NewGuid(); response = sut.Connect(token); Assert.IsTrue(response.ConnectionEstablished); } [TestMethod] public void MustHandleClientReadyCorrectly() { var clientReady = false; sut.AllowConnection = true; sut.ClientReady += () => clientReady = true; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new SimpleMessage(SimpleMessagePurport.ClientIsReady) { CommunicationToken = token }; var response = sut.Send(message); Assert.IsTrue(clientReady); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); } [TestMethod] public void MustHandleConfigurationRequestCorrectly() { var args = default(ClientConfigurationEventArgs); var configuration = new ClientConfiguration { Settings = new Settings { AdminPasswordHash = "12345" } }; sut.AllowConnection = true; sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; }; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new SimpleMessage(SimpleMessagePurport.ConfigurationNeeded) { CommunicationToken = token }; var response = sut.Send(message); Assert.IsNotNull(args); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(ConfigurationResponse)); Assert.AreEqual(configuration.Settings.AdminPasswordHash, (response as ConfigurationResponse)?.Configuration.Settings.AdminPasswordHash); } [TestMethod] public void MustHandleShutdownRequestCorrectly() { var shutdownRequested = false; sut.AllowConnection = true; sut.ShutdownRequested += () => shutdownRequested = true; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new SimpleMessage(SimpleMessagePurport.RequestShutdown) { CommunicationToken = token }; var response = sut.Send(message); Assert.IsTrue(shutdownRequested); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); } [TestMethod] public void MustHandleMessageBoxReplyCorrectly() { var args = default(MessageBoxReplyEventArgs); var requestId = Guid.NewGuid(); var result = MessageBoxResult.Ok; var sync = new AutoResetEvent(false); sut.AllowConnection = true; sut.MessageBoxReplyReceived += (a) => { args = a; sync.Set(); }; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new MessageBoxReplyMessage(requestId, result) { CommunicationToken = token }; var response = sut.Send(message); sync.WaitOne(); Assert.IsNotNull(args); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); Assert.AreEqual(requestId, args.RequestId); Assert.AreEqual(result, args.Result); } [TestMethod] public void MustHandlePasswordReplyCorrectly() { var args = default(PasswordReplyEventArgs); var password = "test1234"; var requestId = Guid.NewGuid(); var success = true; var sync = new AutoResetEvent(false); sut.AllowConnection = true; sut.PasswordReceived += (a) => { args = a; sync.Set(); }; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new PasswordReplyMessage(requestId, success, password) { CommunicationToken = token }; var response = sut.Send(message); sync.WaitOne(); Assert.IsNotNull(args); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); Assert.AreEqual(password, args.Password); Assert.AreEqual(requestId, args.RequestId); Assert.AreEqual(success, args.Success); } [TestMethod] public void MustHandleReconfigurationRequestCorrectly() { var args = default(ReconfigurationEventArgs); var path = "C:\\Temp\\Some\\File.seb"; var sync = new AutoResetEvent(false); sut.AllowConnection = true; sut.ReconfigurationRequested += (a) => { args = a; sync.Set(); }; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new ReconfigurationMessage(path) { CommunicationToken = token }; var response = sut.Send(message); sync.WaitOne(); Assert.IsNotNull(args); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); Assert.AreEqual(path, args.ConfigurationPath); } [TestMethod] public void MustReturnUnknownMessageAsDefault() { sut.AllowConnection = true; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; var message = new TestMessage { CommunicationToken = token } as Message; var response = sut.Send(message); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); message = new SimpleMessage(default(SimpleMessagePurport)) { CommunicationToken = token }; response = sut.Send(message); Assert.IsNotNull(response); Assert.IsInstanceOfType(response, typeof(SimpleResponse)); Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); } [TestMethod] public void MustNotFailIfNoEventHandlersSubscribed() { sut.AllowConnection = true; sut.AuthenticationToken = Guid.Empty; var token = sut.Connect(Guid.Empty).CommunicationToken.Value; sut.Send(new SimpleMessage(SimpleMessagePurport.ClientIsReady) { CommunicationToken = token }); sut.Send(new SimpleMessage(SimpleMessagePurport.ConfigurationNeeded) { CommunicationToken = token }); sut.Send(new SimpleMessage(SimpleMessagePurport.RequestShutdown) { CommunicationToken = token }); sut.Send(new MessageBoxReplyMessage(Guid.Empty, MessageBoxResult.Cancel) { CommunicationToken = token }); sut.Send(new PasswordReplyMessage(Guid.Empty, false, "") { CommunicationToken = token }); sut.Send(new ReconfigurationMessage("") { CommunicationToken = token }); sut.Disconnect(new DisconnectionMessage { CommunicationToken = token }); } private class TestMessage : Message { }; } }