From 59991ec75e65727a58caa217144f263838ba5fb5 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 5 Sep 2023 21:59:45 -0700 Subject: [PATCH] feat: Allow MongoDb module configuration without credentials (#983) Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> --- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 46 ++++++++++++++----- .../MongoDbContainerTest.cs | 9 ++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index 3aeb1a442..ea0808826 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -41,8 +41,10 @@ private MongoDbBuilder(MongoDbConfiguration resourceConfiguration) /// A configured instance of . public MongoDbBuilder WithUsername(string username) { - return Merge(DockerResourceConfiguration, new MongoDbConfiguration(username: username)) - .WithEnvironment("MONGO_INITDB_ROOT_USERNAME", username); + var initDbRootUsername = username ?? string.Empty; + + return Merge(DockerResourceConfiguration, new MongoDbConfiguration(username: initDbRootUsername)) + .WithEnvironment("MONGO_INITDB_ROOT_USERNAME", initDbRootUsername); } /// @@ -52,15 +54,22 @@ public MongoDbBuilder WithUsername(string username) /// A configured instance of . public MongoDbBuilder WithPassword(string password) { - return Merge(DockerResourceConfiguration, new MongoDbConfiguration(password: password)) - .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", password); + var initDbRootPassword = password ?? string.Empty; + + return Merge(DockerResourceConfiguration, new MongoDbConfiguration(password: initDbRootPassword)) + .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", initDbRootPassword); } /// public override MongoDbContainer Build() { Validate(); - return new MongoDbContainer(DockerResourceConfiguration, TestcontainersSettings.Logger); + + // The wait strategy relies on the configuration of MongoDb. If credentials are + // provided, the log message "Waiting for connections" appears twice. + // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. + var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); + return new MongoDbContainer(mongoDbBuilder.DockerResourceConfiguration, TestcontainersSettings.Logger); } /// @@ -70,22 +79,24 @@ protected override MongoDbBuilder Init() .WithImage(MongoDbImage) .WithPortBinding(MongoDbPort, true) .WithUsername(DefaultUsername) - .WithPassword(DefaultPassword) - .WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil())); + .WithPassword(DefaultPassword); } /// protected override void Validate() { + const string message = "Missing username or password. Both must be specified for a user to be created."; + base.Validate(); _ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username)) - .NotNull() - .NotEmpty(); + .NotNull(); _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password)) - .NotNull() - .NotEmpty(); + .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)); } /// @@ -111,13 +122,24 @@ private sealed class WaitUntil : IWaitUntil { private static readonly string[] LineEndings = { "\r\n", "\n" }; + private readonly int _count; + + /// + /// Initializes a new instance of the class. + /// + /// The container configuration. + public WaitUntil(MongoDbConfiguration configuration) + { + _count = string.IsNullOrEmpty(configuration.Username) && string.IsNullOrEmpty(configuration.Password) ? 1 : 2; + } + /// public async Task UntilAsync(IContainer container) { var (stdout, stderr) = await container.GetLogsAsync(timestampsEnabled: false) .ConfigureAwait(false); - return 2.Equals(Array.Empty() + return _count.Equals(Array.Empty() .Concat(stdout.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) .Concat(stderr.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) .Count(line => line.Contains("Waiting for connections"))); diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs index 7948d7e24..38728c372 100644 --- a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs @@ -57,6 +57,15 @@ public MongoDbDefaultConfiguration() } } + [UsedImplicitly] + public sealed class MongoDbNoAuthConfiguration : MongoDbContainerTest + { + public MongoDbNoAuthConfiguration() + : base(new MongoDbBuilder().WithUsername(string.Empty).WithPassword(string.Empty).Build()) + { + } + } + [UsedImplicitly] public sealed class MongoDbV5Configuration : MongoDbContainerTest {