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

feat: Reintroduce Papercut module #1268

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cdd7769
Added a wait strategy using http request is succeeded for the health …
TechLiam Sep 20, 2024
e2f55a3
Added retry mechanic to HTTP client check for Papercut test
TechLiam Sep 20, 2024
e6eb084
Added documentation back
TechLiam Sep 20, 2024
df7ecd0
Merge branch 'develop' into feature/reintroduced-papercut
TechLiam Sep 20, 2024
71cd801
Introduced batching of test runs so we are not spinning up to many co…
TechLiam Sep 21, 2024
99b34a5
Merge remote-tracking branch 'origin/feature/reintroduced-papercut' i…
TechLiam Sep 21, 2024
d7809e1
Now lets run the batches in Parallel
TechLiam Sep 21, 2024
ae74b7b
Added batch count for better output message
TechLiam Sep 21, 2024
1f7d187
Added missing bits form DotNetTestSettings
TechLiam Sep 21, 2024
369dcca
Removed retry around Papercut http API request
TechLiam Sep 21, 2024
039fee0
Merge branch 'testcontainers:develop' into feature/reintroduced-papercut
TechLiam Sep 21, 2024
6b668d6
Updated to offical image from Papercut which we should be using
TechLiam Sep 21, 2024
32b5689
Merge remote-tracking branch 'origin/feature/reintroduced-papercut' i…
TechLiam Sep 21, 2024
47d4083
Added removal of docker images after every test project only for CICD…
TechLiam Sep 21, 2024
b61aead
Merge branch 'refs/heads/develop' into feature/reintroduced-papercut
TechLiam Oct 6, 2024
d7bb326
Final bit of clean up of cake script changes
TechLiam Oct 6, 2024
466ccf5
Final bit of clean up of cake script changes
TechLiam Oct 6, 2024
3d76a07
Merge remote-tracking branch 'origin/feature/reintroduced-papercut' i…
TechLiam Oct 6, 2024
eb05e63
chore: Clean up usings, consider default TC config
HofmeisterAn Oct 10, 2024
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
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Neo4j", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle", "src\Testcontainers.Oracle\Testcontainers.Oracle.csproj", "{596EAFC1-0496-495C-B382-D57415FA456A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Papercut", "src\Testcontainers.Papercut\Testcontainers.Papercut.csproj", "{B2608563-8EE4-49AA-A9A0-B1614486AEEF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PostgreSql", "src\Testcontainers.PostgreSql\Testcontainers.PostgreSql.csproj", "{8AB91636-9055-4900-A72A-7CFFACDFDBF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PubSub", "src\Testcontainers.PubSub\Testcontainers.PubSub.csproj", "{E6642255-667D-476B-B584-089AA5E6C0B1}"
Expand Down Expand Up @@ -167,6 +169,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Neo4j.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle.Tests", "tests\Testcontainers.Oracle.Tests\Testcontainers.Oracle.Tests.csproj", "{4AC1088B-9965-4497-AC8E-570F1AD5631F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Papercut.Tests", "tests\Testcontainers.Papercut.Tests\Testcontainers.Papercut.Tests.csproj", "{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Platform.Linux.Tests", "tests\Testcontainers.Platform.Linux.Tests\Testcontainers.Platform.Linux.Tests.csproj", "{DA1D7ADE-452C-4369-83CC-56289176EACD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Platform.Windows.Tests", "tests\Testcontainers.Platform.Windows.Tests\Testcontainers.Platform.Windows.Tests.csproj", "{3E55CBE8-AFE8-426D-9470-49D63CD1051C}"
Expand Down Expand Up @@ -332,6 +336,10 @@ Global
{596EAFC1-0496-495C-B382-D57415FA456A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{596EAFC1-0496-495C-B382-D57415FA456A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{596EAFC1-0496-495C-B382-D57415FA456A}.Release|Any CPU.Build.0 = Release|Any CPU
{B2608563-8EE4-49AA-A9A0-B1614486AEEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2608563-8EE4-49AA-A9A0-B1614486AEEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2608563-8EE4-49AA-A9A0-B1614486AEEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2608563-8EE4-49AA-A9A0-B1614486AEEF}.Release|Any CPU.Build.0 = Release|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -508,6 +516,10 @@ Global
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Release|Any CPU.Build.0 = Release|Any CPU
{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20}.Release|Any CPU.Build.0 = Release|Any CPU
{DA1D7ADE-452C-4369-83CC-56289176EACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA1D7ADE-452C-4369-83CC-56289176EACD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA1D7ADE-452C-4369-83CC-56289176EACD}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -591,6 +603,7 @@ Global
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{ADC2372B-6FE0-421D-8277-BB628E8EFC22} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{596EAFC1-0496-495C-B382-D57415FA456A} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{B2608563-8EE4-49AA-A9A0-B1614486AEEF} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{8AB91636-9055-4900-A72A-7CFFACDFDBF0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{E6642255-667D-476B-B584-089AA5E6C0B1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{27D46863-65B9-4934-B3C8-2383B217A477} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -635,6 +648,7 @@ Global
{87A3F137-6DC3-4CE5-91E6-01797D076086} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{D3F63405-C0FA-4F83-8B79-E30BFF5FF5BF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{4AC1088B-9965-4497-AC8E-570F1AD5631F} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{F03FA970-BE2B-4AE2-96FE-7E1F805CEA20} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{DA1D7ADE-452C-4369-83CC-56289176EACD} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{3E55CBE8-AFE8-426D-9470-49D63CD1051C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
Expand Down
1 change: 1 addition & 0 deletions docs/modules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ await moduleNameContainer.StartAsync();
| NATS | `nats:2.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Nats) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Nats) |
| Neo4j | `neo4j:5.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.Neo4j) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Neo4j) |
| Oracle | `gvenzl/oracle-xe:21.3.0-slim-faststart` | [NuGet](https://www.nuget.org/packages/Testcontainers.Oracle) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Oracle) |
| Papercut | `changemakerstudiosus/papercut-smtp:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Papercut) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Papercut) |
| PostgreSQL | `postgres:15.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.PostgreSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PostgreSql) |
| PubSub | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.PubSub) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PubSub) |
| Pulsar | `apachepulsar/pulsar:3.0.6` | [NuGet](https://www.nuget.org/packages/Testcontainers.Pulsar) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Pulsar) |
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers.Neo4j/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
global using System;
global using System.Linq;
global using System.Text.RegularExpressions;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.Papercut/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
83 changes: 83 additions & 0 deletions src/Testcontainers.Papercut/PapercutBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
namespace Testcontainers.Papercut;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class PapercutBuilder : ContainerBuilder<PapercutBuilder, PapercutContainer, PapercutConfiguration>
{
public const string PapercutImage = "changemakerstudiosus/papercut-smtp:latest";

public const ushort SmtpPort = 25;

public const ushort HttpPort = 80;

/// <summary>
/// Initializes a new instance of the <see cref="PapercutBuilder" /> class.
/// </summary>
public PapercutBuilder()
: this(new PapercutConfiguration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="PapercutBuilder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private PapercutBuilder(PapercutConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override PapercutConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
public override PapercutContainer Build()
{
Validate();
return new PapercutContainer(DockerResourceConfiguration);
}

/// <inheritdoc />
protected override PapercutBuilder Init()
{
return base.Init()
.WithImage(PapercutImage)
.WithPortBinding(SmtpPort, true)
.WithPortBinding(HttpPort, true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request =>
request.ForPath("/health").ForPort(HttpPort).ForResponseMessageMatching(IsInstanceHealthyAsync)));
}

/// <inheritdoc />
protected override PapercutBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PapercutConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PapercutBuilder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PapercutConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PapercutBuilder Merge(PapercutConfiguration oldValue, PapercutConfiguration newValue)
{
return new PapercutBuilder(new PapercutConfiguration(oldValue, newValue));
}

/// <summary>
/// Determines whether the instance is healthy or not.
/// </summary>
/// <param name="response">The HTTP response that contains the health information.</param>
/// <returns>A value indicating whether the instance is healthy or not.</returns>
private static async Task<bool> IsInstanceHealthyAsync(HttpResponseMessage response)
{
var body = await response.Content.ReadAsStringAsync()
.ConfigureAwait(false);

return "Papercut WebUI server started successfully.".Equals(body, StringComparison.OrdinalIgnoreCase);
}
}
53 changes: 53 additions & 0 deletions src/Testcontainers.Papercut/PapercutConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Testcontainers.Papercut;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class PapercutConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="PapercutConfiguration" /> class.
/// </summary>
public PapercutConfiguration()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="PapercutConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PapercutConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PapercutConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PapercutConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PapercutConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PapercutConfiguration(PapercutConfiguration resourceConfiguration)
: this(new PapercutConfiguration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PapercutConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public PapercutConfiguration(PapercutConfiguration oldValue, PapercutConfiguration newValue)
: base(oldValue, newValue)
{
}
}
29 changes: 29 additions & 0 deletions src/Testcontainers.Papercut/PapercutContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Testcontainers.Papercut;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class PapercutContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="PapercutContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public PapercutContainer(PapercutConfiguration configuration)
: base(configuration)
{
}

/// <summary>
/// Gets the SMTP port.
/// </summary>
public ushort SmtpPort => GetMappedPublicPort(PapercutBuilder.SmtpPort);

/// <summary>
/// Gets the Papercut base address.
/// </summary>
/// <returns>The Papercut base address.</returns>
public string GetBaseAddress()
{
return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(PapercutBuilder.HttpPort)).ToString();
}
}
12 changes: 12 additions & 0 deletions src/Testcontainers.Papercut/Testcontainers.Papercut.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions src/Testcontainers.Papercut/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
global using System;
global using System.Net.Http;
global using System.Threading.Tasks;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using JetBrains.Annotations;
1 change: 1 addition & 0 deletions tests/Testcontainers.Papercut.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
65 changes: 65 additions & 0 deletions tests/Testcontainers.Papercut.Tests/PapercutContainerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
namespace Testcontainers.Papercut;

public sealed class PapercutContainerTest : IAsyncLifetime
{
private readonly PapercutContainer _papercutContainer = new PapercutBuilder().Build();

public Task InitializeAsync()
{
return _papercutContainer.StartAsync();
}

public Task DisposeAsync()
{
return _papercutContainer.DisposeAsync().AsTask();
}

[Fact]
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
public async Task ReceivesSentMessage()
{
// Given
const string subject = "Test";

using var smtpClient = new SmtpClient(_papercutContainer.Hostname, _papercutContainer.SmtpPort);

using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_papercutContainer.GetBaseAddress());

// When
smtpClient.Send("[email protected]", "[email protected]", subject, "A test message");

var messagesJson = await httpClient.GetStringAsync("/api/messages")
.ConfigureAwait(true);

var jsonDocument = JsonDocument.Parse(messagesJson);
var messages = jsonDocument.RootElement.GetProperty("messages").Deserialize<Message[]>();

// Then
Assert.Single(messages, message => subject.Equals(message.Subject));
}

private readonly struct Message
{
[JsonConstructor]
public Message(string id, string subject, string size, DateTime createdAt)
{
Id = id;
Subject = subject;
Size = size;
CreatedAt = createdAt;
}

[JsonPropertyName("id")]
public string Id { get; }

[JsonPropertyName("subject")]
public string Subject { get; }

[JsonPropertyName("size")]
public string Size { get; }

[JsonPropertyName("createdAt")]
public DateTime CreatedAt { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="coverlet.collector"/>
<PackageReference Include="xunit.runner.visualstudio"/>
<PackageReference Include="xunit"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/Testcontainers.Papercut/Testcontainers.Papercut.csproj"/>
<ProjectReference Include="../Testcontainers.Commons/Testcontainers.Commons.csproj"/>
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions tests/Testcontainers.Papercut.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
global using System;
global using System.Net.Http;
global using System.Net.Mail;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
global using Xunit;