Skip to content

Commit 87914f3

Browse files
authored
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)
1 parent 3d1660e commit 87914f3

File tree

13 files changed

+83
-55
lines changed

13 files changed

+83
-55
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- 642 Expose container port bindings automatically
88

9+
### Fixed
10+
11+
- 610 Trim traling slashes in Dockerfile directory path (otherwise, it cuts the first character of the relative path), Normalize paths to forward slashes
12+
913
## [2.2.0]
1014

1115
### Added

examples/WeatherForecast/README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
# Testcontainers for .NET WeatherForecast example
22

3-
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.
3+
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:
4+
5+
```console
6+
git clone git@github.com:testcontainers/testcontainers-dotnet.git
7+
cd ./testcontainers-dotnet/examples/WeatherForecast/
8+
dotnet build WeatherForecast.sln
9+
dotnet test WeatherForecast.sln
10+
```

examples/WeatherForecast/tests/WeatherForecast.Test/WeatherForecastTest.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using DotNet.Testcontainers.Builders;
1010
using OpenQA.Selenium;
1111
using OpenQA.Selenium.Chrome;
12+
using OpenQA.Selenium.Support.UI;
1213
using WeatherForecast.Entities;
1314
using Xunit;
1415

@@ -69,7 +70,7 @@ public Web(WeatherForecastContainer weatherForecastContainer)
6970

7071
[Fact]
7172
[Trait("Category", nameof(Web))]
72-
public async Task Get_WeatherForecast_ReturnsSevenDays()
73+
public void Get_WeatherForecast_ReturnsSevenDays()
7374
{
7475
// Given
7576
string ScreenshotFileName() => $"{nameof(Get_WeatherForecast_ReturnsSevenDays)}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.png";
@@ -83,8 +84,8 @@ public async Task Get_WeatherForecast_ReturnsSevenDays()
8384

8485
chrome.FindElement(By.TagName("fluent-button")).Click();
8586

86-
await Task.Delay(TimeSpan.FromSeconds(1))
87-
.ConfigureAwait(false);
87+
var wait = new WebDriverWait(chrome, TimeSpan.FromSeconds(10));
88+
wait.Until(webDriver => 1.Equals(webDriver.FindElements(By.TagName("span")).Count));
8889

8990
chrome.GetScreenshot().SaveAsFile(Path.Combine(CommonDirectoryPath.GetSolutionDirectory().DirectoryPath, ScreenshotFileName()));
9091

src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
namespace DotNet.Testcontainers.Builders
22
{
33
using System;
4+
using System.Threading;
5+
using System.Threading.Tasks;
46
using DotNet.Testcontainers.Configurations;
57
using DotNet.Testcontainers.Containers;
68

79
/// <inheritdoc cref="IDockerEndpointAuthenticationProvider" />
810
internal class DockerEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider
911
{
12+
private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
13+
1014
/// <inheritdoc />
1115
public virtual bool IsApplicable()
1216
{
@@ -29,7 +33,7 @@ public virtual bool IsAvailable()
2933
{
3034
try
3135
{
32-
dockerClient.System.PingAsync().GetAwaiter().GetResult();
36+
TaskFactory.StartNew(() => dockerClient.System.PingAsync()).Unwrap().GetAwaiter().GetResult();
3337
return true;
3438
}
3539
catch (Exception)

src/Testcontainers/Builders/TestcontainersBuilderAzuriteExtension.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static ITestcontainersBuilder<AzuriteTestcontainer> WithAzurite(this ITes
5858
}
5959

6060
return builder
61-
.WithCommand(GetExecutable(configuration))
61+
.WithEntrypoint(GetExecutable(configuration))
6262
.WithCommand(GetEnabledServices(configuration))
6363
.WithCommand(GetWorkspaceDirectoryPath())
6464
.WithCommand(GetDebugModeEnabled(configuration))

src/Testcontainers/Clients/TestcontainersClient.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace DotNet.Testcontainers.Clients
1717
/// <inheritdoc cref="ITestcontainersClient" />
1818
internal sealed class TestcontainersClient : ITestcontainersClient
1919
{
20-
public const string TestcontainersLabel = "testcontainers";
20+
public const string TestcontainersLabel = "org.testcontainers";
2121

2222
private readonly string osRootDirectory = Path.GetPathRoot(Directory.GetCurrentDirectory());
2323

src/Testcontainers/Configurations/IOperatingSystem.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ public interface IOperatingSystem
1919
/// <param name="path">Path to normalize.</param>
2020
/// <returns>Normalized path.</returns>
2121
[PublicAPI]
22-
string NormalizePath(string path);
22+
string NormalizePath([CanBeNull] string path);
2323
}
2424
}

src/Testcontainers/Configurations/Images/ImageFromDockerfileConfiguration.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace DotNet.Testcontainers.Configurations
66
/// <inheritdoc cref="IImageFromDockerfileConfiguration" />
77
internal sealed class ImageFromDockerfileConfiguration : DockerResourceConfiguration, IImageFromDockerfileConfiguration
88
{
9+
private static readonly IOperatingSystem OS = new Unix();
10+
911
/// <summary>
1012
/// Initializes a new instance of the <see cref="ImageFromDockerfileConfiguration" /> class.
1113
/// </summary>
@@ -36,8 +38,8 @@ public ImageFromDockerfileConfiguration(
3638
: base(dockerEndpointAuthenticationConfiguration, labels)
3739
{
3840
this.Image = image;
39-
this.Dockerfile = dockerfile;
40-
this.DockerfileDirectory = dockerfileDirectory;
41+
this.Dockerfile = OS.NormalizePath(dockerfile);
42+
this.DockerfileDirectory = OS.NormalizePath(dockerfileDirectory);
4143
this.DeleteIfExists = deleteIfExists;
4244
this.BuildArguments = buildArguments;
4345
}

src/Testcontainers/Configurations/Unix.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Unix(IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConfig)
5555
/// <inheritdoc />
5656
public string NormalizePath(string path)
5757
{
58-
return path.Replace('\\', '/');
58+
return path?.Replace('\\', '/');
5959
}
6060
}
6161
}

src/Testcontainers/Configurations/Windows.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Windows(IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConf
5555
/// <inheritdoc />
5656
public string NormalizePath(string path)
5757
{
58-
return path.Replace('/', '\\');
58+
return path?.Replace('/', '\\');
5959
}
6060
}
6161
}

src/Testcontainers/Images/DockerfileArchive.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public string Tar()
7777
foreach (var file in GetFiles(this.dockerfileDirectory.FullName))
7878
{
7979
// SharpZipLib drops the root path: https://github.com/icsharpcode/SharpZipLib/pull/582.
80-
var relativePath = file.Substring(this.dockerfileDirectory.FullName.Length + 1);
80+
var relativePath = file.Substring(this.dockerfileDirectory.FullName.TrimEnd('/', '\\').Length + 1);
8181

8282
if (dockerIgnoreFile.Denies(relativePath))
8383
{

tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using DotNet.Testcontainers.Configurations;
99
using DotNet.Testcontainers.Containers;
1010
using DotNet.Testcontainers.Images;
11+
using Microsoft.Extensions.Logging;
1112
using Xunit;
1213

1314
public abstract class ProtectDockerDaemonSocket : IAsyncLifetime
@@ -34,7 +35,7 @@ protected ProtectDockerDaemonSocket(ITestcontainersBuilder<TestcontainersContain
3435
.WithExposedPort(TlsPort)
3536
.WithPortBinding(TlsPort, true)
3637
.WithBindMount(this.hostCertsDirectoryPath, this.containerCertsDirectoryPath, AccessMode.ReadWrite)
37-
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(TlsPort))
38+
.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new UntilListenOn()))
3839
.Build();
3940
}
4041

@@ -67,5 +68,15 @@ public Task DisposeAsync()
6768
{
6869
return this.container.DisposeAsync().AsTask();
6970
}
71+
72+
private sealed class UntilListenOn : IWaitUntil
73+
{
74+
public async Task<bool> Until(ITestcontainersContainer testcontainers, ILogger logger)
75+
{
76+
var (_, stderr) = await testcontainers.GetLogs()
77+
.ConfigureAwait(false);
78+
return stderr != null && stderr.Contains("API listen on [::]:2376");
79+
}
80+
}
7081
}
7182
}

tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/AzuriteTestcontainerTest.cs

+40-41
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace DotNet.Testcontainers.Tests.Unit
1111
using Azure.Storage.Queues;
1212
using DotNet.Testcontainers.Configurations;
1313
using DotNet.Testcontainers.Tests.Fixtures;
14+
using JetBrains.Annotations;
1415
using Xunit;
1516

1617
public static class AzuriteTestcontainerTest
@@ -52,38 +53,9 @@ public void ShouldEnableAllServices(AzuriteTestcontainerConfiguration configurat
5253
}
5354
}
5455

55-
[Collection(nameof(Testcontainers))]
56-
public sealed class AllServicesEnabled : IClassFixture<AzuriteFixture.AzuriteDefaultFixture>, IClassFixture<AzuriteFixture.AzuriteWithCustomContainerPortsFixture>
56+
[UsedImplicitly]
57+
public sealed class AllServicesEnabled
5758
{
58-
private readonly AzuriteFixture.AzuriteDefaultFixture commonContainerPorts;
59-
60-
private readonly AzuriteFixture.AzuriteDefaultFixture customContainerPorts;
61-
62-
public AllServicesEnabled(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts, AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts)
63-
{
64-
this.commonContainerPorts = commonContainerPorts;
65-
this.customContainerPorts = customContainerPorts;
66-
}
67-
68-
private AllServicesEnabled(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts)
69-
{
70-
_ = commonContainerPorts;
71-
}
72-
73-
private AllServicesEnabled(AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts)
74-
{
75-
_ = customContainerPorts;
76-
}
77-
78-
[Fact]
79-
public async Task ConnectionEstablished()
80-
{
81-
var exception = await Record.ExceptionAsync(() => Task.WhenAll(EstablishConnection(this.commonContainerPorts), EstablishConnection(this.customContainerPorts)))
82-
.ConfigureAwait(false);
83-
84-
Assert.Null(exception);
85-
}
86-
8759
private static async Task EstablishConnection(AzuriteFixture.AzuriteDefaultFixture azurite)
8860
{
8961
// Given
@@ -118,6 +90,42 @@ private static async Task EstablishConnection(AzuriteFixture.AzuriteDefaultFixtu
11890
Assert.Contains(QueueServiceDataFileName, execResult.Stdout);
11991
Assert.Contains(TableServiceDataFileName, execResult.Stdout);
12092
}
93+
94+
[Collection(nameof(Testcontainers))]
95+
public sealed class CommonContainerPorts : IClassFixture<AzuriteFixture.AzuriteDefaultFixture>
96+
{
97+
private readonly AzuriteFixture.AzuriteDefaultFixture commonContainerPorts;
98+
99+
public CommonContainerPorts(AzuriteFixture.AzuriteDefaultFixture commonContainerPorts)
100+
{
101+
this.commonContainerPorts = commonContainerPorts;
102+
}
103+
104+
[Fact]
105+
public async Task ConnectionEstablished()
106+
{
107+
Assert.Null(await Record.ExceptionAsync(() => EstablishConnection(this.commonContainerPorts))
108+
.ConfigureAwait(false));
109+
}
110+
}
111+
112+
[Collection(nameof(Testcontainers))]
113+
public sealed class CustomContainerPorts : IClassFixture<AzuriteFixture.AzuriteWithCustomContainerPortsFixture>
114+
{
115+
private readonly AzuriteFixture.AzuriteDefaultFixture customContainerPorts;
116+
117+
public CustomContainerPorts(AzuriteFixture.AzuriteWithCustomContainerPortsFixture customContainerPorts)
118+
{
119+
this.customContainerPorts = customContainerPorts;
120+
}
121+
122+
[Fact]
123+
public async Task ConnectionEstablished()
124+
{
125+
Assert.Null(await Record.ExceptionAsync(() => EstablishConnection(this.customContainerPorts))
126+
.ConfigureAwait(false));
127+
}
128+
}
121129
}
122130

123131
[Collection(nameof(Testcontainers))]
@@ -256,16 +264,7 @@ public sealed class CustomLocation : IClassFixture<AzuriteFixture.AzuriteWithCus
256264

257265
public CustomLocation(AzuriteFixture.AzuriteWithCustomWorkspaceFixture azurite)
258266
{
259-
if (Directory.Exists(azurite.Configuration.Location))
260-
{
261-
this.dataFiles = Directory.EnumerateFiles(azurite.Configuration.Location, "*", SearchOption.TopDirectoryOnly)
262-
.Select(Path.GetFileName)
263-
.ToArray();
264-
}
265-
else
266-
{
267-
this.dataFiles = Array.Empty<string>();
268-
}
267+
this.dataFiles = Directory.Exists(azurite.Configuration.Location) ? Directory.EnumerateFiles(azurite.Configuration.Location, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) : Array.Empty<string>();
269268
}
270269

271270
[Fact]

0 commit comments

Comments
 (0)