Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/minor improvements #621

Merged
merged 4 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- 525 Read ServerURL, Username and Secret field from CredsStore response to pull private Docker images
- 595 Implement `TestcontainersContainer.DisposeAsync` thread safe (rename `TestcontainersState` to `TestcontainersStates`)
- 604 Do not deny all files in the Docker image tarball when a `.dockerignore` entry ends with `/`
- 610 Do not deny all files in the Docker image tarball when a `.dockerignore` entry ends with `/*`

## [2.1.0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using DotNet.Testcontainers.Containers;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using WeatherForecast.Entities;
using WeatherForecast.Repositories;
using Xunit;

namespace WeatherForecast.InProcess.Test;
Expand Down Expand Up @@ -41,8 +43,12 @@ public Task DisposeAsync()
return _mssqlContainer.DisposeAsync().AsTask();
}

public sealed class Api : IClassFixture<WeatherForecastTest>
public sealed class Api : IClassFixture<WeatherForecastTest>, IDisposable
{
private readonly WebApplicationFactory<Program> _webApplicationFactory;

private readonly IServiceScope _serviceScope;

private readonly HttpClient _httpClient;

public Api(WeatherForecastTest weatherForecastTest)
Expand All @@ -51,7 +57,16 @@ public Api(WeatherForecastTest weatherForecastTest)
Environment.SetEnvironmentVariable("ASPNETCORE_Kestrel__Certificates__Default__Path", "certificate.crt");
Environment.SetEnvironmentVariable("ASPNETCORE_Kestrel__Certificates__Default__Password", "password");
Environment.SetEnvironmentVariable("ConnectionStrings__DefaultConnection", weatherForecastTest._mssqlContainer.ConnectionString);
_httpClient = new WebApplicationFactory<Program>().CreateClient();
_webApplicationFactory = new WebApplicationFactory<Program>();
_serviceScope = _webApplicationFactory.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
_httpClient = _webApplicationFactory.CreateClient();
}

public void Dispose()
{
_httpClient.Dispose();
_serviceScope.Dispose();
_webApplicationFactory.Dispose();
}

[Fact]
Expand All @@ -75,5 +90,22 @@ public async Task Get_WeatherForecast_ReturnsSevenDays()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(7, weatherForecast!.Count());
}

[Fact]
[Trait("Category", nameof(Api))]
public async Task Get_WeatherForecast_ReturnsThreeDays()
{
// Given
const int threeDays = 3;

var weatherDataReadOnlyRepository = _serviceScope.ServiceProvider.GetRequiredService<IWeatherDataReadOnlyRepository>();

// When
var weatherForecast = await weatherDataReadOnlyRepository.GetAllAsync(string.Empty, string.Empty, DateTime.Today, DateTime.Today.AddDays(threeDays))
.ConfigureAwait(false);

// Then
Assert.Equal(threeDays, weatherForecast.Count());
}
}
}
38 changes: 18 additions & 20 deletions src/Testcontainers/Containers/TestcontainersContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace DotNet.Testcontainers.Containers
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet;
Expand All @@ -20,8 +19,6 @@ public class TestcontainersContainer : ITestcontainersContainer
{
private const TestcontainersStates ContainerHasBeenCreatedStates = TestcontainersStates.Created | TestcontainersStates.Running | TestcontainersStates.Exited;

private static readonly string[] DockerDesktopGateways = { "host.docker.internal", "gateway.docker.internal" };

private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);

private readonly ITestcontainersClient client;
Expand Down Expand Up @@ -379,30 +376,31 @@ private void ThrowIfContainerHasNotBeenCreated()

private string GetContainerGateway()
{
if (!this.client.IsRunningInsideDocker || !ContainerHasBeenCreatedStates.HasFlag(this.State))
const string localhost = "localhost";

if (!ContainerHasBeenCreatedStates.HasFlag(this.State))
{
return "localhost";
return localhost;
}

string GetDockerDesktopGateway(string dockerDesktopGateway)
if (!this.client.IsRunningInsideDocker)
{
try
{
_ = Dns.GetHostEntry(dockerDesktopGateway);
return dockerDesktopGateway;
}
catch
{
return null;
}
return localhost;
}

var endpointSettings = this.container.NetworkSettings.Networks.TryGetValue("bridge", out var network) ? network : new EndpointSettings();
if (!string.IsNullOrWhiteSpace(endpointSettings.Gateway))
{
return endpointSettings.Gateway;
}

var hostname = DockerDesktopGateways
.AsParallel()
.Select(GetDockerDesktopGateway)
.FirstOrDefault(dockerDesktopGateway => dockerDesktopGateway != null);
var networkSettings = this.container.NetworkSettings;
if (!string.IsNullOrWhiteSpace(networkSettings.Gateway))
{
return networkSettings.Gateway;
}

return hostname ?? this.container.NetworkSettings.Networks.First().Value.Gateway;
return localhost;
}
}
}
2 changes: 1 addition & 1 deletion src/Testcontainers/Images/DockerfileArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public string Tar()

var tarEntry = TarEntry.CreateEntryFromFile(file);
tarEntry.Name = relativePath;
tarArchive.WriteEntry(tarEntry, true);
tarArchive.WriteEntry(tarEntry, false);
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/Testcontainers/Images/IgnoreFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ public IgnoreFile(IEnumerable<string> patterns, ILogger logger)
// Trim each line.
.Select(line => line.Trim())

// Exclude files and directories.
.Select(line => line.TrimEnd('/'))

// Exclude files and directories.
.Select(line =>
{
const string filesAndDirectories = "/*";
return line.EndsWith(filesAndDirectories, StringComparison.InvariantCulture) ? line.Substring(0, line.Length - filesAndDirectories.Length) : line;
})

// Remove empty line.
.Where(line => !string.IsNullOrEmpty(line))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ public sealed class IgnoreFileFixture : TheoryData<IgnoreFile, string, bool>
public IgnoreFileFixture()
{
var logger = TestcontainersSettings.Logger;
var ignoreDirectory = new IgnoreFile(new[] { "bin/", "obj/" }, logger);
var ignoreFilesAndDirectories = new IgnoreFile(new[] { "bin/", "obj/*" }, logger);
var ignoreNonRecursiveFiles = new IgnoreFile(new[] { "*/temp*" }, logger);
var ignoreNonRecursiveNestedFiles = new IgnoreFile(new[] { "*/*/temp*" }, logger);
var ignoreRecursiveFiles = new IgnoreFile(new[] { "**/*.txt" }, logger);
var ignoreSingleCharacterFiles = new IgnoreFile(new[] { "temp?" }, logger);
var ignoreExceptionFiles = new IgnoreFile(new[] { "*.md", "!README*.md", "README-secret.md" }, logger);
this.Add(ignoreDirectory, "Testcontainers.sln", true);
this.Add(ignoreDirectory, "bin/Debug/net6.0", false);
this.Add(ignoreDirectory, "obj/Debug/net6.0", false);
this.Add(ignoreFilesAndDirectories, "bin/Debug/net6.0", false);
this.Add(ignoreFilesAndDirectories, "obj/Debug/net6.0", false);
this.Add(ignoreFilesAndDirectories, "Testcontainers.sln", true);
this.Add(ignoreNonRecursiveFiles, "lipsum/temp", false);
this.Add(ignoreNonRecursiveFiles, "lipsum/temp.txt", false);
this.Add(ignoreNonRecursiveFiles, "lipsum/lorem/temp", true);
Expand Down