From 4bb2edcd7789d35d8cd072dd719286ce59a4430d Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Sun, 18 Aug 2024 15:16:42 +0200 Subject: [PATCH] (refactor): exec tests --- src/PodmanClient/PodmanClientDotNet.csproj | 2 +- src/PodmanClientDotNet.Tests/Archives/Tar.cs | 54 +++++ .../PodmanClientContainersTests.cs | 97 ++++++--- .../PodmanClientDotNet.Tests.csproj | 1 + .../PodmanClientExecTests.cs | 193 ++++++++++++++++++ .../PodmanClientImagesTests.cs | 8 + 6 files changed, 327 insertions(+), 28 deletions(-) create mode 100644 src/PodmanClientDotNet.Tests/Archives/Tar.cs create mode 100644 src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs diff --git a/src/PodmanClient/PodmanClientDotNet.csproj b/src/PodmanClient/PodmanClientDotNet.csproj index da9dc2b..46b0db0 100644 --- a/src/PodmanClient/PodmanClientDotNet.csproj +++ b/src/PodmanClient/PodmanClientDotNet.csproj @@ -8,7 +8,7 @@ PodmanClient.DotNet - 1.0.2 + 1.0.3 Maksym Sadovnychyy MAKS-IT PodmanClient diff --git a/src/PodmanClientDotNet.Tests/Archives/Tar.cs b/src/PodmanClientDotNet.Tests/Archives/Tar.cs new file mode 100644 index 0000000..8dd6dbe --- /dev/null +++ b/src/PodmanClientDotNet.Tests/Archives/Tar.cs @@ -0,0 +1,54 @@ +using ICSharpCode.SharpZipLib.Tar; + +namespace MaksIT.PodmanClientDotNet.Tests.Archives +{ + public static class Tar + { + public static void CreateTarFromDirectory(string sourceDirectory, Stream outputStream) + { + using (var tarOutputStream = new TarOutputStream(outputStream)) + { + tarOutputStream.IsStreamOwner = false; + AddDirectoryFilesToTar(tarOutputStream, sourceDirectory, true); + } + } + + static void AddDirectoryFilesToTar(TarOutputStream tarOutputStream, string sourceDirectory, bool recursive, string baseDirectory = null) + { + // If baseDirectory is null, set it to the sourceDirectory to start with + if (baseDirectory == null) + { + baseDirectory = sourceDirectory; + } + + var directoryInfo = new DirectoryInfo(sourceDirectory); + + foreach (var fileInfo in directoryInfo.GetFiles()) + { + // Calculate the relative path for the file within the base directory + string relativePath = Path.GetRelativePath(baseDirectory, fileInfo.FullName); + + // Create tar entry with the relative path + var entry = TarEntry.CreateEntryFromFile(fileInfo.FullName); + entry.Name = relativePath.Replace(Path.DirectorySeparatorChar, '/'); // Use Unix-style path separators + tarOutputStream.PutNextEntry(entry); + + using (var fileStream = fileInfo.OpenRead()) + { + fileStream.CopyTo(tarOutputStream); + } + + tarOutputStream.CloseEntry(); + } + + if (recursive) + { + foreach (var subDirectory in directoryInfo.GetDirectories()) + { + // Recurse into subdirectories, passing the base directory + AddDirectoryFilesToTar(tarOutputStream, subDirectory.FullName, true, baseDirectory); + } + } + } + } +} diff --git a/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs index aceceb8..8b1e257 100644 --- a/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs +++ b/src/PodmanClientDotNet.Tests/PodmanClientContainersTests.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -using Xunit; -using System.Threading.Tasks; + +using MaksIT.PodmanClientDotNet.Tests.Archives; namespace MaksIT.PodmanClientDotNet.Tests { public class PodmanClientContainersTests { @@ -15,11 +15,11 @@ namespace MaksIT.PodmanClientDotNet.Tests { _client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60); } - #region Success + #region Success Cases [Fact] public async Task PodmanClient_ContainerLifecycle_Success() { // Arrange - string containerName = "test-container"; + string containerName = $"podman-client-test-{Guid.NewGuid()}"; string image = "alpine:latest"; // Act & Assert @@ -30,20 +30,54 @@ namespace MaksIT.PodmanClientDotNet.Tests { await ForceDeleteContainerAsync(containerId); } + [Fact] + public async Task CopyFilesToContainer_Success() { + // Arrange + string containerName = $"podman-client-test-{Guid.NewGuid()}"; + string image = "alpine:latest"; + string pathInContainer = "/podman-test-copy"; + + // Create temporary folder with random files + string tempFolderPath = CreateTemporaryFolderWithFiles(); + + try { + // Act + await PullImageAsync(image); + var containerId = await CreateContainerAsync(containerName, image); + await StartContainerAsync(containerId); + + // Archive the folder and copy to container + using (var tarStream = CreateTarStream(tempFolderPath)) { + await CopyToContainerAsync(containerId, tarStream, pathInContainer); + } + + // Stop and delete the container + await StopContainerAsync(containerId); + await ForceDeleteContainerAsync(containerId); + } + finally { + // Cleanup: Delete temporary folder + if (Directory.Exists(tempFolderPath)) { + Directory.Delete(tempFolderPath, true); + } + } + } + #endregion + + #region Helper Methods private async Task PullImageAsync(string image) { - // Implement the logic to pull the image var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(image)); Assert.Null(exception); // Expect no exceptions if the pull was successful } private async Task CreateContainerAsync(string containerName, string image) { var createResponse = await _client.CreateContainerAsync( - name: containerName, - image: image, - command: new List { - "sh", "-c", - "sleep infinity" - }); + name: containerName, + image: image, + command: new List { + "sh", "-c", + "sleep infinity" + }); Assert.NotNull(createResponse); Assert.False(string.IsNullOrEmpty(createResponse.Id)); // Ensure a valid container ID is returned return createResponse.Id; @@ -63,42 +97,51 @@ namespace MaksIT.PodmanClientDotNet.Tests { var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(containerId)); Assert.Null(exception); // Expect no exceptions if the container was deleted successfully } + + private async Task CopyToContainerAsync(string containerId, Stream tarStream, string path) { + var exception = await Record.ExceptionAsync(() => _client.ExtractArchiveToContainerAsync(containerId, tarStream, path)); + Assert.Null(exception); // Expect no exceptions if the copy was successful + } + + private string CreateTemporaryFolderWithFiles() { + string tempFolder = Path.Combine(Path.GetTempPath(), $"podman-test-{Guid.NewGuid()}"); + Directory.CreateDirectory(tempFolder); + + // Create some random files + for (int i = 0; i < 5; i++) { + File.WriteAllText(Path.Combine(tempFolder, $"test-file-{i}.txt"), $"This is test file {i}"); + } + + return tempFolder; + } + + private Stream CreateTarStream(string folderPath) { + var memoryStream = new MemoryStream(); + Tar.CreateTarFromDirectory(folderPath, memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); // Reset the stream position for reading + return memoryStream; + } #endregion - #region Fail + #region Fail Cases [Fact] public async Task StartContainerAsync_Should_HandleErrors() { - // Arrange string invalidContainerId = "invalid-container-id"; - - // Act var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(invalidContainerId)); - - // Assert Assert.NotNull(exception); // Expect an exception due to invalid container ID } [Fact] public async Task StopContainerAsync_Should_HandleErrors() { - // Arrange string invalidContainerId = "invalid-container-id"; - - // Act var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(invalidContainerId)); - - // Assert Assert.NotNull(exception); // Expect an exception due to invalid container ID } [Fact] public async Task ForceDeleteContainerAsync_Should_HandleErrors() { - // Arrange string invalidContainerId = "invalid-container-id"; - - // Act var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(invalidContainerId)); - - // Assert Assert.NotNull(exception); // Expect an exception due to invalid container ID } #endregion diff --git a/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj index 17f4ec6..06fff20 100644 --- a/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj +++ b/src/PodmanClientDotNet.Tests/PodmanClientDotNet.Tests.csproj @@ -16,6 +16,7 @@ + all diff --git a/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs new file mode 100644 index 0000000..dc0c396 --- /dev/null +++ b/src/PodmanClientDotNet.Tests/PodmanClientExecTests.cs @@ -0,0 +1,193 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace MaksIT.PodmanClientDotNet.Tests { + public class PodmanClientExecTests { + private readonly PodmanClient _client; + + public PodmanClientExecTests() { + var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); + var logger = loggerFactory.CreateLogger(); + + _client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60); + } + + #region Success Cases + + [Fact] + public async Task Full_ContainerLifecycle_With_Exec_Should_Succeed() { + // Arrange + string containerName = $"podman-client-test-{Guid.NewGuid()}"; + string image = "alpine:latest"; + + // Act & Assert + // 1. Pull the image + await PullImageAsync(image); + + // 2. Create the container with sleep infinity command + var containerId = await CreateContainerAsync(containerName, image); + + // 3. Start the container + await StartContainerAsync(containerId); + + // 4. Execute a command in the container to install a package (e.g., "apk add curl") + var execId = await CreateExecAsync(containerName, new[] { "apk", "add", "--no-cache", "curl" }); + await StartExecAsync(execId); + + // 5. Stop the container + await StopContainerAsync(containerId); + + // 6. Delete the container + await ForceDeleteContainerAsync(containerId); + } + + #endregion + + #region Helper Methods + + private async Task PullImageAsync(string image) { + var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(image)); + Assert.Null(exception); // Expect no exceptions if the pull was successful + } + + private async Task CreateContainerAsync(string containerName, string image) { + var createResponse = await _client.CreateContainerAsync( + name: containerName, + image: image, + command: new List { + "sh", "-c", + "sleep infinity" + }); + Assert.NotNull(createResponse); + Assert.False(string.IsNullOrEmpty(createResponse.Id)); // Ensure a valid container ID is returned + return createResponse.Id; + } + + private async Task StartContainerAsync(string containerId) { + var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(containerId)); + Assert.Null(exception); // Expect no exceptions if the container was started successfully + } + + private async Task CreateExecAsync(string containerName, string[] cmd) { + var execResponse = await _client.CreateExecAsync(containerName, cmd); + Assert.NotNull(execResponse); + Assert.False(string.IsNullOrEmpty(execResponse.Id)); // Ensure a valid exec ID is returned + return execResponse.Id; + } + + private async Task StartExecAsync(string execId) { + var exception = await Record.ExceptionAsync(() => _client.StartExecAsync(execId)); + Assert.Null(exception); // Expect no exceptions if the exec command was started successfully + } + + private async Task StopContainerAsync(string containerId) { + var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(containerId)); + Assert.Null(exception); // Expect no exceptions if the container was stopped successfully + } + + private async Task ForceDeleteContainerAsync(string containerId) { + var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(containerId)); + Assert.Null(exception); // Expect no exceptions if the container was deleted successfully + } + + #endregion + + #region Fail Cases + + [Fact] + public async Task PullImageAsync_Should_HandleErrors() { + // Arrange + string invalidImageReference = "invalidimage:latest"; // Intentionally wrong image + + // Act + var exception = await Record.ExceptionAsync(() => _client.PullImageAsync(invalidImageReference)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task CreateContainerAsync_Should_HandleErrors() { + // Arrange + string invalidImage = "invalidimage:latest"; // Intentionally wrong image + string containerName = "test-container"; + + // Act + var exception = await Record.ExceptionAsync(() => _client.CreateContainerAsync(containerName, invalidImage, new List { "sh", "-c", "sleep infinity" })); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task StartContainerAsync_Should_HandleErrors() { + // Arrange + string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID + + // Act + var exception = await Record.ExceptionAsync(() => _client.StartContainerAsync(invalidContainerId)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task CreateExecAsync_Should_HandleErrors() { + // Arrange + string containerName = "invalid-container"; // Intentionally wrong container name + var cmd = new[] { "apk", "add", "--no-cache", "curl" }; + + // Act + var exception = await Record.ExceptionAsync(() => _client.CreateExecAsync(containerName, cmd)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task StartExecAsync_Should_HandleErrors() { + // Arrange + string invalidExecId = "invalid-exec-id"; // Intentionally wrong exec ID + + // Act + var exception = await Record.ExceptionAsync(() => _client.StartExecAsync(invalidExecId)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task StopContainerAsync_Should_HandleErrors() { + // Arrange + string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID + + // Act + var exception = await Record.ExceptionAsync(() => _client.StopContainerAsync(invalidContainerId)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + [Fact] + public async Task ForceDeleteContainerAsync_Should_HandleErrors() { + // Arrange + string invalidContainerId = "invalid-container-id"; // Intentionally wrong container ID + + // Act + var exception = await Record.ExceptionAsync(() => _client.ForceDeleteContainerAsync(invalidContainerId)); + + // Assert + Assert.NotNull(exception); + Assert.IsType(exception); // Ensure it's the expected type + } + + #endregion + } +} diff --git a/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs b/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs index 72fe949..1a87e41 100644 --- a/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs +++ b/src/PodmanClientDotNet.Tests/PodmanClientImagesTests.cs @@ -15,6 +15,8 @@ public class PodmanClientImagesTests { _client = new PodmanClient(logger, "http://wks0002.corp.maks-it.com:8080", 60); } + #region Success Cases + [Fact] public async Task PodmanClient_IntegrationTests() { // Test 1: Pull Image - Success @@ -23,6 +25,9 @@ public class PodmanClientImagesTests { // Test 2: Tag Image - Success await TagImageAsync_Should_Succeed(); } + #endregion + + #region Helper Methods private async Task PullImageAsync_Should_Succeed() { // Arrange @@ -47,7 +52,9 @@ public class PodmanClientImagesTests { // Assert Assert.Null(exception); // Expect no exceptions if the tagging was successful } + #endregion + #region Fail Cases [Fact] public async Task PodmanClient_PullImage_Errors() { @@ -76,4 +83,5 @@ public class PodmanClientImagesTests { Assert.NotNull(exception); // Expect an exception due to nonexistent image Assert.IsType(exception); // Ensure it's the expected type } + #endregion }