From 87914f38a67a57e23c61542e30f86842928bfe84 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:39:27 +0100 Subject: [PATCH] fix(#610): Trim traling slashes in Dockerfile directory path (otherwise, it cuts the first character of the relative path), Normalize paths to forward slashes (#651) --- CHANGELOG.md | 4 + examples/WeatherForecast/README.md | 9 ++- .../WeatherForecastTest.cs | 7 +- .../DockerEndpointAuthenticationProvider.cs | 6 +- .../TestcontainersBuilderAzuriteExtension.cs | 2 +- .../Clients/TestcontainersClient.cs | 2 +- .../Configurations/IOperatingSystem.cs | 2 +- .../ImageFromDockerfileConfiguration.cs | 6 +- src/Testcontainers/Configurations/Unix.cs | 2 +- src/Testcontainers/Configurations/Windows.cs | 2 +- .../Images/DockerfileArchive.cs | 2 +- .../Unix/ProtectDockerDaemonSocket.cs | 13 ++- .../Databases/AzuriteTestcontainerTest.cs | 81 +++++++++---------- 13 files changed, 83 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5071c58d8..cb622a504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - 642 Expose container port bindings automatically +### Fixed + +- 610 Trim traling slashes in Dockerfile directory path (otherwise, it cuts the first character of the relative path), Normalize paths to forward slashes + ## [2.2.0] ### Added diff --git a/examples/WeatherForecast/README.md b/examples/WeatherForecast/README.md index c28a48061..975590cfa 100644 --- a/examples/WeatherForecast/README.md +++ b/examples/WeatherForecast/README.md @@ -1,3 +1,10 @@ # Testcontainers for .NET WeatherForecast example -This example builds and ships a Blazor application in a Docker image build, runs a Docker container and executes tests against a running instance of the application. Testcontainers for .NET takes care of the Docker image build and the Docker container that hosts the application. Spin up as much as containers as you like and run your tests heavily in parallel. +This example builds and ships a Blazor application in a Docker image build, runs a Docker container and executes tests against a running instance of the application. Testcontainers for .NET takes care of the Docker image build and the Docker container that hosts the application. Spin up as much as containers as you like and run your tests heavily in parallel. Checkout and run the tests on your machine: + +```console +git clone git@github.com:testcontainers/testcontainers-dotnet.git +cd ./testcontainers-dotnet/examples/WeatherForecast/ +dotnet build WeatherForecast.sln +dotnet test WeatherForecast.sln +``` diff --git a/examples/WeatherForecast/tests/WeatherForecast.Test/WeatherForecastTest.cs b/examples/WeatherForecast/tests/WeatherForecast.Test/WeatherForecastTest.cs index 698d4a64f..7dfe45156 100644 --- a/examples/WeatherForecast/tests/WeatherForecast.Test/WeatherForecastTest.cs +++ b/examples/WeatherForecast/tests/WeatherForecast.Test/WeatherForecastTest.cs @@ -9,6 +9,7 @@ using DotNet.Testcontainers.Builders; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Support.UI; using WeatherForecast.Entities; using Xunit; @@ -69,7 +70,7 @@ public Web(WeatherForecastContainer weatherForecastContainer) [Fact] [Trait("Category", nameof(Web))] - public async Task Get_WeatherForecast_ReturnsSevenDays() + public void Get_WeatherForecast_ReturnsSevenDays() { // Given string ScreenshotFileName() => $"{nameof(Get_WeatherForecast_ReturnsSevenDays)}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.png"; @@ -83,8 +84,8 @@ public async Task Get_WeatherForecast_ReturnsSevenDays() chrome.FindElement(By.TagName("fluent-button")).Click(); - await Task.Delay(TimeSpan.FromSeconds(1)) - .ConfigureAwait(false); + var wait = new WebDriverWait(chrome, TimeSpan.FromSeconds(10)); + wait.Until(webDriver => 1.Equals(webDriver.FindElements(By.TagName("span")).Count)); chrome.GetScreenshot().SaveAsFile(Path.Combine(CommonDirectoryPath.GetSolutionDirectory().DirectoryPath, ScreenshotFileName())); diff --git a/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs index f4b154423..dcd516dd5 100644 --- a/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs @@ -1,12 +1,16 @@ namespace DotNet.Testcontainers.Builders { using System; + using System.Threading; + using System.Threading.Tasks; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; /// internal class DockerEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider { + private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); + /// public virtual bool IsApplicable() { @@ -29,7 +33,7 @@ public virtual bool IsAvailable() { try { - dockerClient.System.PingAsync().GetAwaiter().GetResult(); + TaskFactory.StartNew(() => dockerClient.System.PingAsync()).Unwrap().GetAwaiter().GetResult(); return true; } catch (Exception) diff --git a/src/Testcontainers/Builders/TestcontainersBuilderAzuriteExtension.cs b/src/Testcontainers/Builders/TestcontainersBuilderAzuriteExtension.cs index 00762fd62..19e06ac28 100644 --- a/src/Testcontainers/Builders/TestcontainersBuilderAzuriteExtension.cs +++ b/src/Testcontainers/Builders/TestcontainersBuilderAzuriteExtension.cs @@ -58,7 +58,7 @@ public static ITestcontainersBuilder WithAzurite(this ITes } return builder - .WithCommand(GetExecutable(configuration)) + .WithEntrypoint(GetExecutable(configuration)) .WithCommand(GetEnabledServices(configuration)) .WithCommand(GetWorkspaceDirectoryPath()) .WithCommand(GetDebugModeEnabled(configuration)) diff --git a/src/Testcontainers/Clients/TestcontainersClient.cs b/src/Testcontainers/Clients/TestcontainersClient.cs index bf1ff8d74..e326d8104 100644 --- a/src/Testcontainers/Clients/TestcontainersClient.cs +++ b/src/Testcontainers/Clients/TestcontainersClient.cs @@ -17,7 +17,7 @@ namespace DotNet.Testcontainers.Clients /// internal sealed class TestcontainersClient : ITestcontainersClient { - public const string TestcontainersLabel = "testcontainers"; + public const string TestcontainersLabel = "org.testcontainers"; private readonly string osRootDirectory = Path.GetPathRoot(Directory.GetCurrentDirectory()); diff --git a/src/Testcontainers/Configurations/IOperatingSystem.cs b/src/Testcontainers/Configurations/IOperatingSystem.cs index 5c99cda38..c67293c54 100644 --- a/src/Testcontainers/Configurations/IOperatingSystem.cs +++ b/src/Testcontainers/Configurations/IOperatingSystem.cs @@ -19,6 +19,6 @@ public interface IOperatingSystem /// Path to normalize. /// Normalized path. [PublicAPI] - string NormalizePath(string path); + string NormalizePath([CanBeNull] string path); } } diff --git a/src/Testcontainers/Configurations/Images/ImageFromDockerfileConfiguration.cs b/src/Testcontainers/Configurations/Images/ImageFromDockerfileConfiguration.cs index a1057deb2..5e4b6842f 100644 --- a/src/Testcontainers/Configurations/Images/ImageFromDockerfileConfiguration.cs +++ b/src/Testcontainers/Configurations/Images/ImageFromDockerfileConfiguration.cs @@ -6,6 +6,8 @@ namespace DotNet.Testcontainers.Configurations /// internal sealed class ImageFromDockerfileConfiguration : DockerResourceConfiguration, IImageFromDockerfileConfiguration { + private static readonly IOperatingSystem OS = new Unix(); + /// /// Initializes a new instance of the class. /// @@ -36,8 +38,8 @@ public ImageFromDockerfileConfiguration( : base(dockerEndpointAuthenticationConfiguration, labels) { this.Image = image; - this.Dockerfile = dockerfile; - this.DockerfileDirectory = dockerfileDirectory; + this.Dockerfile = OS.NormalizePath(dockerfile); + this.DockerfileDirectory = OS.NormalizePath(dockerfileDirectory); this.DeleteIfExists = deleteIfExists; this.BuildArguments = buildArguments; } diff --git a/src/Testcontainers/Configurations/Unix.cs b/src/Testcontainers/Configurations/Unix.cs index e34247b8b..59b083e7a 100644 --- a/src/Testcontainers/Configurations/Unix.cs +++ b/src/Testcontainers/Configurations/Unix.cs @@ -55,7 +55,7 @@ public Unix(IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConfig) /// public string NormalizePath(string path) { - return path.Replace('\\', '/'); + return path?.Replace('\\', '/'); } } } diff --git a/src/Testcontainers/Configurations/Windows.cs b/src/Testcontainers/Configurations/Windows.cs index 0b58e636d..c280efe8b 100644 --- a/src/Testcontainers/Configurations/Windows.cs +++ b/src/Testcontainers/Configurations/Windows.cs @@ -55,7 +55,7 @@ public Windows(IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConf /// public string NormalizePath(string path) { - return path.Replace('/', '\\'); + return path?.Replace('/', '\\'); } } } diff --git a/src/Testcontainers/Images/DockerfileArchive.cs b/src/Testcontainers/Images/DockerfileArchive.cs index e4ebc7d70..bcb82085d 100644 --- a/src/Testcontainers/Images/DockerfileArchive.cs +++ b/src/Testcontainers/Images/DockerfileArchive.cs @@ -77,7 +77,7 @@ public string Tar() foreach (var file in GetFiles(this.dockerfileDirectory.FullName)) { // SharpZipLib drops the root path: https://github.com/icsharpcode/SharpZipLib/pull/582. - var relativePath = file.Substring(this.dockerfileDirectory.FullName.Length + 1); + var relativePath = file.Substring(this.dockerfileDirectory.FullName.TrimEnd('/', '\\').Length + 1); if (dockerIgnoreFile.Denies(relativePath)) { diff --git a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs index 2a14bf080..261c59080 100644 --- a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs +++ b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs @@ -8,6 +8,7 @@ using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; using DotNet.Testcontainers.Images; + using Microsoft.Extensions.Logging; using Xunit; public abstract class ProtectDockerDaemonSocket : IAsyncLifetime @@ -34,7 +35,7 @@ protected ProtectDockerDaemonSocket(ITestcontainersBuilder Until(ITestcontainersContainer testcontainers, ILogger logger) + { + var (_, stderr) = await testcontainers.GetLogs() + .ConfigureAwait(false); + return stderr != null && stderr.Contains("API listen on [::]:2376"); + } + } } } diff --git a/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/AzuriteTestcontainerTest.cs b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/AzuriteTestcontainerTest.cs index 5367c5d16..bde4e354c 100644 --- a/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/AzuriteTestcontainerTest.cs +++ b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/AzuriteTestcontainerTest.cs @@ -11,6 +11,7 @@ namespace DotNet.Testcontainers.Tests.Unit using Azure.Storage.Queues; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Tests.Fixtures; + using JetBrains.Annotations; using Xunit; public static class AzuriteTestcontainerTest @@ -52,38 +53,9 @@ public void ShouldEnableAllServices(AzuriteTestcontainerConfiguration configurat } } - [Collection(nameof(Testcontainers))] - public sealed class AllServicesEnabled : IClassFixture, IClassFixture + [UsedImplicitly] + public sealed class AllServicesEnabled { - private readonly AzuriteFixture.AzuriteDefaultFixture commonContainerPorts; - - private readonly AzuriteFixture.AzuriteDefaultFixture customContainerPorts; - - public AllServicesEnabled(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts, AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts) - { - this.commonContainerPorts = commonContainerPorts; - this.customContainerPorts = customContainerPorts; - } - - private AllServicesEnabled(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts) - { - _ = commonContainerPorts; - } - - private AllServicesEnabled(AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts) - { - _ = customContainerPorts; - } - - [Fact] - public async Task ConnectionEstablished() - { - var exception = await Record.ExceptionAsync(() => Task.WhenAll(EstablishConnection(this.commonContainerPorts), EstablishConnection(this.customContainerPorts))) - .ConfigureAwait(false); - - Assert.Null(exception); - } - private static async Task EstablishConnection(AzuriteFixture.AzuriteDefaultFixture azurite) { // Given @@ -118,6 +90,42 @@ private static async Task EstablishConnection(AzuriteFixture.AzuriteDefaultFixtu Assert.Contains(QueueServiceDataFileName, execResult.Stdout); Assert.Contains(TableServiceDataFileName, execResult.Stdout); } + + [Collection(nameof(Testcontainers))] + public sealed class CommonContainerPorts : IClassFixture + { + private readonly AzuriteFixture.AzuriteDefaultFixture commonContainerPorts; + + public CommonContainerPorts(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts) + { + this.commonContainerPorts = commonContainerPorts; + } + + [Fact] + public async Task ConnectionEstablished() + { + Assert.Null(await Record.ExceptionAsync(() => EstablishConnection(this.commonContainerPorts)) + .ConfigureAwait(false)); + } + } + + [Collection(nameof(Testcontainers))] + public sealed class CustomContainerPorts : IClassFixture + { + private readonly AzuriteFixture.AzuriteDefaultFixture customContainerPorts; + + public CustomContainerPorts(AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts) + { + this.customContainerPorts = customContainerPorts; + } + + [Fact] + public async Task ConnectionEstablished() + { + Assert.Null(await Record.ExceptionAsync(() => EstablishConnection(this.customContainerPorts)) + .ConfigureAwait(false)); + } + } } [Collection(nameof(Testcontainers))] @@ -256,16 +264,7 @@ public sealed class CustomLocation : IClassFixture(); - } + this.dataFiles = Directory.Exists(azurite.Configuration.Location) ? Directory.EnumerateFiles(azurite.Configuration.Location, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) : Array.Empty(); } [Fact]