Skip to content

Commit

Permalink
feat: Add NATS module (#1003)
Browse files Browse the repository at this point in the history
Co-authored-by: Andre Hofmeister <[email protected]>
  • Loading branch information
niklasfp and HofmeisterAn authored Sep 19, 2023
1 parent 0c6588e commit 560b64f
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 1 deletion.
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MsSql", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql", "src\Testcontainers.MySql\Testcontainers.MySql.csproj", "{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Nats", "src\Testcontainers.Nats\Testcontainers.Nats.csproj", "{BF37BEA1-0816-4326-B1E0-E82290F8FCE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Neo4j", "src\Testcontainers.Neo4j\Testcontainers.Neo4j.csproj", "{ADC2372B-6FE0-421D-8277-BB628E8EFC22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle", "src\Testcontainers.Oracle\Testcontainers.Oracle.csproj", "{596EAFC1-0496-495C-B382-D57415FA456A}"
Expand Down Expand Up @@ -115,6 +117,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MsSql.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql.Tests", "tests\Testcontainers.MySql.Tests\Testcontainers.MySql.Tests.csproj", "{E42DA1CE-698F-4E45-8D1F-5D5895893840}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Nats.Tests", "tests\Testcontainers.Nats.Tests\Testcontainers.Nats.Tests.csproj", "{87A3F137-6DC3-4CE5-91E6-01797D076086}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Neo4j.Tests", "tests\Testcontainers.Neo4j.Tests\Testcontainers.Neo4j.Tests.csproj", "{D3F63405-C0FA-4F83-8B79-E30BFF5FF5BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle.Tests", "tests\Testcontainers.Oracle.Tests\Testcontainers.Oracle.Tests.csproj", "{4AC1088B-9965-4497-AC8E-570F1AD5631F}"
Expand Down Expand Up @@ -230,6 +234,10 @@ Global
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC}.Release|Any CPU.Build.0 = Release|Any CPU
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0}.Release|Any CPU.Build.0 = Release|Any CPU
{ADC2372B-6FE0-421D-8277-BB628E8EFC22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADC2372B-6FE0-421D-8277-BB628E8EFC22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADC2372B-6FE0-421D-8277-BB628E8EFC22}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -354,6 +362,10 @@ Global
{E42DA1CE-698F-4E45-8D1F-5D5895893840}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E42DA1CE-698F-4E45-8D1F-5D5895893840}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E42DA1CE-698F-4E45-8D1F-5D5895893840}.Release|Any CPU.Build.0 = Release|Any CPU
{87A3F137-6DC3-4CE5-91E6-01797D076086}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87A3F137-6DC3-4CE5-91E6-01797D076086}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87A3F137-6DC3-4CE5-91E6-01797D076086}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87A3F137-6DC3-4CE5-91E6-01797D076086}.Release|Any CPU.Build.0 = Release|Any CPU
{D3F63405-C0FA-4F83-8B79-E30BFF5FF5BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3F63405-C0FA-4F83-8B79-E30BFF5FF5BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3F63405-C0FA-4F83-8B79-E30BFF5FF5BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -428,6 +440,7 @@ Global
{2613F146-6C66-4059-9D37-D48BA6B61515} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{121FB123-40D9-44D4-9AB7-AD57ED34F466} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{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}
{8AB91636-9055-4900-A72A-7CFFACDFDBF0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -459,6 +472,7 @@ Global
{82A7E7B8-3187-4CAE-845B-0BF43409B38A} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{E42DA1CE-698F-4E45-8D1F-5D5895893840} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{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}
{DA1D7ADE-452C-4369-83CC-56289176EACD} = {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 @@ -41,6 +41,7 @@ await moduleNameContainer.StartAsync();
| MinIO | `minio/minio:RELEASE.2023-01-31T02-24-19Z` | [NuGet](https://www.nuget.org/packages/Testcontainers.Minio) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Minio) |
| MongoDB | `mongo:6.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MongoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MongoDb) |
| MySQL | `mysql:8.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MySql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MySql) |
| 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) |
| PostgreSQL | `postgres:15.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.PostgreSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PostgreSql) |
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MongoDb/MongoDbBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected override void Validate()
.NotNull();

_ = Guard.Argument(DockerResourceConfiguration, "Credentials")
.ThrowIf(argument => 1.Equals(new[] { argument.Value.Username, argument.Value.Password }.Count(string.IsNullOrEmpty)), argument => new ArgumentException(message, argument.Name));
.ThrowIf(argument => 1.Equals(new[] { argument.Value.Username, argument.Value.Password }.Count(string.IsNullOrWhiteSpace)), argument => new ArgumentException(message, argument.Name));
}

/// <inheritdoc />
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.Nats/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
117 changes: 117 additions & 0 deletions src/Testcontainers.Nats/NatsBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
namespace Testcontainers.Nats;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class NatsBuilder : ContainerBuilder<NatsBuilder, NatsContainer, NatsConfiguration>
{
public const string NatsImage = "nats:2.9";

public const ushort NatsClientPort = 4222;

public const ushort NatsClusterRoutingPort = 6222;

public const ushort NatsHttpManagementPort = 8222;

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

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

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

/// <summary>
/// Sets the Nats username.
/// </summary>
/// <param name="username">The Nats username.</param>
/// <returns>A configured instance of <see cref="NatsBuilder" />.</returns>
public NatsBuilder WithUsername(string username)
{
return Merge(DockerResourceConfiguration, new NatsConfiguration(username: username))
.WithCommand("--user", username);
}

/// <summary>
/// Sets the Nats password.
/// </summary>
/// <param name="password">The Nats password.</param>
/// <returns>A configured instance of <see cref="NatsBuilder" />.</returns>
public NatsBuilder WithPassword(string password)
{
return Merge(DockerResourceConfiguration, new NatsConfiguration(password: password))
.WithCommand("--pass", password);
}

/// <inheritdoc />
public override NatsContainer Build()
{
Validate();
return new NatsContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
protected override NatsBuilder Init()
{
return base.Init()
.WithImage(NatsImage)
.WithPortBinding(NatsClientPort, true)
.WithPortBinding(NatsHttpManagementPort, true)
.WithPortBinding(NatsClusterRoutingPort, true)
.WithUsername(string.Empty)
.WithPassword(string.Empty)
.WithCommand("--http_port", NatsHttpManagementPort.ToString())
.WithCommand("--jetstream")
.WithCommand("--debug")
.WithCommand("--trace")
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Server is ready"));
}

/// <inheritdoc />
protected override void Validate()
{
const string message = "Missing username or password. Both must be specified.";

base.Validate();

_ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username))
.NotNull();

_ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password))
.NotNull();

_ = Guard.Argument(DockerResourceConfiguration, "Credentials")
.ThrowIf(argument => 1.Equals(new[] { argument.Value.Username, argument.Value.Password }.Count(string.IsNullOrWhiteSpace)), argument => new ArgumentException(message, argument.Name));
}

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

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

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

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class NatsConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="NatsConfiguration" /> class.
/// </summary>
/// <param name="username">The Nats username.</param>
/// <param name="password">The Nats password.</param>
public NatsConfiguration(
string username = null,
string password = null)
{
Username = username;
Password = password;
}

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

/// <summary>
/// Initializes a new instance of the <see cref="NatsConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public NatsConfiguration(NatsConfiguration oldValue, NatsConfiguration newValue)
: base(oldValue, newValue)
{
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
}

/// <summary>
/// The Nats username.
/// </summary>
public string Username { get; }

/// <summary>
/// The Nats password.
/// </summary>
public string Password { get; }
}
43 changes: 43 additions & 0 deletions src/Testcontainers.Nats/NatsContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace Testcontainers.Nats;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class NatsContainer : DockerContainer
{
private readonly NatsConfiguration _configuration;

/// <summary>
/// Initializes a new instance of the <see cref="NatsContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public NatsContainer(NatsConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
_configuration = configuration;
}

/// <summary>
/// Gets the Nats connection string.
/// </summary>
/// <remarks>
/// If both username and password are set in the builder configuration, they will be included in the connection string.
/// </remarks>
/// <returns>A Nats connection string in the format: <c>nats://hostname:port</c>.</returns>
public string GetConnectionString()
{
var endpoint = new UriBuilder("nats://", Hostname, GetMappedPublicPort(NatsBuilder.NatsClientPort));
endpoint.UserName = Uri.EscapeDataString(_configuration.Username);
endpoint.Password = Uri.EscapeDataString(_configuration.Password);
return endpoint.ToString();
}

/// <summary>
/// Gets the Nats monitoring endpoint.
/// </summary>
/// <returns>An HTTP address in the format: <c>http://hostname:port</c>.</returns>
public string GetManagementEndpoint()
{
return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(NatsBuilder.NatsHttpManagementPort)).ToString();
}
}
13 changes: 13 additions & 0 deletions src/Testcontainers.Nats/Testcontainers.Nats.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions src/Testcontainers.Nats/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global using System;
global using System.Linq;
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;
global using Microsoft.Extensions.Logging;
1 change: 1 addition & 0 deletions tests/Testcontainers.Nats.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
Loading

0 comments on commit 560b64f

Please sign in to comment.