Skip to content

Commit

Permalink
chore: Add WithAcceptLicenseAgreement(bool) to container builder (#1370)
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn authored Feb 14, 2025
1 parent 7715969 commit fa65855
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 52 deletions.
26 changes: 11 additions & 15 deletions src/Testcontainers.Db2/Db2Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ public sealed class Db2Builder : ContainerBuilder<Db2Builder, Db2Container, Db2C

public const string DefaultPassword = "db2inst1";

private const string AcceptLicenseAgreementEnvVar = "LICENSE";

private const string AcceptLicenseAgreement = "accept";

private const string DeclineLicenseAgreement = "decline";

/// <summary>
/// Initializes a new instance of the <see cref="Db2Builder" /> class.
/// </summary>
Expand All @@ -42,6 +36,15 @@ private Db2Builder(Db2Configuration resourceConfiguration)
/// <inheritdoc />
protected override Db2Configuration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "LICENSE";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "accept";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "decline";

/// <summary>
/// Accepts the license agreement.
/// </summary>
Expand All @@ -50,7 +53,7 @@ private Db2Builder(Db2Configuration resourceConfiguration)
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the Db2 license agreement is accepted.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
public override Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement;
return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement);
Expand Down Expand Up @@ -94,6 +97,7 @@ public Db2Builder WithPassword(string password)
public override Db2Container Build()
{
Validate();
ValidateLicenseAgreement();
return new Db2Container(DockerResourceConfiguration);
}

Expand All @@ -110,16 +114,8 @@ protected override Db2Builder Init() => base.Init()
/// <inheritdoc />
protected override void Validate()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

base.Validate();

Predicate<Db2Configuration> 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();
Expand Down
15 changes: 9 additions & 6 deletions src/Testcontainers.Neo4j/Neo4jBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ public sealed class Neo4jBuilder : ContainerBuilder<Neo4jBuilder, Neo4jContainer

public const ushort Neo4jBoltPort = 7687;

private const string AcceptLicenseAgreementEnvVar = "NEO4J_ACCEPT_LICENSE_AGREEMENT";

private const string AcceptLicenseAgreement = "yes";

private const string DeclineLicenseAgreement = "no";

/// <summary>
/// Initializes a new instance of the <see cref="Neo4jBuilder" /> class.
/// </summary>
Expand All @@ -38,6 +32,15 @@ private Neo4jBuilder(Neo4jConfiguration resourceConfiguration)
/// <inheritdoc />
protected override Neo4jConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "NEO4J_ACCEPT_LICENSE_AGREEMENT";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "yes";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "no";

/// <summary>
/// Sets the image to the Neo4j Enterprise Edition.
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers.Pulsar/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 11 additions & 21 deletions src/Testcontainers.ServiceBus/ServiceBusBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ public sealed class ServiceBusBuilder : ContainerBuilder<ServiceBusBuilder, Serv

public const ushort ServiceBusPort = 5672;

private const string AcceptLicenseAgreementEnvVar = "ACCEPT_EULA";

private const string AcceptLicenseAgreement = "Y";

private const string DeclineLicenseAgreement = "N";

/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusBuilder" /> class.
/// </summary>
Expand All @@ -40,6 +34,15 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration)
/// <inheritdoc />
protected override ServiceBusConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "ACCEPT_EULA";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "Y";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "N";

/// <summary>
/// Accepts the license agreement.
/// </summary>
Expand All @@ -48,7 +51,7 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration)
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the Azure Service Bus Emulator license agreement is accepted.</param>
/// <returns>A configured instance of <see cref="ServiceBusBuilder" />.</returns>
public ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
public override ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement;
return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement);
Expand Down Expand Up @@ -85,6 +88,7 @@ public ServiceBusBuilder WithMsSqlContainer(
public override ServiceBusContainer Build()
{
Validate();
ValidateLicenseAgreement();

if (DockerResourceConfiguration.DatabaseContainer != null)
{
Expand All @@ -105,20 +109,6 @@ public override ServiceBusContainer Build()
return new ServiceBusContainer(serviceBusContainer.DockerResourceConfiguration);
}

/// <inheritdoc />
protected override void Validate()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

base.Validate();

Predicate<ServiceBusConfiguration> 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));
}

/// <inheritdoc />
protected override ServiceBusBuilder Init()
{
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers.ServiceBus/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/Testcontainers/Builders/Base64Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ public Base64Provider(JsonElement jsonElement, ILogger logger)
}

/// <summary>
/// Gets a predicate that determines whether or not a <see cref="JsonProperty" /> contains a Docker registry key.
/// Gets a predicate that determines whether a <see cref="JsonProperty" /> contains a Docker registry key.
/// </summary>
public static Func<JsonProperty, string, bool> HasDockerRegistryKey { get; }
= (property, hostname) => property.Name.Equals(hostname, StringComparison.OrdinalIgnoreCase) || property.Name.EndsWith("://" + hostname, StringComparison.OrdinalIgnoreCase);

/// <inheritdoc />
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));
}

/// <inheritdoc />
Expand Down
39 changes: 38 additions & 1 deletion src/Testcontainers/Builders/ContainerBuilder`3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,28 @@ protected ContainerBuilder(TConfigurationEntity dockerResourceConfiguration)
{
}

/// <summary>
/// Gets the name of the environment variable that must be set to accept the image license agreement.
/// </summary>
protected virtual string AcceptLicenseAgreementEnvVar { get; }

/// <summary>
/// Gets the expected value of <see cref="AcceptLicenseAgreementEnvVar" /> that indicates acceptance of the license agreement.
/// </summary>
protected virtual string AcceptLicenseAgreement { get; }

/// <summary>
/// Gets the expected value of <see cref="AcceptLicenseAgreementEnvVar" /> that indicates rejection of the license agreement.
/// </summary>
protected virtual string DeclineLicenseAgreement { get; }

/// <inheritdoc />
public virtual TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
const string licenseAgreementNotRequired = "The module does not require you to accept a license agreement.";
throw new InvalidOperationException(licenseAgreementNotRequired);
}

/// <inheritdoc />
public TBuilderEntity DependsOn(IContainer container)
{
Expand Down Expand Up @@ -374,7 +396,7 @@ public TBuilderEntity WithStartupCallback(Func<TContainerEntity, CancellationTok
/// <inheritdoc />
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);
}

/// <inheritdoc />
Expand All @@ -390,6 +412,21 @@ protected override void Validate()
.NotNull();
}

/// <summary>
/// Validates the license agreement.
/// </summary>
/// <exception cref="ArgumentException">Thrown when the license agreement is not accepted.</exception>
protected virtual void ValidateLicenseAgreement()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

Predicate<TConfigurationEntity> 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));
}

/// <summary>
/// Clones the Docker resource builder configuration.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers/Builders/CredsHelperProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public CredsHelperProvider(JsonElement jsonElement, ILogger logger)
/// <inheritdoc />
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));
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers/Builders/CredsStoreProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CredsStoreProvider(JsonElement jsonElement, ILogger logger)
/// <inheritdoc />
public bool IsApplicable(string hostname)
{
return !default(JsonElement).Equals(_rootElement) && !string.IsNullOrEmpty(_rootElement.GetString());
return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !string.IsNullOrEmpty(_rootElement.GetString());
}

/// <inheritdoc />
Expand Down
12 changes: 12 additions & 0 deletions src/Testcontainers/Builders/IContainerBuilder`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ namespace DotNet.Testcontainers.Builders
[PublicAPI]
public interface IContainerBuilder<out TBuilderEntity, out TContainerEntity> : IAbstractBuilder<TBuilderEntity, TContainerEntity, CreateContainerParameters>
{
/// <summary>
/// Accepts the license agreement.
/// </summary>
/// <remarks>
/// Modules that require a license agreement must override and implement this
/// method to enforce proper license acceptance behavior.
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the license agreement is accepted.</param>
/// <returns>A configured instance of <typeparamref name="TBuilderEntity" />.</returns>
/// <exception cref="InvalidOperationException">Thrown when the module does not require a license agreement.</exception>
TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement);

/// <summary>
/// Sets the dependent container to resolve and start before starting this container configuration.
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions tests/Testcontainers.Db2.Tests/DeclineLicenseAgreementTest.cs
Original file line number Diff line number Diff line change
@@ -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<ArgumentException>(() => new Db2Builder().Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}

[Fact]
public void WithLicenseAgreementDeclinedThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new Db2Builder().WithAcceptLicenseAgreement(false).Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}
}
2 changes: 2 additions & 0 deletions tests/Testcontainers.Db2.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
3 changes: 0 additions & 3 deletions tests/Testcontainers.Kafka.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Testcontainers.Tests;

public sealed class AcceptLicenseAgreementTest
{
[Fact]
public void WithLicenseAgreementAcceptedThrowsArgumentException()
{
var exception = Assert.Throws<InvalidOperationException>(() => new ContainerBuilder().WithAcceptLicenseAgreement(true).Build());
Assert.Equal("The module does not require you to accept a license agreement.", exception.Message);
}
}
Original file line number Diff line number Diff line change
@@ -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<ArgumentException>(() => new ServiceBusBuilder().Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}

[Fact]
public void WithLicenseAgreementDeclinedThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new ServiceBusBuilder().WithAcceptLicenseAgreement(false).Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}
}
2 changes: 2 additions & 0 deletions tests/Testcontainers.ServiceBus.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down

0 comments on commit fa65855

Please sign in to comment.