From daf5d113e3ca6dc7f51923541f5bf4ae8dd7d1cd Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 20 Apr 2020 13:12:49 -0400 Subject: [PATCH] Add tests for RepositoryFactory --- .../Components/InstanceFactory.cs | 13 ++- .../Components/InstanceManager.cs | 21 ++++- .../Components/Repository/Repository.cs | 6 +- .../Repository/RepositoryManager.cs | 3 +- .../System/IPlatformIdentifier.cs | 5 - .../System/PlatformIdentifier.cs | 51 +---------- .../Repository/TestRepositoryFactory.cs | 91 +++++++++++++++++++ 7 files changed, 129 insertions(+), 61 deletions(-) create mode 100644 tests/Tgstation.Server.Host.Tests/Components/Repository/TestRepositoryFactory.cs diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index 4d02e969b6..c8774659b6 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -250,12 +250,21 @@ namespace Tgstation.Server.Host.Components throw; } } - #pragma warning restore CA1506 +#pragma warning restore CA1506 /// - public Task StartAsync(CancellationToken cancellationToken) => byondInstaller.CleanCache(cancellationToken); + public Task StartAsync(CancellationToken cancellationToken) + { + CheckSystemCompatibility(); + return byondInstaller.CleanCache(cancellationToken); + } /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + /// + /// Test that the is functional. + /// + private void CheckSystemCompatibility() => repositoryFactory.CreateInMemory().Dispose(); } } diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index d9c45691d1..b0c15cacd8 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -10,6 +10,7 @@ using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; +using Tgstation.Server.Host.Security; using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.Components @@ -52,6 +53,11 @@ namespace Tgstation.Server.Host.Components /// readonly IPlatformIdentifier platformIdentifier; + /// + /// The for the + /// + readonly ISystemIdentityFactory systemIdentityFactory; + /// /// The for the /// @@ -82,6 +88,7 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of . + /// The value of . /// The value of public InstanceManager( IInstanceFactory instanceFactory, @@ -91,6 +98,7 @@ namespace Tgstation.Server.Host.Components IJobManager jobManager, IServerControl serverControl, IPlatformIdentifier platformIdentifier, + ISystemIdentityFactory systemIdentityFactory, ILogger logger) { this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); @@ -100,6 +108,7 @@ namespace Tgstation.Server.Host.Components this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl)); this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier)); + this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); serverControl.RegisterForRestart(this); @@ -228,7 +237,7 @@ namespace Tgstation.Server.Host.Components { try { - platformIdentifier.CheckCompatibility(); + CheckSystemCompatibility(); var factoryStartup = instanceFactory.StartAsync(cancellationToken); await databaseContext.Initialize(cancellationToken).ConfigureAwait(false); await jobManager.StartAsync(cancellationToken).ConfigureAwait(false); @@ -283,5 +292,15 @@ namespace Tgstation.Server.Host.Components downgradeVersion = updateVersion != null && updateVersion < application.Version ? updateVersion : null; return Task.CompletedTask; } + + /// + /// Check we have a valid system identity. + /// + private void CheckSystemCompatibility() + { + using var systemIdentity = systemIdentityFactory.GetCurrent(); + if (!systemIdentity.CanCreateSymlinks) + throw new InvalidOperationException("The user running tgstation-server cannot create symlinks! Please try running as an administrative user!"); + } } } diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index dd64bcdb19..d180d541d7 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -175,7 +175,7 @@ namespace Tgstation.Server.Host.Components.Repository progressReporter(50 + ((int)(50 * ((float)sentBytes) / totalBytes))); return !cancellationToken.IsCancellationRequested; }, - CredentialsProvider = credentialsProvider.GenerateHandler(username, password) + CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password) }; /// @@ -262,7 +262,7 @@ namespace Tgstation.Server.Host.Components.Repository return !cancellationToken.IsCancellationRequested; }, OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, - CredentialsProvider = credentialsProvider.GenerateHandler(username, password) + CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password) }, logMessage); } catch (UserCancelledException) { } @@ -375,7 +375,7 @@ namespace Tgstation.Server.Host.Components.Repository return !cancellationToken.IsCancellationRequested; }, OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, - CredentialsProvider = credentialsProvider.GenerateHandler(username, password) + CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password) }, "Fetch origin commits"); } catch (UserCancelledException) diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs index f495a56693..06ce0f0f41 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -70,11 +70,12 @@ namespace Tgstation.Server.Host.Components.Repository ILogger logger, RepositorySettings repositorySettings) { - this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings)); + this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings)); semaphore = new SemaphoreSlim(1); } diff --git a/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs b/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs index b42b0a8a19..2036796994 100644 --- a/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs +++ b/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs @@ -14,10 +14,5 @@ /// The extension of executable script files for the system /// string ScriptFileExtension { get; } - - /// - /// Check if the system is capable of running tgstation-server. - /// - void CheckCompatibility(); } } diff --git a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs index 04c5ee01cb..c2ec1382aa 100644 --- a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs +++ b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs @@ -1,8 +1,4 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Runtime.InteropServices; -using Tgstation.Server.Host.Components.Repository; -using Tgstation.Server.Host.Security; +using System.Runtime.InteropServices; namespace Tgstation.Server.Host.System { @@ -15,56 +11,13 @@ namespace Tgstation.Server.Host.System /// public string ScriptFileExtension { get; } - /// - /// The for the . - /// - readonly ISystemIdentityFactory systemIdentityFactory; - - /// - /// The for the . - /// - readonly IRepositoryFactory repositoryFactory; - - /// - /// The for the . - /// - readonly ILogger logger; - /// /// Construct a /// - /// The value of . - /// The value of . - /// The value of . - public PlatformIdentifier( - ISystemIdentityFactory systemIdentityFactory, - IRepositoryFactory repositoryFactory, - ILogger logger) + public PlatformIdentifier() { - this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); - this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory)); - this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); - IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); ScriptFileExtension = IsWindows ? "bat" : "sh"; } - - /// - public void CheckCompatibility() - { - try - { - repositoryFactory.CreateInMemory().Dispose(); - } - catch - { - logger.LogCritical("Unable to initialize libgit2! This is a common problem on POSIX installations. Try using Docker."); - throw; - } - - using var systemIdentity = systemIdentityFactory.GetCurrent(); - if (!systemIdentity.CanCreateSymlinks) - throw new InvalidOperationException("The user running tgstation-server cannot create symlinks! Please try running as an administrative user!"); - } } } diff --git a/tests/Tgstation.Server.Host.Tests/Components/Repository/TestRepositoryFactory.cs b/tests/Tgstation.Server.Host.Tests/Components/Repository/TestRepositoryFactory.cs new file mode 100644 index 0000000000..1434f24e0a --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Components/Repository/TestRepositoryFactory.cs @@ -0,0 +1,91 @@ +using LibGit2Sharp; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Repository.Tests +{ + /// + /// Tests for . + /// + [TestClass] + public sealed class TestRepositoryFactory + { + static IRepositoryFactory CreateFactory() => new RepositoryFactory(Mock.Of>()); + + static Task TestRepoLoading( + string path, + IRepositoryFactory repositoryFactory = null) => + (repositoryFactory ?? CreateFactory()) + .CreateFromPath(path, default); + + [TestMethod] + public void TestConstructionThrows() => Assert.ThrowsException(() => new RepositoryFactory(null)); + + [TestMethod] + public void TestInMemoryRepoCreation() + { + using var repo = new RepositoryFactory(Mock.Of>()).CreateInMemory(); + } + + [TestMethod] + public async Task TestLoadedRepoCreation() + { + using var repo = await TestRepoLoading(Path.GetFullPath("../../../../..")); + + // Checking the commit previous to the one that creates this function exists + var gitObject = repo.Lookup("4802806bdccee27cb3d5fa8c2d0dfbdc117fdb4d"); + Assert.IsNotNull(gitObject); + var commit = gitObject.Peel(); + + Assert.AreEqual("Cleanup repository management code", commit.Message.Trim()); + } + + [TestMethod] + public async Task TestCloning() + { + var tempDir = Path.GetTempFileName(); + File.Delete(tempDir); + try + { + var factory = CreateFactory(); + await factory.Clone( + new Uri("https://github.com/Cyberboss/Test"), + new CloneOptions + { + CredentialsProvider = factory.GenerateCredentialsHandler(null, null) + }, + tempDir, + default); + + using (var repo = await TestRepoLoading(tempDir)) + { + var gitObject = repo.Lookup("f636418bf47d238d33b0e4a34f0072b23a8aad0e"); + Assert.IsNotNull(gitObject); + var commit = gitObject.Peel(); + + Assert.AreEqual("Update Test.md", commit.Message); + } + } + finally + { + // Takes a while to release the repo handle sometimes... + for (var i = 0; i < 5; ++i) + { + try + { + Directory.Delete(tempDir, true); + break; + } + catch (UnauthorizedAccessException) + { + await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false); + } + } + } + } + } +}