/* * Copyright (c) 2021 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.Collections.Generic; using System.IO; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Configuration.ConfigurationData; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.DataFormats; using SafeExamBrowser.Configuration.Contracts.DataResources; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Configuration.UnitTests { [TestClass] public class ConfigurationRepositoryTests { private ConfigurationRepository sut; private Mock<IDataParser> binaryParser; private Mock<IDataSerializer> binarySerializer; private Mock<ICertificateStore> certificateStore; private Mock<IResourceLoader> fileLoader; private Mock<IResourceSaver> fileSaver; private Mock<IHashAlgorithm> hashAlgorithm; private Mock<IModuleLogger> logger; private Mock<IResourceLoader> networkLoader; private Mock<IDataParser> xmlParser; private Mock<IDataSerializer> xmlSerializer; [TestInitialize] public void Initialize() { binaryParser = new Mock<IDataParser>(); binarySerializer = new Mock<IDataSerializer>(); certificateStore = new Mock<ICertificateStore>(); fileLoader = new Mock<IResourceLoader>(); fileSaver = new Mock<IResourceSaver>(); hashAlgorithm = new Mock<IHashAlgorithm>(); logger = new Mock<IModuleLogger>(); networkLoader = new Mock<IResourceLoader>(); xmlParser = new Mock<IDataParser>(); xmlSerializer = new Mock<IDataSerializer>(); fileLoader.Setup(f => f.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile); fileSaver.Setup(f => f.CanSave(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile); networkLoader.Setup(n => n.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.Scheme.Equals("http") || u.Scheme.Equals("seb")); SetEntryAssembly(); sut = new ConfigurationRepository(certificateStore.Object, hashAlgorithm.Object, logger.Object); sut.InitializeAppConfig(); } [TestMethod] public void ConfigureClient_MustWorkAsExpected() { var stream = new MemoryStream() as Stream; var password = new PasswordParameters { Password = "test123" }; var parseResult = new ParseResult { Format = FormatType.Binary, RawData = new Dictionary<string, object>(), Status = LoadStatus.Success }; var serializeResult = new SerializeResult { Data = new MemoryStream(), Status = SaveStatus.Success }; RegisterModules(); fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true); binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult); binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true); binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<PasswordParameters>())).Returns(serializeResult); fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success); var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password); fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once); binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once); certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Once); binarySerializer.Verify(b => b.TrySerialize( It.IsAny<Dictionary<string, object>>(), It.Is<PasswordParameters>(p => p.IsHash == true && p.Password == string.Empty)), Times.Once); fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Once); Assert.AreEqual(SaveStatus.Success, status); } [TestMethod] public void ConfigureClient_MustKeepSameEncryptionAccordingToConfiguration() { var stream = new MemoryStream() as Stream; var password = new PasswordParameters { Password = "test123" }; var parseResult = new ParseResult { Encryption = new PublicKeyParameters { InnerEncryption = password, SymmetricEncryption = true }, Format = FormatType.Binary, RawData = new Dictionary<string, object> { { Keys.ConfigurationFile.KeepClientConfigEncryption, true } }, Status = LoadStatus.Success }; var serializeResult = new SerializeResult { Data = new MemoryStream(), Status = SaveStatus.Success }; RegisterModules(); fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true); binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult); binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true); binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>())).Returns(serializeResult); fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success); var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password); binarySerializer.Verify(b => b.TrySerialize( It.IsAny<Dictionary<string, object>>(), It.Is<PublicKeyParameters>(p => p.InnerEncryption == password && p.SymmetricEncryption)), Times.Once); Assert.AreEqual(SaveStatus.Success, status); } [TestMethod] public void ConfigureClient_MustAbortProcessOnError() { var stream = new MemoryStream() as Stream; var password = new PasswordParameters { Password = "test123" }; var parseResult = new ParseResult { Format = FormatType.Binary, RawData = new Dictionary<string, object>(), Status = LoadStatus.Success }; var serializeResult = new SerializeResult { Data = new MemoryStream(), Status = SaveStatus.Success }; RegisterModules(); fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>(); var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password); fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once); binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never); certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Never); binarySerializer.Verify(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>()), Times.Never); fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Never); Assert.AreEqual(SaveStatus.UnexpectedError, status); } [TestMethod] public void TryLoad_MustWorkAsExpected() { var stream = new MemoryStream() as Stream; var parseResult = new ParseResult { RawData = new Dictionary<string, object>(), Status = LoadStatus.Success }; RegisterModules(); networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true); binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult); var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once); fileLoader.Verify(f => f.TryLoad(It.IsAny<Uri>(), out stream), Times.Never); networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once); networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once); binaryParser.Verify(b => b.CanParse(It.IsAny<Stream>()), Times.Once); binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once); xmlParser.Verify(x => x.CanParse(It.IsAny<Stream>()), Times.AtMostOnce); xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never); Assert.AreEqual(LoadStatus.Success, result); } [TestMethod] public void TryLoad_MustReportPasswordNeed() { var stream = new MemoryStream() as Stream; var parseResult = new ParseResult { Status = LoadStatus.PasswordNeeded }; RegisterModules(); networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true); binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult); var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); Assert.AreEqual(LoadStatus.PasswordNeeded, result); } [TestMethod] public void TryLoad_MustNotFailToIfNoLoaderRegistered() { var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); Assert.AreEqual(LoadStatus.NotSupported, result); sut.Register(fileLoader.Object); sut.Register(networkLoader.Object); result = sut.TryLoadSettings(new Uri("ftp://www.blubb.org"), out _); fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once); networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once); Assert.AreEqual(LoadStatus.NotSupported, result); } [TestMethod] public void TryLoad_MustNotFailIfNoParserRegistered() { var data = default(Stream); networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success); sut.Register(networkLoader.Object); var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out data), Times.Once); Assert.AreEqual(LoadStatus.NotSupported, result); } [TestMethod] public void TryLoad_MustNotFailInCaseOfUnexpectedError() { var data = default(Stream); networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Throws<Exception>(); sut.Register(networkLoader.Object); var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); Assert.AreEqual(LoadStatus.UnexpectedError, result); binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>(); networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success); sut.Register(binaryParser.Object); result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _); Assert.AreEqual(LoadStatus.UnexpectedError, result); } [TestMethod] public void MustInitializeSessionConfiguration() { var appConfig = sut.InitializeAppConfig(); var configuration = sut.InitializeSessionConfiguration(); Assert.IsNull(configuration.Settings); Assert.IsInstanceOfType(configuration.AppConfig, typeof(AppConfig)); Assert.IsInstanceOfType(configuration.ClientAuthenticationToken, typeof(Guid)); Assert.IsInstanceOfType(configuration.SessionId, typeof(Guid)); } [TestMethod] public void MustUpdateAppConfig() { var appConfig = sut.InitializeAppConfig(); var clientAddress = appConfig.ClientAddress; var clientId = appConfig.ClientId; var clientLogFilePath = appConfig.ClientLogFilePath; var runtimeAddress = appConfig.RuntimeAddress; var runtimeId = appConfig.RuntimeId; var runtimeLogFilePath = appConfig.RuntimeLogFilePath; var serviceEventName = appConfig.ServiceEventName; var configuration = sut.InitializeSessionConfiguration(); Assert.AreEqual(configuration.AppConfig.ClientLogFilePath, clientLogFilePath); Assert.AreEqual(configuration.AppConfig.RuntimeAddress, runtimeAddress); Assert.AreEqual(configuration.AppConfig.RuntimeId, runtimeId); Assert.AreEqual(configuration.AppConfig.RuntimeLogFilePath, runtimeLogFilePath); Assert.AreNotEqual(configuration.AppConfig.ClientAddress, clientAddress); Assert.AreNotEqual(configuration.AppConfig.ClientId, clientId); Assert.AreNotEqual(configuration.AppConfig.ServiceEventName, serviceEventName); } [TestMethod] public void MustUpdateSessionConfiguration() { var appConfig = sut.InitializeAppConfig(); var firstSession = sut.InitializeSessionConfiguration(); var secondSession = sut.InitializeSessionConfiguration(); var thirdSession = sut.InitializeSessionConfiguration(); Assert.AreNotEqual(firstSession.SessionId, secondSession.SessionId); Assert.AreNotEqual(firstSession.ClientAuthenticationToken, secondSession.ClientAuthenticationToken); Assert.AreNotEqual(secondSession.SessionId, thirdSession.SessionId); Assert.AreNotEqual(secondSession.ClientAuthenticationToken, thirdSession.ClientAuthenticationToken); } private void RegisterModules() { sut.Register(binaryParser.Object); sut.Register(binarySerializer.Object); sut.Register(fileLoader.Object); sut.Register(fileSaver.Object); sut.Register(networkLoader.Object); sut.Register(xmlParser.Object); sut.Register(xmlSerializer.Object); } /// <summary> /// Required for unit tests to be able to retrieve the <see cref="Assembly.GetEntryAssembly"/> while executing. /// </summary> public void SetEntryAssembly() { var assembly = Assembly.GetCallingAssembly(); var manager = new AppDomainManager(); var entryAssemblyfield = manager.GetType().GetField("m_entryAssembly", BindingFlags.Instance | BindingFlags.NonPublic); entryAssemblyfield.SetValue(manager, assembly); var domain = AppDomain.CurrentDomain; var domainManagerField = domain.GetType().GetField("_domainManager", BindingFlags.Instance | BindingFlags.NonPublic); domainManagerField.SetValue(domain, manager); } } }