From 6b253cf45cd62cbde20d056ebc1bf9c113d1881c Mon Sep 17 00:00:00 2001 From: Chris Cheetham Date: Tue, 15 Jun 2021 12:32:24 -0400 Subject: [PATCH] Add unit tests for new controller --- .editorconfig | 23 + ...ice.sln => Steeltoe.NetCoreToolService.sln | 35 +- ...teeltoe.NetCoreToolService.sln.DotSettings | 0 Version.props | 17 +- .../Diagnostics/CommandException.cs | 24 + .../Diagnostics/CommandExecutor.cs | 134 +++++ src/Common.Utils/Diagnostics/CommandResult.cs | 27 + .../Diagnostics/ICommandExecutor.cs | 24 + src/Common.Utils/IO/Steeltoe.Common.IO.csproj | 7 + .../IO/TempDirectory.cs | 25 +- .../Utils => Common.Utils}/IO/TempFile.cs | 25 +- .../Utils => Common.Utils}/IO/TempPath.cs | 35 +- src/Common.Utils/Steeltoe.Common.Utils.csproj | 5 + .../Archivers/ArchiverRegistry.cs | 58 -- .../Controllers/NewController.cs | 392 +++++++------ .../Models/TemplateDictionary.cs | 15 + src/NetCoreToolService/Models/TemplateInfo.cs | 33 ++ .../IArchiver.cs => Packagers/IPackager.cs} | 18 +- .../ZipPackager.cs} | 11 +- .../Services/IArchiverRegistry.cs | 25 - src/NetCoreToolService/Services/Service.cs | 28 - src/NetCoreToolService/Startup.cs | 5 +- ...roj => Steeltoe.NetCoreToolService.csproj} | 8 +- .../appsettings.Development.json | 1 + .../Diagnostics/CommandExecutorTest.cs | 40 ++ .../IO/Steeltoe.Common.IO.Test.csproj | 18 + .../Common.Utils.Test/IO/TempDirectoryTest.cs | 31 + test/Common.Utils.Test/IO/TempFileTest.cs | 30 + .../Steeltoe.Common.Utils.Test.csproj | 11 + test/Directory.Build.props | 24 +- .../Archivers/ArchiverRegistryTests.cs | 49 -- .../Controllers/NewControllerTest.cs | 553 ++++++++++++++++++ .../ZipPackagerTests.cs} | 30 +- ...> Steeltoe.NetCoreToolService.Test.csproj} | 6 +- 34 files changed, 1325 insertions(+), 442 deletions(-) create mode 100644 .editorconfig rename NetCoreToolService.sln => Steeltoe.NetCoreToolService.sln (55%) rename NetCoreToolService.sln.DotSettings => Steeltoe.NetCoreToolService.sln.DotSettings (100%) create mode 100644 src/Common.Utils/Diagnostics/CommandException.cs create mode 100644 src/Common.Utils/Diagnostics/CommandExecutor.cs create mode 100644 src/Common.Utils/Diagnostics/CommandResult.cs create mode 100644 src/Common.Utils/Diagnostics/ICommandExecutor.cs create mode 100644 src/Common.Utils/IO/Steeltoe.Common.IO.csproj rename src/{NetCoreToolService/Utils => Common.Utils}/IO/TempDirectory.cs (58%) rename src/{NetCoreToolService/Utils => Common.Utils}/IO/TempFile.cs (60%) rename src/{NetCoreToolService/Utils => Common.Utils}/IO/TempPath.cs (57%) create mode 100644 src/Common.Utils/Steeltoe.Common.Utils.csproj delete mode 100644 src/NetCoreToolService/Archivers/ArchiverRegistry.cs create mode 100644 src/NetCoreToolService/Models/TemplateDictionary.cs create mode 100644 src/NetCoreToolService/Models/TemplateInfo.cs rename src/NetCoreToolService/{Services/IArchiver.cs => Packagers/IPackager.cs} (51%) rename src/NetCoreToolService/{Archivers/ZipArchiver.cs => Packagers/ZipPackager.cs} (92%) delete mode 100644 src/NetCoreToolService/Services/IArchiverRegistry.cs delete mode 100644 src/NetCoreToolService/Services/Service.cs rename src/NetCoreToolService/{NetCoreToolService.csproj => Steeltoe.NetCoreToolService.csproj} (61%) create mode 100644 test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs create mode 100644 test/Common.Utils.Test/IO/Steeltoe.Common.IO.Test.csproj create mode 100644 test/Common.Utils.Test/IO/TempDirectoryTest.cs create mode 100644 test/Common.Utils.Test/IO/TempFileTest.cs create mode 100644 test/Common.Utils.Test/Steeltoe.Common.Utils.Test.csproj delete mode 100644 test/NetCoreToolService.Test/Archivers/ArchiverRegistryTests.cs create mode 100644 test/NetCoreToolService.Test/Controllers/NewControllerTest.cs rename test/NetCoreToolService.Test/{Archivers/ZipArchiverTests.cs => Packagers/ZipPackagerTests.cs} (83%) rename test/NetCoreToolService.Test/{NetCoreToolService.Test.csproj => Steeltoe.NetCoreToolService.Test.csproj} (51%) diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..05aa18a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +indent_style = space +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 +charset = utf-8 + +[*.{csproj,props,targets,DotSettings}] +indent_size = 2 + +[*.cs] +indent_size = 4 + +[*.yaml] +indent_size = 2 + +[*.ps1] +indent_size = 4 + +[*.json] +indent_size = 2 diff --git a/NetCoreToolService.sln b/Steeltoe.NetCoreToolService.sln similarity index 55% rename from NetCoreToolService.sln rename to Steeltoe.NetCoreToolService.sln index c44c6ca..9140723 100644 --- a/NetCoreToolService.sln +++ b/Steeltoe.NetCoreToolService.sln @@ -5,14 +5,19 @@ VisualStudioVersion = 16.6.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C742A7B8-80CA-4365-85CA-C29AA744CE54}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreToolService", "src\NetCoreToolService\NetCoreToolService.csproj", "{1462EDFE-F1FC-48C2-80C1-917317EE3C97}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steeltoe.Common.Utils", "src\Common.Utils\Steeltoe.Common.Utils.csproj", "{3E82184B-FA14-4B24-9ED0-A823465B6AB9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.NetCoreToolService", "src\NetCoreToolService\Steeltoe.NetCoreToolService.csproj", "{1462EDFE-F1FC-48C2-80C1-917317EE3C97}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{410C0E72-737F-4168-AECA-2F6D19EE86D5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreToolService.Test", "test\NetCoreToolService.Test\NetCoreToolService.Test.csproj", "{6BD6C793-E555-475F-A2E1-12D7F3F56A3B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steeltoe.Common.Utils.Test", "test\Common.Utils.Test\Steeltoe.Common.Utils.Test.csproj", "{06247239-19D8-4FB5-99F1-F399346ADE4F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.NetCoreToolService.Test", "test\NetCoreToolService.Test\Steeltoe.NetCoreToolService.Test.csproj", "{6BD6C793-E555-475F-A2E1-12D7F3F56A3B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C37528B8-BDD1-440F-B69A-AD57F939620C}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig .gitconfig = .gitconfig .gitignore = .gitignore Directory.Build.props = Directory.Build.props @@ -57,6 +62,30 @@ Global {6BD6C793-E555-475F-A2E1-12D7F3F56A3B}.Release|x64.Build.0 = Release|Any CPU {6BD6C793-E555-475F-A2E1-12D7F3F56A3B}.Release|x86.ActiveCfg = Release|Any CPU {6BD6C793-E555-475F-A2E1-12D7F3F56A3B}.Release|x86.Build.0 = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|x64.Build.0 = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Debug|x86.Build.0 = Debug|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|Any CPU.Build.0 = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|x64.ActiveCfg = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|x64.Build.0 = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|x86.ActiveCfg = Release|Any CPU + {3E82184B-FA14-4B24-9ED0-A823465B6AB9}.Release|x86.Build.0 = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|x64.ActiveCfg = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|x64.Build.0 = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|x86.ActiveCfg = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Debug|x86.Build.0 = Debug|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|Any CPU.Build.0 = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|x64.ActiveCfg = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|x64.Build.0 = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|x86.ActiveCfg = Release|Any CPU + {06247239-19D8-4FB5-99F1-F399346ADE4F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -64,6 +93,8 @@ Global GlobalSection(NestedProjects) = preSolution {1462EDFE-F1FC-48C2-80C1-917317EE3C97} = {C742A7B8-80CA-4365-85CA-C29AA744CE54} {6BD6C793-E555-475F-A2E1-12D7F3F56A3B} = {410C0E72-737F-4168-AECA-2F6D19EE86D5} + {3E82184B-FA14-4B24-9ED0-A823465B6AB9} = {C742A7B8-80CA-4365-85CA-C29AA744CE54} + {06247239-19D8-4FB5-99F1-F399346ADE4F} = {410C0E72-737F-4168-AECA-2F6D19EE86D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D8EFB01A-92BF-418B-B2B2-A12045B772E2} diff --git a/NetCoreToolService.sln.DotSettings b/Steeltoe.NetCoreToolService.sln.DotSettings similarity index 100% rename from NetCoreToolService.sln.DotSettings rename to Steeltoe.NetCoreToolService.sln.DotSettings diff --git a/Version.props b/Version.props index 4dfd976..ef073c9 100644 --- a/Version.props +++ b/Version.props @@ -2,7 +2,22 @@ 0.0.1 - 5.6.* + + + + 5.0.* + 5.6.* + + + + 1.3.* + 2.9.* + 5.10.* + 5.5.* + 3.1.* + 16.7.* + 4.14.* + 2.4.* diff --git a/src/Common.Utils/Diagnostics/CommandException.cs b/src/Common.Utils/Diagnostics/CommandException.cs new file mode 100644 index 0000000..9fc0049 --- /dev/null +++ b/src/Common.Utils/Diagnostics/CommandException.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Steeltoe.Common.Utils.Diagnostics +{ + /// + /// The exception that is thrown when a system error occurs running a command. + /// + public class CommandException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// Foo. + /// Foo bar. + public CommandException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/Common.Utils/Diagnostics/CommandExecutor.cs b/src/Common.Utils/Diagnostics/CommandExecutor.cs new file mode 100644 index 0000000..250c6b8 --- /dev/null +++ b/src/Common.Utils/Diagnostics/CommandExecutor.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; + +namespace Steeltoe.Common.Utils.Diagnostics +{ + /// + public class CommandExecutor : ICommandExecutor + { + private static int _commandCounter; + + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Injected logger. + public CommandExecutor(ILogger logger = null) + { + _logger = logger; + } + + /// + public async Task ExecuteAsync(string command, string workingDirectory = null, int timeout = -1) + { + var commandId = ++_commandCounter; + using var process = new Process(); + var arguments = command.Split(new[] { ' ' }, 2); + process.StartInfo.FileName = arguments[0]; + if (arguments.Length > 1) + { + process.StartInfo.Arguments = arguments[1]; + } + + if (workingDirectory != null) + { + process.StartInfo.WorkingDirectory = workingDirectory; + } + + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + + var output = new StringBuilder(); + var outputCloseEvent = new TaskCompletionSource(); + process.OutputDataReceived += (_, e) => + { + if (e.Data is null) + { + outputCloseEvent.SetResult(true); + } + else + { + output.AppendLine(e.Data); + } + }; + + var error = new StringBuilder(); + var errorCloseEvent = new TaskCompletionSource(); + process.ErrorDataReceived += (_, e) => + { + if (e.Data is null) + { + errorCloseEvent.SetResult(true); + } + else + { + error.AppendLine(e.Data); + } + }; + + _logger?.LogDebug("[{CommandId}] command: {Command}", commandId, command); + try + { + if (!process.Start()) + { + _logger?.LogDebug("[{CommandId}] failed to start: {Error}", commandId, "no details available"); + throw new Exception($"'{command}' failed to start; no details available"); + } + } + catch (Exception ex) + { + _logger?.LogDebug("[{CommandId}] failed to start: {Error}", commandId, ex.Message); + throw new CommandException($"'{command}' failed to start: {ex.Message}", ex); + } + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + // ReSharper disable once AccessToDisposedClosure + var waitForExit = Task.Run(() => process.WaitForExit(timeout)); + var processTask = Task.WhenAll(waitForExit, outputCloseEvent.Task, errorCloseEvent.Task); + if (await Task.WhenAny(Task.Delay(timeout), processTask) == processTask && waitForExit.Result) + { + var result = new CommandResult + { + ExitCode = process.ExitCode, Output = output.ToString(), Error = error.ToString(), + }; + _logger?.LogDebug("[{CommandId}] exit code: {ExitCode}", commandId, result.ExitCode); + if (result.Output.Length > 0) + { + _logger?.LogDebug("[{CommandId}] stdout:\n{Output}", commandId, result.Output); + } + + if (result.Error.Length > 0) + { + _logger?.LogDebug("[{CommandId}] stderr:\n{Error}", commandId, result.Error); + } + + return result; + } + + try + { + process.Kill(); + } + catch + { + // ignore + } + + _logger?.LogDebug("[{CommandId}] timed out: {TimeOut}ms", commandId, timeout); + throw new Exception($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out"); + } + } +} diff --git a/src/Common.Utils/Diagnostics/CommandResult.cs b/src/Common.Utils/Diagnostics/CommandResult.cs new file mode 100644 index 0000000..f401b69 --- /dev/null +++ b/src/Common.Utils/Diagnostics/CommandResult.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +namespace Steeltoe.Common.Utils.Diagnostics +{ + /// + /// An simple abstraction of a command result. + /// + public struct CommandResult + { + /// + /// Gets the command exit code. + /// + public int ExitCode { get; init; } + + /// + /// Gets the command exit STDOUT. + /// + public string Output { get; init; } + + /// + /// Gets the command exit STDERR. + /// + public string Error { get; init; } + } +} diff --git a/src/Common.Utils/Diagnostics/ICommandExecutor.cs b/src/Common.Utils/Diagnostics/ICommandExecutor.cs new file mode 100644 index 0000000..7dfe11d --- /dev/null +++ b/src/Common.Utils/Diagnostics/ICommandExecutor.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; + +namespace Steeltoe.Common.Utils.Diagnostics +{ + /// + /// A utility abstraction to simplify the running of commands. + /// + public interface ICommandExecutor + { + /// + /// Execute the command and return the result. + /// + /// Command to be executed. + /// The directory that contains the command process. + /// The amount of time in milliseconds to wait for command to complete. + /// Command result. + /// If a process can not be started for command. + Task ExecuteAsync(string command, string workingDirectory = null, int timeout = -1); + } +} diff --git a/src/Common.Utils/IO/Steeltoe.Common.IO.csproj b/src/Common.Utils/IO/Steeltoe.Common.IO.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/Common.Utils/IO/Steeltoe.Common.IO.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/NetCoreToolService/Utils/IO/TempDirectory.cs b/src/Common.Utils/IO/TempDirectory.cs similarity index 58% rename from src/NetCoreToolService/Utils/IO/TempDirectory.cs rename to src/Common.Utils/IO/TempDirectory.cs index ad4c661..e66641c 100644 --- a/src/NetCoreToolService/Utils/IO/TempDirectory.cs +++ b/src/Common.Utils/IO/TempDirectory.cs @@ -4,45 +4,42 @@ using System.IO; -namespace Steeltoe.NetCoreToolService.Utils.IO +namespace Steeltoe.Common.Utils.IO { /// - /// A temp directory. + /// A temporary directory. /// - public sealed class TempDirectory : TempPath + public class TempDirectory : TempPath { /// /// Initializes a new instance of the class. /// - public TempDirectory() - : this(true) + /// Temporary directory prefix. + public TempDirectory(string prefix = null) + : base(prefix) { } /// - /// Initializes a new instance of the class. + /// Creates the temporary directory. /// - /// If true, create the directory. - public TempDirectory(bool create) + protected override void InitializePath() { - if (create) - { - Directory.CreateDirectory(FullName); - } + Directory.CreateDirectory(FullPath); } /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (!Directory.Exists(FullName)) + if (!Directory.Exists(FullPath)) { return; } try { - Directory.Delete(FullName, true); + Directory.Delete(FullPath, true); } catch { diff --git a/src/NetCoreToolService/Utils/IO/TempFile.cs b/src/Common.Utils/IO/TempFile.cs similarity index 60% rename from src/NetCoreToolService/Utils/IO/TempFile.cs rename to src/Common.Utils/IO/TempFile.cs index 288b1b4..5fa4f4f 100644 --- a/src/NetCoreToolService/Utils/IO/TempFile.cs +++ b/src/Common.Utils/IO/TempFile.cs @@ -4,31 +4,28 @@ using System.IO; -namespace Steeltoe.NetCoreToolService.Utils.IO +namespace Steeltoe.Common.Utils.IO { /// - /// A temp file. + /// A temporary directory. /// - public sealed class TempFile : TempPath + public class TempFile : TempPath { /// /// Initializes a new instance of the class. /// - public TempFile() - : this(true) + /// Temporary file prefix. + public TempFile(string prefix = null) + : base(prefix) { } /// - /// Initializes a new instance of the class. + /// Creates the temporary file. /// - /// If true, create file. - public TempFile(bool create) + protected override void InitializePath() { - if (create) - { - File.Create(FullName).Dispose(); - } + File.Create(FullPath).Dispose(); } /// @@ -36,14 +33,14 @@ namespace Steeltoe.NetCoreToolService.Utils.IO { base.Dispose(disposing); - if (!File.Exists(FullName)) + if (!File.Exists(FullPath)) { return; } try { - File.Delete(FullName); + File.Delete(FullPath); } catch { diff --git a/src/NetCoreToolService/Utils/IO/TempPath.cs b/src/Common.Utils/IO/TempPath.cs similarity index 57% rename from src/NetCoreToolService/Utils/IO/TempPath.cs rename to src/Common.Utils/IO/TempPath.cs index ec6f6b2..88b451a 100644 --- a/src/NetCoreToolService/Utils/IO/TempPath.cs +++ b/src/Common.Utils/IO/TempPath.cs @@ -4,22 +4,23 @@ using System; using System.IO; -using System.Reflection; -namespace Steeltoe.NetCoreToolService.Utils.IO +namespace Steeltoe.Common.Utils.IO { /// - /// An abstraction of temp files. + /// An abstraction of a temporary path, such as a file. /// public abstract class TempPath : IDisposable { /// /// Initializes a new instance of the class. /// - protected TempPath() + /// Temporary path prefix. + protected TempPath(string prefix = null) { - Name = Guid.NewGuid().ToString(); - FullName = Path.Join(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name, Name); + Name = $"{prefix ?? string.Empty}{Guid.NewGuid()}"; + FullPath = $"{Path.GetTempPath()}{Path.DirectorySeparatorChar}{Name}"; + Initialize(); } /// @@ -31,17 +32,17 @@ namespace Steeltoe.NetCoreToolService.Utils.IO } /// - /// Gets the full name of this path. + /// Gets the absolute path of the TempPath. /// - public string FullName { get; } + public string FullPath { get; } /// - /// Gets the name of this path. + /// Gets the name of the TempPath. /// public string Name { get; } /// - /// Ensures the underlying temporary path is deleted. + /// Ensures the temporary path is deleted. /// public void Dispose() { @@ -49,7 +50,7 @@ namespace Steeltoe.NetCoreToolService.Utils.IO } /// - /// Ensures the underlying temporary path is deleted. + /// Ensures the temporary path is deleted. /// /// If disposing. protected virtual void Dispose(bool disposing) @@ -59,5 +60,17 @@ namespace Steeltoe.NetCoreToolService.Utils.IO GC.SuppressFinalize(this); } } + + /// + /// Subclasses should override and perform any path initialization here. + /// + protected virtual void InitializePath() + { + } + + private void Initialize() + { + InitializePath(); + } } } diff --git a/src/Common.Utils/Steeltoe.Common.Utils.csproj b/src/Common.Utils/Steeltoe.Common.Utils.csproj new file mode 100644 index 0000000..6b3408f --- /dev/null +++ b/src/Common.Utils/Steeltoe.Common.Utils.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/NetCoreToolService/Archivers/ArchiverRegistry.cs b/src/NetCoreToolService/Archivers/ArchiverRegistry.cs deleted file mode 100644 index 2afe82e..0000000 --- a/src/NetCoreToolService/Archivers/ArchiverRegistry.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.NetCoreToolService.Services; -using System.Collections.Generic; - -namespace Steeltoe.NetCoreToolService.Archivers -{ - /// - /// An in-memory implementation. - /// - public class ArchiverRegistry : Service, IArchiverRegistry - { - /* ----------------------------------------------------------------- * - * fields * - * ----------------------------------------------------------------- */ - - private readonly Dictionary _archivers = new Dictionary(); - - /* ----------------------------------------------------------------- * - * constructors * - * ----------------------------------------------------------------- */ - - /// - /// Initializes a new instance of the class. - /// - /// Injected logger. - public ArchiverRegistry(ILogger logger) - : base(logger) - { - Logger.LogInformation("Initializing archiver registry"); - Register(new ZipArchiver()); - } - - /* ----------------------------------------------------------------- * - * methods * - * ----------------------------------------------------------------- */ - - /// - public void Register(IArchiver archiver) - { - Logger.LogInformation( - "Registering archiver: {Archiver} -> {ArchiverType}", - archiver.Name, - archiver.GetType()); - _archivers.Add(archiver.Name, archiver); - } - - /// - public IArchiver Lookup(string packaging) - { - _archivers.TryGetValue(packaging, out var archiver); - return archiver; - } - } -} diff --git a/src/NetCoreToolService/Controllers/NewController.cs b/src/NetCoreToolService/Controllers/NewController.cs index f8b29b9..2f0cdc8 100644 --- a/src/NetCoreToolService/Controllers/NewController.cs +++ b/src/NetCoreToolService/Controllers/NewController.cs @@ -5,13 +5,15 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Steeltoe.NetCoreToolService.Services; -using Steeltoe.NetCoreToolService.Utils.IO; +using Steeltoe.Common.Utils.Diagnostics; +using Steeltoe.Common.Utils.IO; +using Steeltoe.NetCoreToolService.Models; +using Steeltoe.NetCoreToolService.Packagers; using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace Steeltoe.NetCoreToolService.Controllers @@ -20,21 +22,30 @@ namespace Steeltoe.NetCoreToolService.Controllers /// The controller for "dotnet new". /// [ApiController] - [Route("api/[controller]")] + [Route("api/new")] public class NewController : ControllerBase { - private readonly IArchiverRegistry _archiverRegistry; + private const string DefaultOutput = "Sample"; + + private const string DefaultPackaging = "zip"; + + private readonly ICommandExecutor _commandExecutor; private readonly ILogger _logger; + private readonly Dictionary _packagers = new () + { + { "zip", new ZipPackager() }, + }; + /// /// Initializes a new instance of the class. /// - /// Injected registry of available archivers. + /// Injected command. /// Injected logger. - public NewController(IArchiverRegistry archiverRegistry, ILogger logger) + public NewController(ICommandExecutor commandExecutor, ILogger logger = null) { - _archiverRegistry = archiverRegistry; + _commandExecutor = commandExecutor; _logger = logger; } @@ -45,76 +56,7 @@ namespace Steeltoe.NetCoreToolService.Controllers [HttpGet] public async Task GetTemplates() { - var dict = await GetTemplateDictionary(); - return Ok(dict); - } - - /// - /// Gets a generated project for the specified Net Core Tool template. - /// - /// Template name. - /// Template options. - /// Project archive. - [HttpGet] - [Route("{template}")] - public async Task GetProjectArchiveForTemplate(string template, string options) - { - var opts = options?.Split(',').Select(opt => opt.Trim()).ToList() ?? new List(); - var pArgs = new List() { "new", template }; - var name = opts.Find(opt => opt.StartsWith("output="))?.Split('=', 2)[1]; - if (name is null) - { - name = "Sample"; - pArgs.AddRange(new[] { "--output", name }); - } - - pArgs.AddRange(opts.Select(opt => $"--{opt}")); - - using var workDir = new TempDirectory(); - var pInfo = new ProcessStartInfo - { - Arguments = string.Join(' ', pArgs), - WorkingDirectory = workDir.FullName, - }; - - var result = await ProcessToResultAsync(pInfo); - var ok = result as ContentResult; - if (ok is null) - { - return result; - } - - if (!Directory.EnumerateFileSystemEntries(workDir.FullName).Any()) - { - return NotFound($"template {template} does not exist"); - } - - var archivalType = "zip"; - var archiver = _archiverRegistry.Lookup(archivalType); - if (archiver is null) - { - return NotFound($"Packaging '{archivalType}' not found."); - } - - var archiveBytes = archiver.ToBytes(workDir.FullName); - return File(archiveBytes, archiver.MimeType, $"{name}{archiver.FileExtension}"); - } - - /// - /// Returns "help" for the specified Net Core Tool template. - /// - /// Template name. - /// Template help. - [HttpGet] - [Route("{template}/help")] - public async Task GetTemplateHelp(string template) - { - var pInfo = new ProcessStartInfo - { - ArgumentList = { "new", template, "--help" }, - }; - - return await ProcessToResultAsync(pInfo); + return Ok(await GetTemplateDictionary()); } /// @@ -122,138 +64,210 @@ namespace Steeltoe.NetCoreToolService.Controllers /// /// Template NuGet ID. /// Information about the installed templates. - [HttpPost] - public async Task InstallTemplate(string nuGetId) + [HttpPut("nuget/{nuGetId}")] + public async Task InstallTemplates(string nuGetId) { - if (nuGetId is null) + await _commandExecutor.ExecuteAsync($"{NetCoreTool.Command} new --uninstall {nuGetId}"); + var oldTemplates = await GetTemplateDictionary(); + var installCommand = await _commandExecutor.ExecuteAsync($"{NetCoreTool.Command} new --install {nuGetId}"); + const string notFoundError = "error NU1101: "; + if (installCommand.Output.Contains(notFoundError)) { - return BadRequest("missing NuGet ID"); + var start = installCommand.Output.IndexOf(notFoundError, StringComparison.Ordinal) + + notFoundError.Length; + var end = installCommand.Output.IndexOf('\n', start); + return BadRequest(installCommand.Output[start..end].Trim()); } - var preInstallTemplates = await GetTemplateDictionary(); - - var pInfo = new ProcessStartInfo + var newTemplates = await GetTemplateDictionary(); + foreach (var oldTemplate in oldTemplates.Keys) { - ArgumentList = { "new", "--install", nuGetId }, - }; - await ProcessToStringAsync(pInfo); - - var postInstallTemplates = await GetTemplateDictionary(); - - foreach (var template in preInstallTemplates.Keys) - { - postInstallTemplates.Remove(template); + newTemplates.Remove(oldTemplate); } - return Ok(postInstallTemplates); + return CreatedAtAction(nameof(InstallTemplates), newTemplates); } - private async Task> GetTemplateDictionary() + /// + /// Uninstalls the Net Core Tool templates for the specified NuGet ID. + /// + /// Template NuGet ID. + /// Information about the installed templates. + [HttpDelete("nuget/{nuGetId}")] + public async Task UninstallTemplates(string nuGetId) { - var pInfo = new ProcessStartInfo + var oldTemplates = await GetTemplateDictionary(); + var uninstallCommand = + await _commandExecutor.ExecuteAsync($"{NetCoreTool.Command} new --uninstall {nuGetId}"); + if (uninstallCommand.Output.Contains($"Could not find something to uninstall")) { - ArgumentList = { "new", "--list" }, - }; - var listing = await ProcessToStringAsync(pInfo); - var lines = listing.Split('\n').ToList().FindAll(line => !string.IsNullOrWhiteSpace(line)); + return NotFound($"No templates with NuGet ID '{nuGetId}' installed."); + } + + var newTemplates = await GetTemplateDictionary(); + foreach (var newTemplate in newTemplates.Keys) + { + oldTemplates.Remove(newTemplate); + } + + return Ok(oldTemplates); + } + + /// + /// Returns "help" for the specified Net Core Tool template. + /// + /// Template name. + /// Template help. + [HttpGet("{template}/help")] + public async Task GetTemplateHelp(string template) + { + var helpCommand = await _commandExecutor.ExecuteAsync($"{NetCoreTool.Command} new {template} --help"); + if (helpCommand.ExitCode != 0) + { + var start = helpCommand.Error.IndexOf("No templates found", StringComparison.Ordinal); + var end = helpCommand.Error.IndexOf('\n', start); + return NotFound(helpCommand.Error[start..end].Trim()); + } + + return Ok(helpCommand.Output.Trim()); + } + + /// + /// Gets a generated project for the specified Net Core Tool template. + /// + /// Template name. + /// Template options. + /// Project packaging, e.g. zip. + /// Project archive. + [HttpGet] + [Route("{template}")] + public async Task GetTemplateProject( + string template, + string options = null, + string packaging = DefaultPackaging) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + try + { + var output = DefaultOutput; + var optionList = new List(); + if (options is not null) + { + foreach (var option in options.Split(',')) + { + if (option.Contains('=')) + { + var nvp = option.Split('=', 2); + if (nvp[0].Equals("output")) + { + output = nvp[1]; + continue; + } + } + + optionList.Add($"--{option}"); + } + } + + if (!_packagers.TryGetValue(packaging, out var packager)) + { + return BadRequest($"Unknown or unsupported packaging '{packaging}'."); + } + + using var projectDir = new TempDirectory("NetCoreToolService-"); + var commandLine = new StringBuilder(); + commandLine.Append(NetCoreTool.Command).Append(" new ").Append(template); + commandLine.Append(" --output=").Append(output); + foreach (var option in optionList) + { + commandLine.Append(' ').Append(option); + } + + var newCommand = + await _commandExecutor.ExecuteAsync(commandLine.ToString(), projectDir.FullPath); + + const string unknownTemplateError = "No templates found"; + if (newCommand.Error.Contains(unknownTemplateError)) + { + return NotFound($"Template '{template}' not found."); + } + + const string invalidSwitchError = "Invalid input switch:"; + if (newCommand.Error.Contains(invalidSwitchError)) + { + var start = newCommand.Error.IndexOf(invalidSwitchError, StringComparison.Ordinal) + + invalidSwitchError.Length; + start = newCommand.Error.IndexOf("--", start, StringComparison.Ordinal) + "--".Length; + var end = newCommand.Error.IndexOf('\n', start); + return NotFound($"Switch '{newCommand.Error[start..end]}' not found."); + } + + const string invalidParameterError = "Error: Invalid parameter(s):"; + if (newCommand.Error.Contains(invalidParameterError)) + { + var start = newCommand.Error.IndexOf(invalidParameterError, StringComparison.Ordinal) + + invalidParameterError.Length; + start = newCommand.Error.IndexOf("--", start, StringComparison.Ordinal) + "--".Length; + var end = newCommand.Error.IndexOf('\n', start); + var nvp = newCommand.Error[start..end].Split(' ', 2); + return NotFound($"Option '{nvp[0]}' parameter '{nvp[1]}' not found."); + } + + if (newCommand.ExitCode != 0) + { + return StatusCode(StatusCodes.Status500InternalServerError, newCommand.Error.Trim()); + } + + if (!newCommand.Output.Contains(" was created successfully.")) + { + return StatusCode(StatusCodes.Status500InternalServerError, newCommand.Output.Trim()); + } + + var package = packager.ToBytes(projectDir.FullPath); + return File(package, packager.MimeType, $"{output}{packager.FileExtension}"); + } + finally + { + stopwatch.Stop(); + _logger?.LogDebug("Generated project in {Elapsed:m\\:s\\.fff}", stopwatch.Elapsed); + } + } + + private async Task GetTemplateDictionary() + { + var listCommand = await _commandExecutor.ExecuteAsync($"{NetCoreTool.Command} new --list"); + + var lines = listCommand.Output.Split('\n').ToList() + .FindAll(line => !string.IsNullOrWhiteSpace(line)); + var headingIdx = lines.FindIndex(line => line.StartsWith("-")); var headings = lines[headingIdx].Split(" "); - var nameColLength = headings[0].Length; - var shortNameColStart = nameColLength + 2; - var shortNameColLength = headings[1].Length; - var languageColStart = shortNameColStart + shortNameColLength + 2; - var languageColLength = headings[2].Length; - var tagsColStart = languageColStart + languageColLength + 2; - var tagsColLength = headings[3].Length; + const int nameColStart = 0; + var nameColEnd = nameColStart + headings[0].Length; + var shortNameColStart = nameColEnd + 2; + var shortNameColEnd = shortNameColStart + headings[1].Length; + var languageColStart = shortNameColEnd + 2; + var languageColEnd = languageColStart + headings[2].Length; + var tagsColStart = languageColEnd + 2; + var tagsColEnd = tagsColStart + headings[3].Length; lines = lines.GetRange(headingIdx + 1, lines.Count - headingIdx - 1); - var dict = new Dictionary(); + var dict = new TemplateDictionary(); foreach (var line in lines) { - var templateInfo = new TemplateInfo(); - var template = line.Substring(shortNameColStart, shortNameColLength).Trim(); - templateInfo.Name = line.Substring(0, nameColLength).Trim(); - templateInfo.Languages = line.Substring(languageColStart, languageColLength).Trim(); - templateInfo.Tags = line.Substring(tagsColStart, tagsColLength).Trim(); + var template = line[shortNameColStart..shortNameColEnd].Trim(); + var templateInfo = new TemplateInfo + { + Name = line[nameColStart..nameColEnd].Trim(), + Languages = line[languageColStart..languageColEnd].Trim(), + Tags = line[tagsColStart.. Math.Min(tagsColEnd, line.Length)].Trim(), + }; dict.Add(template, templateInfo); } return dict; } - - private async Task ProcessToStringAsync(ProcessStartInfo processStartInfo) - { - processStartInfo.FileName = NetCoreTool.Command; - TempDirectory workDir = null; - if (string.IsNullOrEmpty(processStartInfo.WorkingDirectory)) - { - workDir = new TempDirectory(); - processStartInfo.WorkingDirectory = workDir.FullName; - } - - processStartInfo.UseShellExecute = false; - processStartInfo.RedirectStandardOutput = true; - processStartInfo.RedirectStandardError = true; - var guid = Path.GetFileName(processStartInfo.WorkingDirectory) ?? "unknown"; - _logger.LogInformation( - "{Guid}: {Command} {Args}", - guid, - processStartInfo.FileName, - processStartInfo.Arguments); - var proc = Process.Start(processStartInfo); - if (proc is null) - { - throw new ActionResultException(StatusCode(StatusCodes.Status503ServiceUnavailable)); - } - - await proc.WaitForExitAsync(); - workDir?.Dispose(); - if (proc.ExitCode == 0) - { - var output = await proc.StandardOutput.ReadToEndAsync(); - _logger.LogInformation("{Guid}>\n{Output}", guid, output); - return output; - } - - var error = await proc.StandardError.ReadToEndAsync(); - _logger.LogInformation("{Guid}: {Error}", guid, error); - throw new ActionResultException(NotFound(error)); - } - - private async Task ProcessToResultAsync(ProcessStartInfo processStartInfo) - { - try - { - return Content(await ProcessToStringAsync(processStartInfo)); - } - catch (ActionResultException e) - { - return e.ActionResult; - } - } - } - - internal class TemplateInfo - { - public string Name { get; set; } - - public string Languages { get; set; } - - public string Tags { get; set; } - - public override string ToString() - { - return $"[name={Name},languages={Languages},tags={Tags}"; - } - } - - internal class ActionResultException : Exception - { - internal ActionResultException(ActionResult actionResult) - { - ActionResult = actionResult; - } - - internal ActionResult ActionResult { get; } } } diff --git a/src/NetCoreToolService/Models/TemplateDictionary.cs b/src/NetCoreToolService/Models/TemplateDictionary.cs new file mode 100644 index 0000000..9adafbf --- /dev/null +++ b/src/NetCoreToolService/Models/TemplateDictionary.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Steeltoe.NetCoreToolService.Models +{ + /// + /// A convenience dictionary for template information. + /// + public class TemplateDictionary : Dictionary + { + } +} diff --git a/src/NetCoreToolService/Models/TemplateInfo.cs b/src/NetCoreToolService/Models/TemplateInfo.cs new file mode 100644 index 0000000..52e5a86 --- /dev/null +++ b/src/NetCoreToolService/Models/TemplateInfo.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +namespace Steeltoe.NetCoreToolService.Models +{ + /// + /// Contains information about a Net Core Tool template. + /// + public class TemplateInfo + { + /// + /// Gets the name of the template. + /// + public string Name { get; internal init; } + + /// + /// Gets the supported languages of the template. + /// + public string Languages { get; internal init; } + + /// + /// Gets the template tags. + /// + public string Tags { get; internal init; } + + /// + public override string ToString() + { + return $"[name={Name},languages={Languages},tags={Tags}"; + } + } +} diff --git a/src/NetCoreToolService/Services/IArchiver.cs b/src/NetCoreToolService/Packagers/IPackager.cs similarity index 51% rename from src/NetCoreToolService/Services/IArchiver.cs rename to src/NetCoreToolService/Packagers/IPackager.cs index 6bdd044..927330e 100644 --- a/src/NetCoreToolService/Services/IArchiver.cs +++ b/src/NetCoreToolService/Packagers/IPackager.cs @@ -2,33 +2,33 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -namespace Steeltoe.NetCoreToolService.Services +namespace Steeltoe.NetCoreToolService.Packagers { /// - /// Contract for archiver implementations. + /// Contract for packager implementations. /// - public interface IArchiver + public interface IPackager { /// - /// Gets the name for the archiver, e.g. "zip". + /// Gets the name for the packager, e.g. "zip". /// string Name { get; } /// - /// Gets the file extension for the archive, e.g. ".zip". + /// Gets the file extension for package, e.g. ".zip". /// string FileExtension { get; } /// - /// Gets the mime type for the archive, e.g. "application/zip". + /// Gets the mime type for a package, e.g. "application/zip". /// string MimeType { get; } /// - /// Returns an archive of the files rooted at path as a byte array. + /// Returns a package of the files rooted at path as a byte array. /// - /// Path to be archived. - /// A byte array containing the archive. + /// Path to be packaged. + /// A byte array containing the package. byte[] ToBytes(string path); } } diff --git a/src/NetCoreToolService/Archivers/ZipArchiver.cs b/src/NetCoreToolService/Packagers/ZipPackager.cs similarity index 92% rename from src/NetCoreToolService/Archivers/ZipArchiver.cs rename to src/NetCoreToolService/Packagers/ZipPackager.cs index 0592575..ca2ac6e 100644 --- a/src/NetCoreToolService/Archivers/ZipArchiver.cs +++ b/src/NetCoreToolService/Packagers/ZipPackager.cs @@ -2,17 +2,16 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -using Steeltoe.NetCoreToolService.Services; using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; -namespace Steeltoe.NetCoreToolService.Archivers +namespace Steeltoe.NetCoreToolService.Packagers { /// - /// An implementation using the ZIP archive file format. + /// An implementation using the ZIP archive file format. /// - public class ZipArchiver : IArchiver + public class ZipPackager : IPackager { /* ----------------------------------------------------------------- * * fields * @@ -35,10 +34,10 @@ namespace Steeltoe.NetCoreToolService.Archivers * ----------------------------------------------------------------- */ /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Compression level default . - public ZipArchiver(CompressionLevel compression = CompressionLevel.Fastest) + public ZipPackager(CompressionLevel compression = CompressionLevel.Fastest) { _compression = compression; } diff --git a/src/NetCoreToolService/Services/IArchiverRegistry.cs b/src/NetCoreToolService/Services/IArchiverRegistry.cs deleted file mode 100644 index 7f02858..0000000 --- a/src/NetCoreToolService/Services/IArchiverRegistry.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.NetCoreToolService.Services -{ - /// - /// Contract for archiver registry implementations. - /// - public interface IArchiverRegistry - { - /// - /// Registers the archiver. - /// - /// The archiver to register. - void Register(IArchiver value); - - /// - /// Look for an archiver with the specified name. - /// - /// The name of the archiver to loookup. - /// The named archiver,or null if no value found. - IArchiver Lookup(string name); - } -} diff --git a/src/NetCoreToolService/Services/Service.cs b/src/NetCoreToolService/Services/Service.cs deleted file mode 100644 index 530c93e..0000000 --- a/src/NetCoreToolService/Services/Service.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.NetCoreToolService.Services -{ - /// - /// Base class for services. - /// - public abstract class Service - { - /// - /// Initializes a new instance of the class. - /// - /// Injected logger. - protected Service(ILogger logger) - { - Logger = logger; - } - - /// - /// Gets the logger. - /// - protected ILogger Logger { get; } - } -} diff --git a/src/NetCoreToolService/Startup.cs b/src/NetCoreToolService/Startup.cs index 6994f18..0e44a05 100644 --- a/src/NetCoreToolService/Startup.cs +++ b/src/NetCoreToolService/Startup.cs @@ -8,8 +8,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; -using Steeltoe.NetCoreToolService.Archivers; -using Steeltoe.NetCoreToolService.Services; +using Steeltoe.Common.Utils.Diagnostics; namespace Steeltoe.NetCoreToolService { @@ -43,7 +42,7 @@ namespace Steeltoe.NetCoreToolService options.JsonSerializerOptions.IgnoreNullValues = true; options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; }); - services.AddSingleton(); + services.AddTransient(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Steeltoe.NetCoreToolService", Version = "v0" }); diff --git a/src/NetCoreToolService/NetCoreToolService.csproj b/src/NetCoreToolService/Steeltoe.NetCoreToolService.csproj similarity index 61% rename from src/NetCoreToolService/NetCoreToolService.csproj rename to src/NetCoreToolService/Steeltoe.NetCoreToolService.csproj index ac1308a..ec23614 100644 --- a/src/NetCoreToolService/NetCoreToolService.csproj +++ b/src/NetCoreToolService/Steeltoe.NetCoreToolService.csproj @@ -1,11 +1,11 @@ - - Steeltoe.NetCoreToolService - - + + + + diff --git a/src/NetCoreToolService/appsettings.Development.json b/src/NetCoreToolService/appsettings.Development.json index b73b124..1db1ae6 100644 --- a/src/NetCoreToolService/appsettings.Development.json +++ b/src/NetCoreToolService/appsettings.Development.json @@ -4,6 +4,7 @@ "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", + "Steeltoe.Common.Utils": "Debug", "Steeltoe.NetCoreToolService": "Debug" } } diff --git a/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs b/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs new file mode 100644 index 0000000..4559fcd --- /dev/null +++ b/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Steeltoe.Common.Utils.Diagnostics; +using Xunit; + +namespace Steeltoe.Common.Utils.Test.Diagnostics +{ + public class CommandExecutorTest + { + private readonly CommandExecutor _commandExecutor = new(); + + [Fact] + public async void SuccessfulCommandShouldReturn0() + { + var result = await _commandExecutor.ExecuteAsync("dotnet --help"); + Assert.Equal(0, result.ExitCode); + Assert.Contains("Usage: dotnet", result.Output); + } + + [Fact] + public async void UnsuccessfulCommandShouldNotReturn0() + { + var result = await _commandExecutor.ExecuteAsync("dotnet --no-such-option"); + Assert.NotEqual(0, result.ExitCode); + Assert.Contains("Unknown option: --no-such-option", result.Error); + } + + [Fact] + public async void UnknownCommandShouldThrowException() + { + Task Act() => _commandExecutor.ExecuteAsync("no-such-command"); + var exc = await Assert.ThrowsAsync(Act); + Assert.Contains("'no-such-command' failed to start", exc.Message); + Assert.NotNull(exc.InnerException); + } + } +} diff --git a/test/Common.Utils.Test/IO/Steeltoe.Common.IO.Test.csproj b/test/Common.Utils.Test/IO/Steeltoe.Common.IO.Test.csproj new file mode 100644 index 0000000..87e435c --- /dev/null +++ b/test/Common.Utils.Test/IO/Steeltoe.Common.IO.Test.csproj @@ -0,0 +1,18 @@ + + + netcoreapp3.1;net5.0; + + + + + + + + PreserveNewest + + + + + + + diff --git a/test/Common.Utils.Test/IO/TempDirectoryTest.cs b/test/Common.Utils.Test/IO/TempDirectoryTest.cs new file mode 100644 index 0000000..212de29 --- /dev/null +++ b/test/Common.Utils.Test/IO/TempDirectoryTest.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using Steeltoe.Common.Utils.IO; +using System.IO; +using Xunit; + +namespace Steeltoe.Common.Utils.Test.IO +{ + public class TempDirectoryTest + { + [Fact] + public void TempDirectoryRemovesItself() + { + var tempDir = new TempDirectory(); + Assert.True(Directory.Exists(tempDir.FullPath)); + File.Create(Path.Join(tempDir.FullPath, "foo")).Dispose(); + tempDir.Dispose(); + Assert.False(Directory.Exists(tempDir.FullPath)); + } + + [Fact] + public void TempDirectoryCanBePrefixed() + { + const string prefix = "XXX-"; + using var tempDir = new TempDirectory(prefix); + Assert.StartsWith(prefix, tempDir.Name); + } + } +} diff --git a/test/Common.Utils.Test/IO/TempFileTest.cs b/test/Common.Utils.Test/IO/TempFileTest.cs new file mode 100644 index 0000000..3ede714 --- /dev/null +++ b/test/Common.Utils.Test/IO/TempFileTest.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using Steeltoe.Common.Utils.IO; +using System.IO; +using Xunit; + +namespace Steeltoe.Common.Utils.Test.IO +{ + public class TempFileTest + { + [Fact] + public void TempFileRemovesItself() + { + var tempFile = new TempFile(); + Assert.True(File.Exists(tempFile.FullPath)); + tempFile.Dispose(); + Assert.False(File.Exists(tempFile.FullPath)); + } + + [Fact] + public void TempFileCanBePrefixed() + { + const string prefix = "XXX-"; + using var tempFile = new TempFile(prefix); + Assert.StartsWith(prefix, tempFile.Name); + } + } +} diff --git a/test/Common.Utils.Test/Steeltoe.Common.Utils.Test.csproj b/test/Common.Utils.Test/Steeltoe.Common.Utils.Test.csproj new file mode 100644 index 0000000..67ce9a7 --- /dev/null +++ b/test/Common.Utils.Test/Steeltoe.Common.Utils.Test.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 3ddcf0d..25a479b 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -3,15 +3,21 @@ - - - - - - - - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/test/NetCoreToolService.Test/Archivers/ArchiverRegistryTests.cs b/test/NetCoreToolService.Test/Archivers/ArchiverRegistryTests.cs deleted file mode 100644 index 95af8cf..0000000 --- a/test/NetCoreToolService.Test/Archivers/ArchiverRegistryTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using FluentAssertions; -using Microsoft.Extensions.Logging.Abstractions; -using Steeltoe.NetCoreToolService.Archivers; -using Xunit; - -namespace Steeltoe.NetCoreToolService.Test.Archivers -{ - public class ArchiverRegistryTests - { - /* ----------------------------------------------------------------- * - * positive tests * - * ----------------------------------------------------------------- */ - - [Fact] - public void Zip_Format_Should_Return_ZipArchiver() - { - // Arrange - var registry = new ArchiverRegistry(new NullLogger()); - - // Act - var archiver = registry.Lookup("zip"); - - // Assert - archiver.Should().NotBeNull(); - archiver.Should().BeOfType(); - } - - /* ----------------------------------------------------------------- * - * negative tests * - * ----------------------------------------------------------------- */ - - [Fact] - public void Unknown_Format_Should_Return_Null() - { - // Arrange - var registry = new ArchiverRegistry(new NullLogger()); - - // Act - var archiver = registry.Lookup("unknown"); - - // Assert - archiver.Should().BeNull(); - } - } -} diff --git a/test/NetCoreToolService.Test/Controllers/NewControllerTest.cs b/test/NetCoreToolService.Test/Controllers/NewControllerTest.cs new file mode 100644 index 0000000..2fc772d --- /dev/null +++ b/test/NetCoreToolService.Test/Controllers/NewControllerTest.cs @@ -0,0 +1,553 @@ +using System.IO; +using System.IO.Compression; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Steeltoe.Common.Utils.Diagnostics; +using Steeltoe.NetCoreToolService.Controllers; +using Steeltoe.NetCoreToolService.Models; +using Xunit; + +namespace Steeltoe.NetCoreToolService.Test.Controllers +{ + public class NewControllerTest + + { + /* ----------------------------------------------------------------- * + * positive tests * + * ----------------------------------------------------------------- */ + + [Fact] + public async Task GetTemplates_Should_Return_AllTemplates() + { + // Arrange + var executor = new Mock(); + executor.Setup(expression: c => c.ExecuteAsync($"{NetCoreTool.Command} new --list", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- --------- +My Template myt lang tags +My Other Template myot otherlang othertags +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplates(); + + // Assert + var okResult = Assert.IsType(result); + var templates = Assert.IsType(okResult.Value); + templates.Count.Should().Be(2); + templates.Keys.Should().Contain("myt"); + templates["myt"].Name.Should().Be("My Template"); + templates["myt"].Languages.Should().Be("lang"); + templates["myt"].Tags.Should().Be("tags"); + templates.Keys.Should().Contain("myot"); + templates["myot"].Name.Should().Be("My Other Template"); + templates["myot"].Languages.Should().Be("otherlang"); + templates["myot"].Tags.Should().Be("othertags"); + } + + [Fact] + public async Task InstallTemplates_Should_Return_InstalledTemplates() + { + // Arrange + var executor = new Mock(); + executor.SetupSequence(c => c.ExecuteAsync($"{NetCoreTool.Command} new --list", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- --------- +My Template myt lang tags +My Other Template myot otherlang othertags +", + } + ) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- ---------- +A New Template ant smalltalk newstuff +My Template myt lang tags +My Other Template myot otherlang othertags +Other New Template ont bigtalk otherstuff +", + } + ); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new --install My.Templates", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.InstallTemplates("My.Templates"); + + // Assert + var createdResult = Assert.IsType(result); + var templates = Assert.IsType(createdResult.Value); + templates.Count.Should().Be(2); + templates["ant"].Name.Should().Be("A New Template"); + templates["ant"].Languages.Should().Be("smalltalk"); + templates["ant"].Tags.Should().Be("newstuff"); + templates["ont"].Name.Should().Be("Other New Template"); + templates["ont"].Languages.Should().Be("bigtalk"); + templates["ont"].Tags.Should().Be("otherstuff"); + } + + [Fact] + public async Task UninstallTemplates_Should_Return_UninstalledTemplates() + { + // Arrange + var executor = new Mock(); + executor.SetupSequence(c => c.ExecuteAsync($"{NetCoreTool.Command} new --list", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- ---------- +A New Template ant smalltalk newstuff +My Template myt lang tags +My Other Template myot otherlang othertags +Other New Template ont bigtalk otherstuff +", + } + ) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- --------- +My Template myt lang tags +My Other Template myot otherlang othertags +", + } + ); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new --uninstall My.Templates", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.UninstallTemplates("My.Templates"); + + // Assert + var ok = Assert.IsType(result); + var templates = Assert.IsType(ok.Value); + templates.Count.Should().Be(2); + templates["ant"].Name.Should().Be("A New Template"); + templates["ant"].Languages.Should().Be("smalltalk"); + templates["ant"].Tags.Should().Be("newstuff"); + templates["ont"].Name.Should().Be("Other New Template"); + templates["ont"].Languages.Should().Be("bigtalk"); + templates["ont"].Tags.Should().Be("otherstuff"); + } + + [Fact] + public async Task GetTemplateHelp_Should_Return_Help() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --help", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +Some helpful tips for you +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateHelp("mytemplate"); + + // Assert + var ok = Assert.IsType(result); + var help = Assert.IsType(ok.Value); + help.Should().Be("Some helpful tips for you"); + } + + [Fact] + public async Task GetTemplateProject_Should_Return_ProjectPackage() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +The template ""mytemplate"" was created successfully. +", + Error = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate"); + + // Assert + Assert.IsType(result); + } + + [Fact] + public async Task GetTemplateProject_Should_Use_Defaults() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +The template ""mytemplate"" was created successfully. +", + Error = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate"); + + // Assert + var file = Assert.IsType(result); + file.ContentType.Should().Be("application/zip"); + file.FileDownloadName.Should().Be("Sample.zip"); + } + + [Fact] + public async Task GetTemplateProject_Can_Specify_Output() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Joe", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +The template ""mytemplate"" was created successfully. +", + Error = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate", "output=Joe"); + + // Assert + var file = Assert.IsType(result); + file.FileDownloadName.Should().StartWith("Joe."); + } + + [Fact] + public async Task GetTemplateProject_Can_Specify_ZipPackaging() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +The template ""mytemplate"" was created successfully. +", + Error = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate", packaging: "zip"); + + // Assert + var file = Assert.IsType(result); + var _ = new ZipArchive(new MemoryStream(file.FileContents)); + } + + /* ----------------------------------------------------------------- * + * negative tests * + * ----------------------------------------------------------------- */ + + [Fact] + public async Task InstallTemplates_UnknownNuGet_Should_Return_BadRequest() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new --list", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- --------- +My Template myt lang tags +My Other Template myot otherlang othertags +", + } + ); + executor.Setup(c => + c.ExecuteAsync($"{NetCoreTool.Command} new --install No.Such.Template", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 2, + Output = @" +... error NU1101: Unable to find package No.Such.Template. No packages exist with this id in source(s): myget.org, nuget.org +Failed to restore ... +", + }); + + var controller = new NewController(executor.Object); + + // Act + var result = await controller.InstallTemplates("No.Such.Template"); + + // Assert + var badRequest = Assert.IsType(result); + badRequest.Value.Should() + .Be( + "Unable to find package No.Such.Template. No packages exist with this id in source(s): myget.org, nuget.org"); + } + + [Fact] + public async Task UninstallTemplates_UnknownNuGet_Should_Return_NotFound() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new --list", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +------------------- -------- --------- --------- +", + } + ); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new --uninstall My.Templates", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +Could not find something to uninstall called 'My.Templates'. +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.UninstallTemplates("My.Templates"); + + // Assert + var notFound = Assert.IsType(result); + notFound.Value.Should().Be("No templates with NuGet ID 'My.Templates' installed."); + } + + [Fact] + public async Task GetTemplateHelp_UnknownTemplate_Should_Return_NotFound() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync($"{NetCoreTool.Command} new nosuchtemplate --help", null, -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 6, + Error = @" +No templates found matching: 'nosuchtemplate'. +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateHelp("nosuchtemplate"); + + // Assert + var notFound = Assert.IsType(result); + notFound.Value.Should().Be("No templates found matching: 'nosuchtemplate'."); + } + + [Fact] + public async Task GetTemplateProject_UnknownTemplate_Should_Return_NotFound() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => + c.ExecuteAsync($"{NetCoreTool.Command} new nosuchtemplate --output=Sample", It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 14, + Error = @" +No templates found matching: 'nosuchtemplate'. +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("nosuchtemplate"); + + // Assert + var notFound = Assert.IsType(result); + notFound.Value.Should().Be("Template 'nosuchtemplate' not found."); + } + + [Fact] + public async Task GetTemplateProject_UnknownSwitch_Should_Return_NotFound() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => + c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample --unknown-switch", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 5, + Error = @" +Invalid input switch: + --unknown-switch +For a list of valid options, run 'dotnet new webapi --help'. +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate", "unknown-switch"); + + // Assert + var notFound = Assert.IsType(result); + notFound.Value.Should().Be("Switch 'unknown-switch' not found."); + } + + [Fact] + public async Task GetTemplateProject_UnknownParameter_Should_Return_NotFound() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => + c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample --myoption=unknown", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = "", + Error = @" +Error: Invalid parameter(s): +--myoption unknown + 'unknown' is not a valid value for --myoption +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate", "myoption=unknown"); + + // Assert + var notFound = Assert.IsType(result); + notFound.Value.Should().Be("Option 'myoption' parameter 'unknown' not found."); + } + + [Fact] + public async Task GetTemplateProject_UnknownPackaging_Should_Return_BadRequest() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => + c.ExecuteAsync($"{NetCoreTool.Command} new mytemplate --output=Sample --myoption=unknown", + It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = "", + Error = @" +Error: Invalid parameter(s): +--myoption unknown + 'unknown' is not a valid value for --myoption +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate", packaging: "acme-packaging"); + + // Assert + var badRequest = Assert.IsType(result); + badRequest.Value.Should().Be("Unknown or unsupported packaging 'acme-packaging'."); + } + + [Fact] + public async Task GetTemplateProject_UnknownError_Should_Return_InternalServerError() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 1, + Output = "", + Error = @" +Something bad happened. +", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate"); + + // Assert + var internalServerError = Assert.IsType(result); + internalServerError.StatusCode.Should().Be(StatusCodes.Status500InternalServerError); + internalServerError.Value.Should().Be("Something bad happened."); + } + + [Fact] + public async Task GetTemplateProject_UnknownOutput_Should_Return_InternalServerError() + { + // Arrange + var executor = new Mock(); + executor.Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny(), -1)) + .ReturnsAsync(new CommandResult + { + ExitCode = 0, + Output = @" +Unexpected output. +", + Error = "", + } + ); + var controller = new NewController(executor.Object); + + // Act + var result = await controller.GetTemplateProject("mytemplate"); + + // Assert + var internalServerError = Assert.IsType(result); + internalServerError.StatusCode.Should().Be(StatusCodes.Status500InternalServerError); + internalServerError.Value.Should().Be("Unexpected output."); + } + } +} diff --git a/test/NetCoreToolService.Test/Archivers/ZipArchiverTests.cs b/test/NetCoreToolService.Test/Packagers/ZipPackagerTests.cs similarity index 83% rename from test/NetCoreToolService.Test/Archivers/ZipArchiverTests.cs rename to test/NetCoreToolService.Test/Packagers/ZipPackagerTests.cs index 76c79fa..1587fd9 100644 --- a/test/NetCoreToolService.Test/Archivers/ZipArchiverTests.cs +++ b/test/NetCoreToolService.Test/Packagers/ZipPackagerTests.cs @@ -5,13 +5,13 @@ using System.IO; using System.IO.Compression; using FluentAssertions; -using Steeltoe.NetCoreToolService.Archivers; -using Steeltoe.NetCoreToolService.Utils.IO; +using Steeltoe.Common.Utils.IO; +using Steeltoe.NetCoreToolService.Packagers; using Xunit; -namespace Steeltoe.NetCoreToolService.Test.Archivers +namespace Steeltoe.NetCoreToolService.Test.Packagers { - public class ZipArchiverTests + public class ZipPackagerTests { /* ----------------------------------------------------------------- * * positive tests * @@ -21,11 +21,11 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void ToStream_Should_Create_Zip_Archive() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); var tempDir = new TempDirectory(); // Act - var buf = archiver.ToBytes(tempDir.FullName); + var buf = archiver.ToBytes(tempDir.FullPath); // Assert new ZipArchive(new MemoryStream(buf)).Should().BeOfType(); @@ -35,15 +35,15 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void ToStream_Should_Archive_File_Contents() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); using var tempDir = new TempDirectory(); - var d1 = Path.Join(tempDir.FullName, "d1"); + var d1 = Path.Join(tempDir.FullPath, "d1"); Directory.CreateDirectory(d1); var f1 = Path.Join(d1, "f1"); File.WriteAllText(f1, "f1 stuff"); // Act - var buf = archiver.ToBytes(tempDir.FullName); + var buf = archiver.ToBytes(tempDir.FullPath); // Assert var zip = new ZipArchive(new MemoryStream(buf)); @@ -64,15 +64,15 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void ToStream_Should_Archive_Directories() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); using var tempDir = new TempDirectory(); - var d1 = Path.Join(tempDir.FullName, "d1"); + var d1 = Path.Join(tempDir.FullPath, "d1"); Directory.CreateDirectory(d1); var d2 = Path.Join(d1, "d2"); Directory.CreateDirectory(d2); // Act - var buf = archiver.ToBytes(tempDir.FullName); + var buf = archiver.ToBytes(tempDir.FullPath); // Assert var zip = new ZipArchive(new MemoryStream(buf)); @@ -92,7 +92,7 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void GetPackaging_Should_Be_application_zip() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); // Act var packaging = archiver.Name; @@ -105,7 +105,7 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void GetFileExtension_Should_Be_zip() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); // Act var ext = archiver.FileExtension; @@ -118,7 +118,7 @@ namespace Steeltoe.NetCoreToolService.Test.Archivers public void MimeType_Should_Be_application_zip() { // Arrange - var archiver = new ZipArchiver(); + var archiver = new ZipPackager(); // Act var ext = archiver.MimeType; diff --git a/test/NetCoreToolService.Test/NetCoreToolService.Test.csproj b/test/NetCoreToolService.Test/Steeltoe.NetCoreToolService.Test.csproj similarity index 51% rename from test/NetCoreToolService.Test/NetCoreToolService.Test.csproj rename to test/NetCoreToolService.Test/Steeltoe.NetCoreToolService.Test.csproj index 5583f8c..feb704f 100644 --- a/test/NetCoreToolService.Test/NetCoreToolService.Test.csproj +++ b/test/NetCoreToolService.Test/Steeltoe.NetCoreToolService.Test.csproj @@ -1,9 +1,5 @@ - - Steeltoe.NetCoreToolService.Test - - Directory.Build.props @@ -11,7 +7,7 @@ - +