Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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 .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ jobs:
{ name: "Testcontainers.Redpanda", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.ServiceBus", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Sftp", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Smtp4Dev", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Weaviate", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.WebDriver", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Xunit", runs-on: "ubuntu-22.04" }
Expand Down
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Smtp4Dev", "src\Testcontainers.Smtp4Dev\Testcontainers.Smtp4Dev.csproj", "{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Weaviate", "src\Testcontainers.Weaviate\Testcontainers.Weaviate.csproj", "{68F8600D-24E9-4E03-9E25-5F6EB338EAC1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver", "src\Testcontainers.WebDriver\Testcontainers.WebDriver.csproj", "{64A87DE5-29B0-4A54-9E74-560484D8C7C0}"
Expand Down Expand Up @@ -225,6 +227,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp.Tests", "tests\Testcontainers.Sftp.Tests\Testcontainers.Sftp.Tests.csproj", "{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Smtp4Dev.Tests", "tests\Testcontainers.Smtp4Dev.Tests\Testcontainers.Smtp4Dev.Tests.csproj", "{F7387519-8EB0-4B87-B817-A09CA8CE369A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tests\Testcontainers.Tests\Testcontainers.Tests.csproj", "{27CDB869-A150-4593-958F-6F26E5391E7C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Weaviate.Tests", "tests\Testcontainers.Weaviate.Tests\Testcontainers.Weaviate.Tests.csproj", "{DDB41BC8-5826-4D97-9C5F-001151E3FFD6}"
Expand Down Expand Up @@ -682,6 +686,14 @@ Global
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.Build.0 = Release|Any CPU
{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70}.Release|Any CPU.Build.0 = Release|Any CPU
{F7387519-8EB0-4B87-B817-A09CA8CE369A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7387519-8EB0-4B87-B817-A09CA8CE369A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7387519-8EB0-4B87-B817-A09CA8CE369A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7387519-8EB0-4B87-B817-A09CA8CE369A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5365F780-0E6C-41F0-B1B9-7DC34368F80C} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -794,5 +806,7 @@ Global
{DDB41BC8-5826-4D97-9C5F-001151E3FFD6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{DA635A41-3448-4DF3-8A1E-D3CF9C7F4B70} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{F7387519-8EB0-4B87-B817-A09CA8CE369A} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions src/Testcontainers.Smtp4Dev/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
160 changes: 160 additions & 0 deletions src/Testcontainers.Smtp4Dev/Smtp4DevBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using TestContainers.Smtp4Dev;

namespace Testcontainers.Smtp4Dev;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class Smtp4DevBuilder : ContainerBuilder<Smtp4DevBuilder, Smtp4DevContainer, Smtp4DevConfiguration>
{
public const string Smtp4DevImage = "rnwood/smtp4dev:latest";

public const ushort WebInterfacePort = 80;
public const ushort SmtpPort = 25;
public const ushort ImapPort = 143;

public const bool DefaultLockSettings = false;
public const string DefaultBasePath = "/";
public const string DefaultDatabase = "database.db";
public const ushort DefaultNumberOfMessagesToKeep = 100;
public const ushort DefaultNumberOfSessionsToKeep = 100;
public const bool DefaultDisableMessageSanitisation = false;

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

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

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

/// <summary>
/// Sets whether settings can be changed by user via web interface.
/// </summary>
/// <param name="lockSettings">Locks settings form being changed by user via web interface.</param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithLockSettings(bool lockSettings)
{
return Merge(DockerResourceConfiguration, new Smtp4DevConfiguration(lockSettings: lockSettings))
.WithEnvironment("LockSettings", lockSettings.ToString());
}

/// <summary>
/// Sets the virtual path from web server root where SMTP4DEV web interface will be hosted. e.g. "/" or
/// "/smtp4dev".
/// </summary>
/// <param name="basePath">Locks settings form being changed by user via web interface.</param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithBasePath(string basePath)
{
return Merge(DockerResourceConfiguration, new Smtp4DevConfiguration(basePath: basePath))
.WithEnvironment("BasePath", basePath);
}

/// <summary>
/// Sets the path where the database will be stored relative to APPDATA env var on Windows or XDG_CONFIG_HOME
/// on non-Windows. Specify "" to use an in memory database.
/// </summary>
/// <param name="database">
/// The path where the database will be stored relative to APPDATA env var on Windows or XDG_CONFIG_HOME
/// on non-Windows. Specify "" to use an in memory database.
/// </param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithDatabase(string database)
{
return Merge(DockerResourceConfiguration, new Smtp4DevConfiguration(database: database))
.WithEnvironment("Latabase", database);
}

/// <summary>
/// Sets the number of messages to keep per mailbox.
/// </summary>
/// <param name="numberOfMessagesToKeep">The number of messages to keep per mailbox.</param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithNumberOfMessagesToKeep(int numberOfMessagesToKeep)
{
return Merge(DockerResourceConfiguration,
new Smtp4DevConfiguration(numberOfMessagesToKeep: numberOfMessagesToKeep))
.WithEnvironment("NumberOfMessagesToKeep", numberOfMessagesToKeep.ToString());
}

/// <summary>
/// Sets the number of sessions to keep.
/// </summary>
/// <param name="numberOfSessionsToKeep">The number of sessions to keep.</param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithNumberOfSessionsToKeep(int numberOfSessionsToKeep)
{
return Merge(DockerResourceConfiguration,
new Smtp4DevConfiguration(numberOfSessionsToKeep: numberOfSessionsToKeep))
.WithEnvironment("NumberOfSessionsToKeep", numberOfSessionsToKeep.ToString());
}

/// <summary>
/// Sets whether message HTML sanitisation should be enabled. Dangerous if your messages are not generated by you
/// and not reflective of how messages might render in most email clients.
/// </summary>
/// <param name="disableMessageSanitisations">
/// Whether message HTML sanitisation should be enabled. Dangerous if your messages are not generated by you
/// and not reflective of how messages might render in most email clients.
/// </param>
/// <returns>A configured instance of <see cref="Smtp4DevBuilder" />.</returns>
public Smtp4DevBuilder WithDisableMessageSanitisations(bool disableMessageSanitisations)
{
return Merge(DockerResourceConfiguration,
new Smtp4DevConfiguration(disableMessageSanitisations: disableMessageSanitisations))
.WithEnvironment("DisableMessageSanitisations", disableMessageSanitisations.ToString());
}

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

/// <inheritdoc />
protected override Smtp4DevBuilder Init()
{
return base.Init()
.WithImage(Smtp4DevImage)
.WithPortBinding(SmtpPort, true)
.WithPortBinding(ImapPort, true)
.WithPortBinding(WebInterfacePort, true)
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilMessageIsLogged("Now listening on: .+")
.UntilMessageIsLogged("SMTP Server is listening on port")
.UntilMessageIsLogged("IMAP Server is listening on port") );
}

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

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

/// <inheritdoc />
protected override Smtp4DevBuilder Merge(Smtp4DevConfiguration oldValue, Smtp4DevConfiguration newValue)
{
return new Smtp4DevBuilder(new Smtp4DevConfiguration(oldValue, newValue));
}
}
118 changes: 118 additions & 0 deletions src/Testcontainers.Smtp4Dev/Smtp4DevConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
namespace TestContainers.Smtp4Dev;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public class Smtp4DevConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="Smtp4DevConfiguration" /> class.
/// </summary>
/// <param name="lockSettings">Locks settings form being changed by user via web interface.</param>
/// <param name="basePath">
/// Specifies the virtual path from web server root where SMTP4DEV web interface will be hosted. e.g. "/" or
/// "/smtp4dev".
/// </param>
/// <param name="database">
/// Specifies the path where the database will be stored relative to APPDATA env var on Windows or XDG_CONFIG_HOME
/// on non-Windows. Specify "" to use an in memory database.
/// </param>
/// <param name="numberOfMessagesToKeep">Specifies the number of messages to keep per mailbox.</param>
/// <param name="numberOfSessionsToKeep">Specifies the number of sessions to keep.</param>
/// <param name="disableMessageSanitisations">
/// Disables message HTML sanitisation. Dangerous if your messages are not generated by you and not reflective of
/// how messages might render in most email clients.
/// </param>
public Smtp4DevConfiguration(
bool? lockSettings = null,
string basePath = null,
string database = null,
int? numberOfMessagesToKeep = null,
int? numberOfSessionsToKeep = null,
bool? disableMessageSanitisations = null)
{
LockSettings = lockSettings;
BasePath = basePath;
Database = database;
NumberOfMessagesToKeep = numberOfMessagesToKeep;
NumberOfSessionsToKeep = numberOfSessionsToKeep;
DisableMessageSanitisations = disableMessageSanitisations;
}

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

/// <summary>
/// Initializes a new instance of the <see cref="Smtp4DevConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public Smtp4DevConfiguration(Smtp4DevConfiguration oldValue, Smtp4DevConfiguration newValue)
: base(oldValue, newValue)
{
LockSettings = BuildConfiguration.Combine(oldValue.LockSettings, newValue.LockSettings);
BasePath = BuildConfiguration.Combine(oldValue.BasePath, newValue.BasePath);
Database = BuildConfiguration.Combine(oldValue.Database, newValue.Database);
NumberOfMessagesToKeep = BuildConfiguration.Combine(oldValue.NumberOfMessagesToKeep, newValue.NumberOfMessagesToKeep);
NumberOfSessionsToKeep = BuildConfiguration.Combine(oldValue.NumberOfSessionsToKeep, newValue.NumberOfSessionsToKeep);
DisableMessageSanitisations = BuildConfiguration.Combine(oldValue.DisableMessageSanitisations, newValue.DisableMessageSanitisations);
}

/// <summary>
/// Gets whether settings can be changed by user via web interface.
/// </summary>
public bool? LockSettings { get; }

/// <summary>
/// Gets the virtual path from web server root where SMTP4DEV web interface will be hosted. e.g. "/" or
/// "/smtp4dev".
/// </summary>
public string BasePath { get; }

/// <summary>
/// Gets the path where the database will be stored relative to APPDATA env var on Windows or XDG_CONFIG_HOME
/// on non-Windows. Specify "" to use an in memory database.
/// </summary>
public string Database { get; }

/// <summary>
/// Gets the number of messages to keep per mailbox.
/// </summary>
public int? NumberOfMessagesToKeep { get; }

/// <summary>
/// Gets the number of sessions to keep.
/// </summary>
public int? NumberOfSessionsToKeep { get; }

/// <summary>
/// Gets whether message HTML sanitisation is disabled.
/// </summary>
public bool? DisableMessageSanitisations { get; }
}
15 changes: 15 additions & 0 deletions src/Testcontainers.Smtp4Dev/Smtp4DevContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TestContainers.Smtp4Dev;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class Smtp4DevContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="Smtp4DevContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public Smtp4DevContainer(Smtp4DevConfiguration configuration)
: base(configuration)
{
}
}
13 changes: 13 additions & 0 deletions src/Testcontainers.Smtp4Dev/Testcontainers.Smtp4Dev.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<RootNamespace>TestContainers.Smtp4Dev</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions src/Testcontainers.Smtp4Dev/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
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.Smtp4Dev.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
Loading
Loading