diff --git a/src/Testcontainers.Db2/Db2Builder.cs b/src/Testcontainers.Db2/Db2Builder.cs index 3f1b9ccf1..4731c9005 100644 --- a/src/Testcontainers.Db2/Db2Builder.cs +++ b/src/Testcontainers.Db2/Db2Builder.cs @@ -14,12 +14,6 @@ public sealed class Db2Builder : ContainerBuilder /// Initializes a new instance of the class. /// @@ -42,6 +36,15 @@ private Db2Builder(Db2Configuration resourceConfiguration) /// protected override Db2Configuration DockerResourceConfiguration { get; } + /// + protected override string AcceptLicenseAgreementEnvVar { get; } = "LICENSE"; + + /// + protected override string AcceptLicenseAgreement { get; } = "accept"; + + /// + protected override string DeclineLicenseAgreement { get; } = "decline"; + /// /// Accepts the license agreement. /// @@ -50,7 +53,7 @@ private Db2Builder(Db2Configuration resourceConfiguration) /// /// A boolean value indicating whether the Db2 license agreement is accepted. /// A configured instance of . - public Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement) + public override Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement) { var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement; return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement); @@ -94,6 +97,7 @@ public Db2Builder WithPassword(string password) public override Db2Container Build() { Validate(); + ValidateLicenseAgreement(); return new Db2Container(DockerResourceConfiguration); } @@ -110,16 +114,8 @@ protected override Db2Builder Init() => base.Init() /// protected override void Validate() { - const string message = "The image '{0}' requires you to accept a license agreement."; - base.Validate(); - Predicate licenseAgreementNotAccepted = value => - !value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal); - - _ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image)) - .ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name)); - _ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username)) .NotNull() .NotEmpty(); diff --git a/src/Testcontainers.Neo4j/Neo4jBuilder.cs b/src/Testcontainers.Neo4j/Neo4jBuilder.cs index 0dc610619..399076936 100644 --- a/src/Testcontainers.Neo4j/Neo4jBuilder.cs +++ b/src/Testcontainers.Neo4j/Neo4jBuilder.cs @@ -10,12 +10,6 @@ public sealed class Neo4jBuilder : ContainerBuilder /// Initializes a new instance of the class. /// @@ -38,6 +32,15 @@ private Neo4jBuilder(Neo4jConfiguration resourceConfiguration) /// protected override Neo4jConfiguration DockerResourceConfiguration { get; } + /// + protected override string AcceptLicenseAgreementEnvVar { get; } = "NEO4J_ACCEPT_LICENSE_AGREEMENT"; + + /// + protected override string AcceptLicenseAgreement { get; } = "yes"; + + /// + protected override string DeclineLicenseAgreement { get; } = "no"; + /// /// Sets the image to the Neo4j Enterprise Edition. /// diff --git a/src/Testcontainers.Pulsar/Usings.cs b/src/Testcontainers.Pulsar/Usings.cs index 59c490a37..cbed90f27 100644 --- a/src/Testcontainers.Pulsar/Usings.cs +++ b/src/Testcontainers.Pulsar/Usings.cs @@ -9,7 +9,6 @@ global using System.Threading; global using System.Threading.Tasks; global using Docker.DotNet.Models; -global using DotNet.Testcontainers; global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Configurations; global using DotNet.Testcontainers.Containers; diff --git a/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs b/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs index 278a85cf7..7a15b11e2 100644 --- a/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs +++ b/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs @@ -12,12 +12,6 @@ public sealed class ServiceBusBuilder : ContainerBuilder /// Initializes a new instance of the class. /// @@ -40,6 +34,15 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration) /// protected override ServiceBusConfiguration DockerResourceConfiguration { get; } + /// + protected override string AcceptLicenseAgreementEnvVar { get; } = "ACCEPT_EULA"; + + /// + protected override string AcceptLicenseAgreement { get; } = "Y"; + + /// + protected override string DeclineLicenseAgreement { get; } = "N"; + /// /// Accepts the license agreement. /// @@ -48,7 +51,7 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration) /// /// A boolean value indicating whether the Azure Service Bus Emulator license agreement is accepted. /// A configured instance of . - public ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement) + public override ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement) { var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement; return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement); @@ -85,6 +88,7 @@ public ServiceBusBuilder WithMsSqlContainer( public override ServiceBusContainer Build() { Validate(); + ValidateLicenseAgreement(); if (DockerResourceConfiguration.DatabaseContainer != null) { @@ -105,20 +109,6 @@ public override ServiceBusContainer Build() return new ServiceBusContainer(serviceBusContainer.DockerResourceConfiguration); } - /// - protected override void Validate() - { - const string message = "The image '{0}' requires you to accept a license agreement."; - - base.Validate(); - - Predicate licenseAgreementNotAccepted = value => - !value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal); - - _ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image)) - .ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name)); - } - /// protected override ServiceBusBuilder Init() { diff --git a/src/Testcontainers.ServiceBus/Usings.cs b/src/Testcontainers.ServiceBus/Usings.cs index ea7628ae0..15c8b2eda 100644 --- a/src/Testcontainers.ServiceBus/Usings.cs +++ b/src/Testcontainers.ServiceBus/Usings.cs @@ -4,7 +4,6 @@ global using System.Threading; global using System.Threading.Tasks; global using Docker.DotNet.Models; -global using DotNet.Testcontainers; global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Configurations; global using DotNet.Testcontainers.Containers; diff --git a/src/Testcontainers/Builders/Base64Provider.cs b/src/Testcontainers/Builders/Base64Provider.cs index 29ed7b142..de0671e57 100644 --- a/src/Testcontainers/Builders/Base64Provider.cs +++ b/src/Testcontainers/Builders/Base64Provider.cs @@ -39,7 +39,7 @@ public Base64Provider(JsonElement jsonElement, ILogger logger) } /// - /// Gets a predicate that determines whether or not a contains a Docker registry key. + /// Gets a predicate that determines whether a contains a Docker registry key. /// public static Func HasDockerRegistryKey { get; } = (property, hostname) => property.Name.Equals(hostname, StringComparison.OrdinalIgnoreCase) || property.Name.EndsWith("://" + hostname, StringComparison.OrdinalIgnoreCase); @@ -47,7 +47,7 @@ public Base64Provider(JsonElement jsonElement, ILogger logger) /// public bool IsApplicable(string hostname) { - return !default(JsonElement).Equals(_rootElement) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryKey(property, hostname)); + return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryKey(property, hostname)); } /// diff --git a/src/Testcontainers/Builders/ContainerBuilder`3.cs b/src/Testcontainers/Builders/ContainerBuilder`3.cs index 5bc8509eb..8bf63d77a 100644 --- a/src/Testcontainers/Builders/ContainerBuilder`3.cs +++ b/src/Testcontainers/Builders/ContainerBuilder`3.cs @@ -36,6 +36,28 @@ protected ContainerBuilder(TConfigurationEntity dockerResourceConfiguration) { } + /// + /// Gets the name of the environment variable that must be set to accept the image license agreement. + /// + protected virtual string AcceptLicenseAgreementEnvVar { get; } + + /// + /// Gets the expected value of that indicates acceptance of the license agreement. + /// + protected virtual string AcceptLicenseAgreement { get; } + + /// + /// Gets the expected value of that indicates rejection of the license agreement. + /// + protected virtual string DeclineLicenseAgreement { get; } + + /// + public virtual TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement) + { + const string licenseAgreementNotRequired = "The module does not require you to accept a license agreement."; + throw new InvalidOperationException(licenseAgreementNotRequired); + } + /// public TBuilderEntity DependsOn(IContainer container) { @@ -374,7 +396,7 @@ public TBuilderEntity WithStartupCallback(Func protected override TBuilderEntity Init() { - return base.Init().WithImagePullPolicy(PullPolicy.Missing).WithPortForwarding().WithOutputConsumer(Consume.DoNotConsumeStdoutAndStderr()).WithWaitStrategy(Wait.ForUnixContainer()).WithStartupCallback((_, ct) => Task.CompletedTask); + return base.Init().WithImagePullPolicy(PullPolicy.Missing).WithPortForwarding().WithOutputConsumer(Consume.DoNotConsumeStdoutAndStderr()).WithWaitStrategy(Wait.ForUnixContainer()).WithStartupCallback((_, _) => Task.CompletedTask); } /// @@ -390,6 +412,21 @@ protected override void Validate() .NotNull(); } + /// + /// Validates the license agreement. + /// + /// Thrown when the license agreement is not accepted. + protected virtual void ValidateLicenseAgreement() + { + const string message = "The image '{0}' requires you to accept a license agreement."; + + Predicate licenseAgreementNotAccepted = value => + !value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal); + + _ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image)) + .ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name)); + } + /// /// Clones the Docker resource builder configuration. /// diff --git a/src/Testcontainers/Builders/CredsHelperProvider.cs b/src/Testcontainers/Builders/CredsHelperProvider.cs index 36eb10f14..4e2ab9387 100644 --- a/src/Testcontainers/Builders/CredsHelperProvider.cs +++ b/src/Testcontainers/Builders/CredsHelperProvider.cs @@ -39,7 +39,7 @@ public CredsHelperProvider(JsonElement jsonElement, ILogger logger) /// public bool IsApplicable(string hostname) { - return !default(JsonElement).Equals(_rootElement) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryKey(property, hostname)); + return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryKey(property, hostname)); } /// diff --git a/src/Testcontainers/Builders/CredsStoreProvider.cs b/src/Testcontainers/Builders/CredsStoreProvider.cs index 75617d896..22fac5595 100644 --- a/src/Testcontainers/Builders/CredsStoreProvider.cs +++ b/src/Testcontainers/Builders/CredsStoreProvider.cs @@ -38,7 +38,7 @@ public CredsStoreProvider(JsonElement jsonElement, ILogger logger) /// public bool IsApplicable(string hostname) { - return !default(JsonElement).Equals(_rootElement) && !string.IsNullOrEmpty(_rootElement.GetString()); + return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !string.IsNullOrEmpty(_rootElement.GetString()); } /// diff --git a/src/Testcontainers/Builders/IContainerBuilder`2.cs b/src/Testcontainers/Builders/IContainerBuilder`2.cs index 26477db49..0493fcbe5 100644 --- a/src/Testcontainers/Builders/IContainerBuilder`2.cs +++ b/src/Testcontainers/Builders/IContainerBuilder`2.cs @@ -21,6 +21,18 @@ namespace DotNet.Testcontainers.Builders [PublicAPI] public interface IContainerBuilder : IAbstractBuilder { + /// + /// Accepts the license agreement. + /// + /// + /// Modules that require a license agreement must override and implement this + /// method to enforce proper license acceptance behavior. + /// + /// A boolean value indicating whether the license agreement is accepted. + /// A configured instance of . + /// Thrown when the module does not require a license agreement. + TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement); + /// /// Sets the dependent container to resolve and start before starting this container configuration. /// diff --git a/tests/Testcontainers.Db2.Tests/DeclineLicenseAgreementTest.cs b/tests/Testcontainers.Db2.Tests/DeclineLicenseAgreementTest.cs new file mode 100644 index 000000000..faffd313a --- /dev/null +++ b/tests/Testcontainers.Db2.Tests/DeclineLicenseAgreementTest.cs @@ -0,0 +1,21 @@ +namespace Testcontainers.Db2; + +public sealed partial class DeclineLicenseAgreementTest +{ + [GeneratedRegex("The image '.+' requires you to accept a license agreement\\.")] + private static partial Regex LicenseAgreementNotAccepted(); + + [Fact] + public void WithoutAcceptingLicenseAgreementThrowsArgumentException() + { + var exception = Assert.Throws(() => new Db2Builder().Build()); + Assert.Matches(LicenseAgreementNotAccepted(), exception.Message); + } + + [Fact] + public void WithLicenseAgreementDeclinedThrowsArgumentException() + { + var exception = Assert.Throws(() => new Db2Builder().WithAcceptLicenseAgreement(false).Build()); + Assert.Matches(LicenseAgreementNotAccepted(), exception.Message); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.Db2.Tests/Usings.cs b/tests/Testcontainers.Db2.Tests/Usings.cs index dbf73bb31..2a9a1fa23 100644 --- a/tests/Testcontainers.Db2.Tests/Usings.cs +++ b/tests/Testcontainers.Db2.Tests/Usings.cs @@ -1,4 +1,6 @@ +global using System; global using System.Data; +global using System.Text.RegularExpressions; global using System.Data.Common; global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; diff --git a/tests/Testcontainers.Kafka.Tests/Usings.cs b/tests/Testcontainers.Kafka.Tests/Usings.cs index 3e69bfb4f..6a6c42891 100644 --- a/tests/Testcontainers.Kafka.Tests/Usings.cs +++ b/tests/Testcontainers.Kafka.Tests/Usings.cs @@ -1,8 +1,5 @@ global using System; -global using System.Collections.Generic; -global using System.Diagnostics; global using System.Text; -global using System.Threading; global using System.Threading.Tasks; global using Confluent.Kafka; global using Confluent.Kafka.SyncOverAsync; diff --git a/tests/Testcontainers.Platform.Linux.Tests/AcceptLicenseAgreementTest.cs b/tests/Testcontainers.Platform.Linux.Tests/AcceptLicenseAgreementTest.cs new file mode 100644 index 000000000..d04bde64c --- /dev/null +++ b/tests/Testcontainers.Platform.Linux.Tests/AcceptLicenseAgreementTest.cs @@ -0,0 +1,11 @@ +namespace Testcontainers.Tests; + +public sealed class AcceptLicenseAgreementTest +{ + [Fact] + public void WithLicenseAgreementAcceptedThrowsArgumentException() + { + var exception = Assert.Throws(() => new ContainerBuilder().WithAcceptLicenseAgreement(true).Build()); + Assert.Equal("The module does not require you to accept a license agreement.", exception.Message); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.ServiceBus.Tests/DeclineLicenseAgreementTest.cs b/tests/Testcontainers.ServiceBus.Tests/DeclineLicenseAgreementTest.cs new file mode 100644 index 000000000..f3f693d2f --- /dev/null +++ b/tests/Testcontainers.ServiceBus.Tests/DeclineLicenseAgreementTest.cs @@ -0,0 +1,21 @@ +namespace Testcontainers.ServiceBus; + +public sealed partial class DeclineLicenseAgreementTest +{ + [GeneratedRegex("The image '.+' requires you to accept a license agreement\\.")] + private static partial Regex LicenseAgreementNotAccepted(); + + [Fact] + public void WithoutAcceptingLicenseAgreementThrowsArgumentException() + { + var exception = Assert.Throws(() => new ServiceBusBuilder().Build()); + Assert.Matches(LicenseAgreementNotAccepted(), exception.Message); + } + + [Fact] + public void WithLicenseAgreementDeclinedThrowsArgumentException() + { + var exception = Assert.Throws(() => new ServiceBusBuilder().WithAcceptLicenseAgreement(false).Build()); + Assert.Matches(LicenseAgreementNotAccepted(), exception.Message); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.ServiceBus.Tests/Usings.cs b/tests/Testcontainers.ServiceBus.Tests/Usings.cs index 1e0024b2c..8fb60a0cc 100644 --- a/tests/Testcontainers.ServiceBus.Tests/Usings.cs +++ b/tests/Testcontainers.ServiceBus.Tests/Usings.cs @@ -1,3 +1,5 @@ +global using System; +global using System.Text.RegularExpressions; global using System.Threading.Tasks; global using Azure.Messaging.ServiceBus; global using DotNet.Testcontainers.Builders;