-
-
Notifications
You must be signed in to change notification settings - Fork 350
[Feature] Add Mailpit module #1727
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| root = true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| namespace Testcontainers.Mailpit; | ||
|
|
||
| /// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" /> | ||
| [PublicAPI] | ||
| public sealed class MailpitBuilder | ||
| : ContainerBuilder<MailpitBuilder, MailpitContainer, MailpitConfiguration> | ||
| { | ||
| [Obsolete("This constant is obsolete and will be removed in the future. Use the constructor with the image parameter instead: https://github.com/testcontainers/testcontainers-dotnet/discussions/1470#discussioncomment-15185721.")] | ||
| public const string MailpitImage = "axllent/mailpit:v1.30"; | ||
|
|
||
| public const ushort SmtpPort = 1025; | ||
|
|
||
| public const ushort WebPort = 8025; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitBuilder" /> class. | ||
| /// </summary> | ||
| [Obsolete("This parameterless constructor is obsolete and will be removed. Use the constructor with the image parameter instead: https://github.com/testcontainers/testcontainers-dotnet/discussions/1470#discussioncomment-15185721.")] | ||
| public MailpitBuilder() | ||
| :this(MailpitImage) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="image">The full Docker image name, including the image repository and tag (e.g., <c>axllent/mailpit:v1.30</c>).</param> | ||
| /// <remarks> | ||
| /// Docker image tags available at <see href="https://hub.docker.com/r/axllent/mailpit/tags" />. | ||
| /// </remarks> | ||
| public MailpitBuilder(string image) | ||
| : this(new DockerImage(image)) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="image">An <see cref="IImage" /> instance that specifies the Docker image to be used for the container builder configuration.</param> | ||
| /// <remarks> | ||
| /// Docker image tags available at <see href="https://hub.docker.com/r/axllent/mailpit/tags" />. | ||
| /// </remarks> | ||
| public MailpitBuilder(IImage image) | ||
| : this(new MailpitConfiguration()) | ||
| { | ||
| DockerResourceConfiguration = Init().WithImage(image).DockerResourceConfiguration; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| private MailpitBuilder(MailpitConfiguration resourceConfiguration) | ||
| : base(resourceConfiguration) | ||
| { | ||
| DockerResourceConfiguration = resourceConfiguration; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override MailpitConfiguration DockerResourceConfiguration { get; } | ||
|
|
||
| /// <summary> | ||
| /// Sets the Mailpit MP_SMTP_AUTH config. | ||
| /// </summary> | ||
| /// <param name="credentials">The credentials to be used in SMTP authentication.</param> | ||
| /// <param name="allowInsecure"> | ||
| /// When <see langword="true"/>, the MP_SMTP_AUTH_ALLOW_INSECURE config is set to true to allow insecure PLAIN and LOGIN SMTP authentication. | ||
| /// When <see langword="false"/>, a self-signed certificate is used. Its subject and issuer are <c>CN=localhost, O=Mailpit self-signed certificate</c>. | ||
| /// </param> | ||
| /// <returns>A configured instance of <see cref="MailpitBuilder" />.</returns> | ||
| public MailpitBuilder WithSmtpAuthCredentials(NetworkCredential credentials, bool allowInsecure) | ||
| { | ||
| if (credentials == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(credentials)); | ||
| } | ||
|
|
||
| if (credentials.UserName.Contains(":")) | ||
| { | ||
| throw new ArgumentException("The UserName cannot contain a colon (:) character.", nameof(credentials)); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| // https://mailpit.axllent.org/docs/configuration/smtp/#adding-smtp-authentication | ||
| var builder = Merge(DockerResourceConfiguration, new MailpitConfiguration(smtpAuthCredentials: credentials, smtpAuthAllowInsecure: allowInsecure)) | ||
| .WithEnvironment("MP_SMTP_AUTH", $"{credentials.UserName}:{credentials.Password}"); | ||
|
|
||
| return allowInsecure | ||
| ? builder | ||
| .WithEnvironment("MP_SMTP_AUTH_ALLOW_INSECURE", "1") | ||
| : builder | ||
| // https://mailpit.axllent.org/docs/configuration/certificates/#auto-generate-self-signed-certificates | ||
| .WithEnvironment("MP_SMTP_TLS_CERT", "sans:localhost") | ||
| .WithEnvironment("MP_SMTP_TLS_KEY", "sans:localhost"); | ||
| } | ||
|
Comment on lines
+87
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Handle mutually exclusive environment variables on subsequent calls. If This could result in both 🤖 Prompt for AI Agents |
||
|
|
||
| /// <summary> | ||
| /// Sets the Mailpit MP_MAX_MESSAGES config. | ||
| /// Maximum number of messages to store. Mailpit will periodically delete the oldest messages if greater than this. Set to 0 to disable auto-deletion. | ||
| /// </summary> | ||
| /// <param name="maxMessages">The maximum number of messages to set.</param> | ||
| /// <returns>A configured instance of <see cref="MailpitBuilder" />.</returns> | ||
| public MailpitBuilder WithMaxMessages(uint maxMessages) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new MailpitConfiguration(maxMessages: maxMessages)) | ||
| .WithEnvironment("MP_MAX_MESSAGES", maxMessages.ToString()); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public override MailpitContainer Build() | ||
| { | ||
| Validate(); | ||
| return new MailpitContainer(DockerResourceConfiguration); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override MailpitBuilder Init() | ||
| { | ||
| return base.Init() | ||
| .WithPortBinding(SmtpPort, true) | ||
| .WithPortBinding(WebPort, true) | ||
| .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request => | ||
| // https://mailpit.axllent.org/docs/integration/healthcheck/ | ||
| request.ForPort(WebPort).ForPath("/readyz"))); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override MailpitBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new MailpitConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override MailpitBuilder Clone(IContainerConfiguration resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new MailpitConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override MailpitBuilder Merge(MailpitConfiguration oldValue, MailpitConfiguration newValue) | ||
| { | ||
| return new MailpitBuilder(new MailpitConfiguration(oldValue, newValue)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| namespace Testcontainers.Mailpit; | ||
|
|
||
| /// <inheritdoc cref="ContainerConfiguration" /> | ||
| [PublicAPI] | ||
| public sealed class MailpitConfiguration : ContainerConfiguration | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="smtpAuthCredentials">Username and password for SMTP authentication. The username must not contain a <c>:</c> character.</param> | ||
| /// <param name="smtpAuthAllowInsecure">Typically, STARTTLS is enforced for all SMTP authentication. This option allows insecure PLAIN & LOGIN SMTP authentication.</param> | ||
| /// <param name="maxMessages">Maximum number of messages to store. Mailpit will periodically delete the oldest messages if greater than this. Set to 0 to disable auto-deletion.</param> | ||
| public MailpitConfiguration(NetworkCredential smtpAuthCredentials = null, bool smtpAuthAllowInsecure = true, uint maxMessages = 100) | ||
| { | ||
| SmtpAuthCredentials = smtpAuthCredentials; | ||
| SmtpAuthAllowInsecure = smtpAuthAllowInsecure; | ||
| MaxMessages = maxMessages; | ||
| } | ||
|
Comment on lines
+13
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Use nullable types for configuration properties. In Testcontainers, configuration properties are merged using
📍 Affects 1 file
🤖 Prompt for AI Agents |
||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| public MailpitConfiguration(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="MailpitConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| public MailpitConfiguration(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="MailpitConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| public MailpitConfiguration(MailpitConfiguration resourceConfiguration) | ||
| : this(new MailpitConfiguration(), resourceConfiguration) | ||
| { | ||
| // Passes the configuration upwards to the base implementations to create an updated immutable copy. | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="oldValue">The old Docker resource configuration.</param> | ||
| /// <param name="newValue">The new Docker resource configuration.</param> | ||
| public MailpitConfiguration(MailpitConfiguration oldValue, MailpitConfiguration newValue) | ||
| : base(oldValue, newValue) | ||
| { | ||
| SmtpAuthCredentials = BuildConfiguration.Combine(oldValue.SmtpAuthCredentials, newValue.SmtpAuthCredentials); | ||
| SmtpAuthAllowInsecure = BuildConfiguration.Combine(oldValue.SmtpAuthAllowInsecure, newValue.SmtpAuthAllowInsecure); | ||
| MaxMessages = BuildConfiguration.Combine(oldValue.MaxMessages, newValue.MaxMessages); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Username and password for SMTP authentication. The username must not contain a <c>:</c> character. | ||
| /// </summary> | ||
| public NetworkCredential SmtpAuthCredentials { get; } | ||
|
|
||
| /// <summary> | ||
| /// Typically, STARTTLS is enforced for all SMTP authentication. This option allows insecure PLAIN & LOGIN SMTP authentication. | ||
| /// </summary> | ||
| public bool SmtpAuthAllowInsecure { get; } | ||
|
|
||
| /// <summary> | ||
| /// Maximum number of messages to store. Mailpit will periodically delete the oldest messages if greater than this. Set to 0 to disable auto-deletion. | ||
| /// </summary> | ||
| public uint MaxMessages { get; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| namespace Testcontainers.Mailpit; | ||
|
|
||
| /// <inheritdoc cref="DockerContainer" /> | ||
| [PublicAPI] | ||
| public sealed class MailpitContainer : DockerContainer | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MailpitContainer" /> class. | ||
| /// </summary> | ||
| /// <param name="configuration">The container configuration.</param> | ||
| public MailpitContainer(MailpitConfiguration configuration) | ||
| : base(configuration) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// The SMTP server port. | ||
| /// </summary> | ||
| public ushort SmtpPort => GetMappedPublicPort(MailpitBuilder.SmtpPort); | ||
|
|
||
| /// <summary> | ||
| /// Gets the web server address of the user interface. Can also be used as the base URL for the <see href="https://mailpit.axllent.org/docs/api-v1/"> REST API</see>. | ||
| /// </summary> | ||
| public string GetWebAddress() | ||
| { | ||
| return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(MailpitBuilder.WebPort)).ToString(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <TargetFrameworks>net8.0;net9.0;net10.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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| global using System; | ||
| global using System.Net; | ||
| global using Docker.DotNet.Models; | ||
| global using DotNet.Testcontainers.Builders; | ||
| global using DotNet.Testcontainers.Configurations; | ||
| global using DotNet.Testcontainers.Containers; | ||
| global using DotNet.Testcontainers.Images; | ||
| global using JetBrains.Annotations; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| root = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ubuntu-24.04 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| FROM axllent/mailpit:v1.30@sha256:5a49a77c5bdbe7c5474450b4f46348d09949df3695257729c93a30369382d4f6 |
Uh oh!
There was an error while loading. Please reload this page.