diff --git a/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs index 3f0faa70..aeb8a86f 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs @@ -15,9 +15,9 @@ using SafeExamBrowser.Communication.Contracts.Data; using SafeExamBrowser.Communication.Contracts.Events; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Configuration.Contracts; -using SafeExamBrowser.Settings; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Communication; +using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts.MessageBox; namespace SafeExamBrowser.Runtime.UnitTests.Communication @@ -188,6 +188,60 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication Assert.AreEqual(configuration.Settings.Security.AdminPasswordHash, (response as ConfigurationResponse)?.Configuration.Settings.Security.AdminPasswordHash); } + [TestMethod] + public void MustHandleExamSelectionCorrectly() + { + var args = default(ExamSelectionReplyEventArgs); + var examId = "abc123"; + var requestId = Guid.NewGuid(); + var sync = new AutoResetEvent(false); + + sut.AllowConnection = true; + sut.AuthenticationToken = Guid.Empty; + sut.ExamSelectionReceived += (a) => { args = a; sync.Set(); }; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new ExamSelectionReplyMessage(requestId, true, examId) { 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.IsTrue(args.Success); + Assert.AreEqual(examId, args.SelectedExamId); + Assert.AreEqual(requestId, args.RequestId); + } + + [TestMethod] + public void MustHandleServerFailureActionCorrectly() + { + var args = default(ServerFailureActionReplyEventArgs); + var requestId = Guid.NewGuid(); + var sync = new AutoResetEvent(false); + + sut.AllowConnection = true; + sut.AuthenticationToken = Guid.Empty; + sut.ServerFailureActionReceived += (a) => { args = a; sync.Set(); }; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new ServerFailureActionReplyMessage(true, false, true, requestId) { 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.IsFalse(args.Fallback); + Assert.IsTrue(args.Abort); + Assert.IsTrue(args.Retry); + Assert.AreEqual(requestId, args.RequestId); + } + [TestMethod] public void MustHandleShutdownRequestCorrectly() { diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ServerOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ServerOperationTests.cs index 4408e3b3..50c07e5e 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/ServerOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ServerOperationTests.cs @@ -31,7 +31,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations private Mock fileSystem; private Mock logger; private Mock server; - private AppSettings settings; private Mock configuration; private ServerOperation sut; @@ -44,13 +43,14 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations fileSystem = new Mock(); logger = new Mock(); server = new Mock(); - settings = new AppSettings(); context.Current = new SessionConfiguration(); + context.Current.AppConfig = new AppConfig(); context.Current.Settings = new AppSettings(); context.Next = new SessionConfiguration(); context.Next.AppConfig = new AppConfig(); - context.Next.Settings = settings; + context.Next.Settings = new AppSettings(); + sut = new ServerOperation(new string[0], configuration.Object, fileSystem.Object, logger.Object, context, server.Object); } @@ -68,11 +68,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations var getConnection = 0; var getExams = 0; var initialize = 0; - var serverSettings = settings.Server; + var initialSettings = context.Next.Settings; + var serverSettings = context.Next.Settings.Server; configuration .Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())) .Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; fileSystem.Setup(f => f.Delete(It.IsAny())).Callback(() => delete = ++counter); server.Setup(s => s.Connect()).Returns(new ServerResponse(true)).Callback(() => connect = ++counter); server.Setup(s => s.Initialize(It.IsAny())).Callback(() => initialize = ++counter); @@ -85,7 +87,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations .Setup(s => s.GetConfigurationFor(It.IsAny())) .Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))) .Callback(() => getConfiguration = ++counter); - settings.SessionMode = SessionMode.Server; sut.ActionRequired += (args) => { if (args is ExamSelectionEventArgs e) @@ -119,9 +120,9 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations Assert.AreEqual(exam.Url, context.Next.Settings.Browser.StartUrl); Assert.AreSame(examSettings, context.Next.Settings); Assert.AreSame(serverSettings, context.Next.Settings.Server); - Assert.AreNotSame(settings, context.Next.Settings); + Assert.AreNotSame(initialSettings, context.Next.Settings); Assert.AreEqual(OperationResult.Success, result); - Assert.AreEqual(SessionMode.Server, settings.SessionMode); + Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode); } [TestMethod] @@ -130,14 +131,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; var examSettings = new AppSettings(); + var initialSettings = context.Next.Settings; configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.UnexpectedError); + context.Next.Settings.SessionMode = SessionMode.Server; server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); server.Setup(s => s.Initialize(It.IsAny())); server.Setup(s => s.GetConnectionInfo()).Returns(connection); server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))); - settings.SessionMode = SessionMode.Server; sut.ActionRequired += (args) => { if (args is ExamSelectionEventArgs e) @@ -162,7 +164,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations Assert.AreNotEqual(exam.Id, context.Next.AppConfig.ServerExamId); Assert.AreNotEqual(exam.Url, context.Next.Settings.Browser.StartUrl); Assert.AreNotSame(examSettings, context.Next.Settings); - Assert.AreSame(settings, context.Next.Settings); + Assert.AreSame(initialSettings, context.Next.Settings); Assert.AreEqual(OperationResult.Failed, result); Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode); } @@ -176,12 +178,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations var messageShown = false; configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); server.Setup(s => s.Initialize(It.IsAny())); server.Setup(s => s.GetConnectionInfo()).Returns(connection); server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(false, default(Uri))); - settings.SessionMode = SessionMode.Server; sut.ActionRequired += (args) => { if (args is ExamSelectionEventArgs e) @@ -219,12 +221,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations var messageShown = false; configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); server.Setup(s => s.Initialize(It.IsAny())); server.Setup(s => s.GetConnectionInfo()).Returns(connection); server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(false, default(Uri))); - settings.SessionMode = SessionMode.Server; sut.ActionRequired += (args) => { if (args is ExamSelectionEventArgs e) @@ -254,13 +256,44 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations Assert.AreEqual(OperationResult.Success, result); } + [TestMethod] + public void Perform_MustAutomaticallySelectExam() + { + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSettings = new AppSettings(); + var serverSettings = context.Next.Settings.Server; + + configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; + context.Next.Settings.Server.ExamId = "some id"; + fileSystem.Setup(f => f.Delete(It.IsAny())); + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); + server.Setup(s => s.Initialize(It.IsAny())); + server.Setup(s => s.GetConnectionInfo()).Returns(connection); + server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, new [] { exam })); + server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))); + sut.ActionRequired += (args) => Assert.Fail(); + + var result = sut.Perform(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Once); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Once); + + Assert.AreEqual(OperationResult.Success, result); + } + [TestMethod] public void Perform_MustDoNothingIfNormalSession() { - var eventFired = false; + var initialSettings = context.Next.Settings; - settings.SessionMode = SessionMode.Normal; - sut.ActionRequired += (_) => eventFired = true; + context.Next.Settings.SessionMode = SessionMode.Normal; + sut.ActionRequired += (_) => Assert.Fail(); var result = sut.Perform(); @@ -268,20 +301,253 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations fileSystem.VerifyNoOtherCalls(); server.VerifyNoOtherCalls(); - Assert.IsFalse(eventFired); - Assert.AreSame(settings, context.Next.Settings); + Assert.AreSame(initialSettings, context.Next.Settings); Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode); Assert.AreEqual(OperationResult.Success, result); } [TestMethod] - public void Repeat_MustDoNothingIfNormalSession() + public void Repeat_MustCorrectlyInitializeServerSession() { - var eventFired = false; + var connect = 0; + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var counter = 0; + var delete = 0; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSelection = 0; + var examSettings = new AppSettings(); + var getConfiguration = 0; + var getConnection = 0; + var getExams = 0; + var initialize = 0; + var initialSettings = context.Next.Settings; + var serverSettings = context.Next.Settings.Server; - context.Current.Settings.SessionMode = SessionMode.Normal; - settings.SessionMode = SessionMode.Normal; - sut.ActionRequired += (_) => eventFired = true; + configuration + .Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())) + .Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; + fileSystem.Setup(f => f.Delete(It.IsAny())).Callback(() => delete = ++counter); + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)).Callback(() => connect = ++counter); + server.Setup(s => s.Initialize(It.IsAny())).Callback(() => initialize = ++counter); + server.Setup(s => s.GetConnectionInfo()).Returns(connection).Callback(() => getConnection = ++counter); + server + .Setup(s => s.GetAvailableExams(It.IsAny())) + .Returns(new ServerResponse>(true, default(IEnumerable))) + .Callback(() => getExams = ++counter); + server + .Setup(s => s.GetConfigurationFor(It.IsAny())) + .Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))) + .Callback(() => getConfiguration = ++counter); + sut.ActionRequired += (args) => + { + if (args is ExamSelectionEventArgs e) + { + e.Success = true; + e.SelectedExam = exam; + examSelection = ++counter; + } + }; + + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Once); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Once); + + Assert.AreEqual(1, initialize); + Assert.AreEqual(2, connect); + Assert.AreEqual(3, getExams); + Assert.AreEqual(4, examSelection); + Assert.AreEqual(5, getConfiguration); + Assert.AreEqual(6, getConnection); + Assert.AreEqual(7, delete); + Assert.AreEqual(connection.Api, context.Next.AppConfig.ServerApi); + Assert.AreEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken); + Assert.AreEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token); + Assert.AreEqual(exam.Id, context.Next.AppConfig.ServerExamId); + Assert.AreEqual(exam.Url, context.Next.Settings.Browser.StartUrl); + Assert.AreSame(examSettings, context.Next.Settings); + Assert.AreSame(serverSettings, context.Next.Settings.Server); + Assert.AreNotSame(initialSettings, context.Next.Settings); + Assert.AreEqual(OperationResult.Success, result); + Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode); + } + + [TestMethod] + public void Repeat_MustFailIfSettingsCouldNotBeLoaded() + { + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSettings = new AppSettings(); + var initialSettings = context.Next.Settings; + + configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.UnexpectedError); + context.Next.Settings.SessionMode = SessionMode.Server; + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); + server.Setup(s => s.Initialize(It.IsAny())); + server.Setup(s => s.GetConnectionInfo()).Returns(connection); + server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); + server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))); + sut.ActionRequired += (args) => + { + if (args is ExamSelectionEventArgs e) + { + e.Success = true; + e.SelectedExam = exam; + } + }; + + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Once); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Once); + + Assert.AreNotEqual(connection.Api, context.Next.AppConfig.ServerApi); + Assert.AreNotEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken); + Assert.AreNotEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token); + Assert.AreNotEqual(exam.Id, context.Next.AppConfig.ServerExamId); + Assert.AreNotEqual(exam.Url, context.Next.Settings.Browser.StartUrl); + Assert.AreNotSame(examSettings, context.Next.Settings); + Assert.AreSame(initialSettings, context.Next.Settings); + Assert.AreEqual(OperationResult.Failed, result); + Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode); + } + + [TestMethod] + public void Repeat_MustCorrectlyAbort() + { + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSettings = new AppSettings(); + var messageShown = false; + + configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); + server.Setup(s => s.Initialize(It.IsAny())); + server.Setup(s => s.GetConnectionInfo()).Returns(connection); + server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); + server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(false, default(Uri))); + sut.ActionRequired += (args) => + { + if (args is ExamSelectionEventArgs e) + { + e.Success = true; + e.SelectedExam = exam; + } + + if (args is ServerFailureEventArgs s) + { + s.Abort = true; + messageShown = true; + } + }; + + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Never); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Never); + + Assert.IsTrue(messageShown); + Assert.AreEqual(OperationResult.Aborted, result); + } + + [TestMethod] + public void Repeat_MustCorrectlyFallback() + { + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSettings = new AppSettings(); + var messageShown = false; + + configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); + server.Setup(s => s.Initialize(It.IsAny())); + server.Setup(s => s.GetConnectionInfo()).Returns(connection); + server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, default(IEnumerable))); + server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(false, default(Uri))); + sut.ActionRequired += (args) => + { + if (args is ExamSelectionEventArgs e) + { + e.Success = true; + e.SelectedExam = exam; + } + + if (args is ServerFailureEventArgs s) + { + s.Fallback = true; + messageShown = true; + } + }; + + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Never); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Never); + + Assert.IsTrue(messageShown); + Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode); + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Repeat_MustAutomaticallySelectExam() + { + var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" }; + var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" }; + var examSettings = new AppSettings(); + var serverSettings = context.Next.Settings.Server; + + configuration.Setup(c => c.TryLoadSettings(It.IsAny(), out examSettings, It.IsAny())).Returns(LoadStatus.Success); + context.Next.Settings.SessionMode = SessionMode.Server; + context.Next.Settings.Server.ExamId = "some id"; + fileSystem.Setup(f => f.Delete(It.IsAny())); + server.Setup(s => s.Connect()).Returns(new ServerResponse(true)); + server.Setup(s => s.Initialize(It.IsAny())); + server.Setup(s => s.GetConnectionInfo()).Returns(connection); + server.Setup(s => s.GetAvailableExams(It.IsAny())).Returns(new ServerResponse>(true, new[] { exam })); + server.Setup(s => s.GetConfigurationFor(It.IsAny())).Returns(new ServerResponse(true, new Uri("file:///configuration.seb"))); + sut.ActionRequired += (args) => Assert.Fail(); + + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.IsAny()), Times.Once); + server.Verify(s => s.Connect(), Times.Once); + server.Verify(s => s.Initialize(It.IsAny()), Times.Once); + server.Verify(s => s.GetAvailableExams(It.IsAny()), Times.Once); + server.Verify(s => s.GetConfigurationFor(It.Is(e => e == exam)), Times.Once); + server.Verify(s => s.GetConnectionInfo(), Times.Once); + + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Repeat_MustKeepExistingServerSession() + { + context.Current.AppConfig.ServerApi = "api"; + context.Current.AppConfig.ServerConnectionToken = "token"; + context.Current.AppConfig.ServerExamId = "id"; + context.Current.AppConfig.ServerOauth2Token = "oauth2"; + context.Current.Settings.SessionMode = SessionMode.Server; + context.Next.Settings.SessionMode = SessionMode.Server; var result = sut.Repeat(); @@ -289,19 +555,68 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations fileSystem.VerifyNoOtherCalls(); server.VerifyNoOtherCalls(); - Assert.IsFalse(eventFired); - Assert.AreSame(settings, context.Next.Settings); + Assert.AreEqual(context.Current.AppConfig.ServerApi, context.Next.AppConfig.ServerApi); + Assert.AreEqual(context.Current.AppConfig.ServerConnectionToken, context.Next.AppConfig.ServerConnectionToken); + Assert.AreEqual(context.Current.AppConfig.ServerExamId, context.Next.AppConfig.ServerExamId); + Assert.AreEqual(context.Current.AppConfig.ServerOauth2Token, context.Next.AppConfig.ServerOauth2Token); + Assert.AreSame(context.Current.Settings.Server, context.Next.Settings.Server); + Assert.AreEqual(OperationResult.Success, result); + Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode); + } + + [TestMethod] + public void Repeat_MustDoNothingIfNormalSession() + { + var initialSettings = context.Next.Settings; + + context.Current.Settings.SessionMode = SessionMode.Normal; + context.Next.Settings.SessionMode = SessionMode.Normal; + sut.ActionRequired += (_) => Assert.Fail(); + + var result = sut.Repeat(); + + configuration.VerifyNoOtherCalls(); + fileSystem.VerifyNoOtherCalls(); + server.VerifyNoOtherCalls(); + + Assert.AreSame(initialSettings, context.Next.Settings); Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode); Assert.AreEqual(OperationResult.Success, result); } + [TestMethod] + public void Revert_MustDisconnectFromServer() + { + context.Current.Settings.SessionMode = SessionMode.Server; + server.Setup(s => s.Disconnect()).Returns(new ServerResponse(true)); + + var result = sut.Revert(); + + fileSystem.VerifyNoOtherCalls(); + server.Verify(s => s.Disconnect(), Times.Once); + + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Revert_MustFailWhenDisconnectionUnsuccesful() + { + context.Current.Settings.SessionMode = SessionMode.Server; + server.Setup(s => s.Disconnect()).Returns(new ServerResponse(false)); + + var result = sut.Revert(); + + fileSystem.VerifyNoOtherCalls(); + server.Verify(s => s.Disconnect(), Times.Once); + + Assert.AreEqual(OperationResult.Failed, result); + } + [TestMethod] public void Revert_MustDoNothingIfNormalSession() { - var eventFired = false; - context.Current.Settings.SessionMode = SessionMode.Normal; - sut.ActionRequired += (_) => eventFired = true; + sut.ActionRequired += (_) => Assert.Fail(); var result = sut.Revert(); @@ -309,7 +624,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations fileSystem.VerifyNoOtherCalls(); server.VerifyNoOtherCalls(); - Assert.IsFalse(eventFired); Assert.AreEqual(OperationResult.Success, result); } } diff --git a/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs b/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs index f1749879..eb8af972 100644 --- a/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs @@ -7,6 +7,8 @@ */ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Communication.Contracts.Data; @@ -19,6 +21,7 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Operations.Events; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings; using SafeExamBrowser.Settings.Security; using SafeExamBrowser.Settings.Service; @@ -216,6 +219,57 @@ namespace SafeExamBrowser.Runtime.UnitTests Assert.IsTrue(args.AbortStartup); } + [TestMethod] + public void Operations_MustRequestServerExamSelectionViaDialogOnDefaultDesktop() + { + var args = new ExamSelectionEventArgs(Enumerable.Empty()); + var examSelectionDialog = new Mock(); + var result = new ExamSelectionDialogResult { SelectedExam = new Exam(), Success = true }; + + currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell; + examSelectionDialog.Setup(p => p.Show(It.IsAny())).Returns(result); + uiFactory.Setup(u => u.CreateExamSelectionDialog(It.IsAny>())).Returns(examSelectionDialog.Object); + + sut.TryStart(); + sessionSequence.Raise(s => s.ActionRequired += null, args); + + clientProxy.VerifyNoOtherCalls(); + examSelectionDialog.Verify(p => p.Show(It.IsAny()), Times.Once); + uiFactory.Verify(u => u.CreateExamSelectionDialog(It.IsAny>()), Times.Once); + + Assert.AreEqual(true, args.Success); + Assert.AreEqual(result.SelectedExam, args.SelectedExam); + } + + [TestMethod] + public void Operations_MustRequestServerExamSelectionViaClientOnNewDesktop() + { + var args = new ExamSelectionEventArgs(new[] { new Exam { Id = "abc1234" } }); + var examSelectionReceived = new Action, Guid>((e, id) => + { + runtimeHost.Raise(r => r.ExamSelectionReceived += null, new ExamSelectionReplyEventArgs + { + RequestId = id, + SelectedExamId = "abc1234", + Success = true + }); + }); + + currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop; + clientProxy + .Setup(c => c.RequestExamSelection(It.IsAny>(), It.IsAny())) + .Returns(new CommunicationResult(true)) + .Callback(examSelectionReceived); + + sut.TryStart(); + sessionSequence.Raise(s => s.ActionRequired += null, args); + + clientProxy.Verify(c => c.RequestExamSelection(It.IsAny>(), It.IsAny()), Times.Once); + uiFactory.Verify(u => u.CreateExamSelectionDialog(It.IsAny>()), Times.Never); + + Assert.AreEqual("abc1234", args.SelectedExam.Id); + } + [TestMethod] public void Operations_MustRequestPasswordViaDialogOnDefaultDesktop() { @@ -244,7 +298,7 @@ namespace SafeExamBrowser.Runtime.UnitTests var args = new PasswordRequiredEventArgs(); var passwordReceived = new Action((p, id) => { - runtimeHost.Raise(r => r.PasswordReceived += null, new PasswordReplyEventArgs { RequestId = id, Success = true }); + runtimeHost.Raise(r => r.PasswordReceived += null, new PasswordReplyEventArgs { Password = "test", RequestId = id, Success = true }); }); currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop; @@ -255,6 +309,61 @@ namespace SafeExamBrowser.Runtime.UnitTests clientProxy.Verify(c => c.RequestPassword(It.IsAny(), It.IsAny()), Times.Once); uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny(), It.IsAny()), Times.Never); + + Assert.AreEqual("test", args.Password); + } + + [TestMethod] + public void Operations_MustRequestServerFailureActionViaDialogOnDefaultDesktop() + { + var args = new ServerFailureEventArgs(default(string), default(bool)); + var failureDialog = new Mock(); + var result = new ServerFailureDialogResult { Fallback = true, Success = true }; + + currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell; + failureDialog.Setup(p => p.Show(It.IsAny())).Returns(result); + uiFactory.Setup(u => u.CreateServerFailureDialog(It.IsAny(), It.IsAny())).Returns(failureDialog.Object); + + sut.TryStart(); + sessionSequence.Raise(s => s.ActionRequired += null, args); + + clientProxy.VerifyNoOtherCalls(); + failureDialog.Verify(p => p.Show(It.IsAny()), Times.Once); + uiFactory.Verify(u => u.CreateServerFailureDialog(It.IsAny(), It.IsAny()), Times.Once); + + Assert.AreEqual(result.Abort, args.Abort); + Assert.AreEqual(result.Fallback, args.Fallback); + Assert.AreEqual(result.Retry, args.Retry); + } + + [TestMethod] + public void Operations_MustRequestServerFailureActionViaClientOnNewDesktop() + { + var args = new ServerFailureEventArgs(default(string), default(bool)); + var failureActionReceived = new Action((m, f, id) => + { + runtimeHost.Raise(r => r.ServerFailureActionReceived += null, new ServerFailureActionReplyEventArgs + { + RequestId = id, + Fallback = true + }); + }); + + currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop; + clientProxy + .Setup(c => c.RequestServerFailureAction(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new CommunicationResult(true)) + .Callback(failureActionReceived); + + sut.TryStart(); + sessionSequence.Raise(s => s.ActionRequired += null, args); + + clientProxy.Verify(c => c.RequestServerFailureAction(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + uiFactory.Verify(u => u.CreateServerFailureDialog(It.IsAny(), It.IsAny()), Times.Never); + + Assert.IsFalse(args.Abort); + Assert.IsTrue(args.Fallback); + Assert.IsFalse(args.Retry); } [TestMethod] @@ -383,6 +492,39 @@ namespace SafeExamBrowser.Runtime.UnitTests runtimeWindow.Verify(s => s.UpdateStatus(It.Is(k => k == key), It.IsAny()), Times.Once); } + [TestMethod] + public void Session_MustHideRuntimeWindowWhenUsingDisableExplorerShell() + { + currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell; + StartSession(); + runtimeWindow.Verify(w => w.Hide(), Times.AtLeastOnce); + + runtimeWindow.Reset(); + sessionSequence.Reset(); + + sessionSequence.Setup(b => b.TryRepeat()).Returns(OperationResult.Aborted); + runtimeHost.Raise(h => h.ReconfigurationRequested += null, new ReconfigurationEventArgs()); + runtimeWindow.Verify(w => w.Hide(), Times.AtLeastOnce); + } + + [TestMethod] + public void Session_MustShowMessageBoxOnFailure() + { + bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success); + sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Failed); + sessionContext.Current = null; + sut.TryStart(); + messageBox.Verify(m => m.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeastOnce); + + StartSession(); + messageBox.Reset(); + sessionSequence.Reset(); + + sessionSequence.Setup(b => b.TryRepeat()).Returns(OperationResult.Failed); + runtimeHost.Raise(h => h.ReconfigurationRequested += null, new ReconfigurationEventArgs()); + messageBox.Verify(m => m.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeastOnce); + } + [TestMethod] public void ServiceProxy_MustShutdownWhenConnectionLostAndMandatory() { @@ -578,6 +720,47 @@ namespace SafeExamBrowser.Runtime.UnitTests shutdown.Verify(s => s(), Times.Never); } + [TestMethod] + public void Startup_MustUpdateProgressForBootstrapSequence() + { + var args = new ProgressChangedEventArgs + { + CurrentValue = 12, + IsIndeterminate = true, + MaxValue = 100, + Progress = true, + Regress = true + }; + + bootstrapSequence + .Setup(b => b.TryPerform()) + .Returns(OperationResult.Success) + .Callback(() => { bootstrapSequence.Raise(s => s.ProgressChanged += null, args); }); + + var success = sut.TryStart(); + + splashScreen.Verify(s => s.SetValue(It.Is(i => i == args.CurrentValue)), Times.Once); + splashScreen.Verify(s => s.SetIndeterminate(), Times.Once); + splashScreen.Verify(s => s.SetMaxValue(It.Is(i => i == args.MaxValue)), Times.Once); + splashScreen.Verify(s => s.Progress(), Times.Once); + splashScreen.Verify(s => s.Regress(), Times.Once); + } + + [TestMethod] + public void Startup_MustUpdateStatusForBootstrapSequence() + { + var key = TextKey.OperationStatus_InitializeBrowser; + + bootstrapSequence + .Setup(b => b.TryPerform()) + .Returns(OperationResult.Success) + .Callback(() => { bootstrapSequence.Raise(s => s.StatusChanged += null, key); }); + + var success = sut.TryStart(); + + splashScreen.Verify(s => s.UpdateStatus(It.Is(k => k == key), true), Times.Once); + } + private void StartSession() { bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success); diff --git a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs index a21cd7e4..a55a3568 100644 --- a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs @@ -108,23 +108,20 @@ namespace SafeExamBrowser.Runtime.Operations } } } - else - { - logger.Info("The user aborted the exam selection."); - result = OperationResult.Aborted; - } } } if (abort) { result = OperationResult.Aborted; + logger.Info("The user aborted the server operation."); } if (fallback) { Context.Next.Settings.SessionMode = SessionMode.Normal; result = OperationResult.Success; + logger.Info("The user chose to fallback and start a normal session."); } }