diff --git a/docs/modules/cassandra.md b/docs/modules/cassandra.md index 7e5b70abf..e2a72c55b 100644 --- a/docs/modules/cassandra.md +++ b/docs/modules/cassandra.md @@ -8,7 +8,15 @@ Add the following dependency to your project file: dotnet add package Testcontainers.Cassandra ``` -You can start an Apache Cassandra container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. +You can start an Apache Cassandra container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below: + +=== "Start a Cassandra container" + ```csharp + var cassandraContainer = new CassandraBuilder().Build(); + await cassandraContainer.StartAsync(); + ``` + +The following example utilizes the [xUnit.net](/test_frameworks/xunit_net/) module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class. === "Usage Example" ```csharp diff --git a/docs/modules/db2.md b/docs/modules/db2.md index 8df65e230..e5eddb59d 100644 --- a/docs/modules/db2.md +++ b/docs/modules/db2.md @@ -12,7 +12,15 @@ dotnet add package Testcontainers.Db2 The Linux client dependency, [Net.IBM.Data.Db2-lnx](https://www.nuget.org/packages/Net.IBM.Data.Db2-lnx), requires additional configurations. We use the [Testcontainers.Db2.Tests.targets](https://github.com/testcontainers/testcontainers-dotnet/blob/develop/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.targets) file to configure the environment variables: `LD_LIBRARY_PATH`, `PATH`, `DB2_CLI_DRIVER_INSTALL_PATH`, at runtime. -You can start an Db2 container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. +You can start a Db2 container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below: + +=== "Start a Db2 container" + ```csharp + var db2Container = new Db2Builder().Build(); + await db2Container.StartAsync(); + ``` + +The following example utilizes the [xUnit.net](/test_frameworks/xunit_net/) module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class. === "Usage Example" ```csharp diff --git a/docs/modules/mssql.md b/docs/modules/mssql.md index 64b52faa5..93e1aa26c 100644 --- a/docs/modules/mssql.md +++ b/docs/modules/mssql.md @@ -8,14 +8,15 @@ Add the following dependency to your project file: dotnet add package Testcontainers.MsSql ``` -You can start a MSSQL container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations. +You can start a MSSQL container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below: -=== "Create Container Instance" +=== "Start a MSSQL container" ```csharp - --8<-- "tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs:CreateMsSqlContainer" + var msSqlContainer = new MsSqlBuilder().Build(); + await msSqlContainer.StartAsync(); ``` -This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. +The following example utilizes the [xUnit.net](/test_frameworks/xunit_net/) module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class. === "Usage Example" ```csharp diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index b38611e4d..376056cf3 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -8,7 +8,15 @@ Add the following dependency to your project file: dotnet add package Testcontainers.PostgreSql ``` -You can start an PostgreSQL container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. +You can start a PostgreSQL container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below: + +=== "Start a PostgreSQL container" + ```csharp + var postgreSqlContainer = new PostgreSqlBuilder().Build(); + await postgreSqlContainer.StartAsync(); + ``` + +The following example utilizes the [xUnit.net](/test_frameworks/xunit_net/) module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class. === "Usage Example" ```csharp diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md index f1c5627ff..e9e5ce454 100644 --- a/docs/modules/pulsar.md +++ b/docs/modules/pulsar.md @@ -8,7 +8,7 @@ Add the following dependency to your project file: dotnet add package Testcontainers.Pulsar ``` -You can start a Apache Pulsar container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations. +You can start an Apache Pulsar container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations. === "Create Container Instance" ```csharp diff --git a/docs/modules/qdrant.md b/docs/modules/qdrant.md index 5de677a2a..9da592fdb 100644 --- a/docs/modules/qdrant.md +++ b/docs/modules/qdrant.md @@ -8,7 +8,7 @@ Add the following dependency to your project file: dotnet add package Testcontainers.Qdrant ``` -You can start an Qdrant container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. +You can start a Qdrant container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. === "Usage Example" ```csharp diff --git a/src/Testcontainers.Cassandra/CassandraContainer.cs b/src/Testcontainers.Cassandra/CassandraContainer.cs index 3e44d61ce..819148014 100644 --- a/src/Testcontainers.Cassandra/CassandraContainer.cs +++ b/src/Testcontainers.Cassandra/CassandraContainer.cs @@ -16,9 +16,12 @@ public CassandraContainer(CassandraConfiguration configuration) /// The Cassandra connection string. public string GetConnectionString() { + var publicPort = GetMappedPublicPort(CassandraBuilder.CqlPort).ToString(); + var properties = new Dictionary(); properties.Add("Contact Points", Hostname); - properties.Add("Port", GetMappedPublicPort(CassandraBuilder.CqlPort).ToString()); + properties.Add("Port", publicPort); + properties.Add("Cluster Name", $"{Hostname}:{publicPort}"); return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value))); } diff --git a/src/Testcontainers.EventHubs/Usings.cs b/src/Testcontainers.EventHubs/Usings.cs index b68f67969..aac442571 100644 --- a/src/Testcontainers.EventHubs/Usings.cs +++ b/src/Testcontainers.EventHubs/Usings.cs @@ -4,7 +4,6 @@ global using System.Linq; global using System.Text; global using System.Text.Json; -global using System.Threading.Tasks; global using Docker.DotNet.Models; global using DotNet.Testcontainers; global using DotNet.Testcontainers.Builders; diff --git a/src/Testcontainers/Configurations/WaitStrategies/IWaitForContainerOS.cs b/src/Testcontainers/Configurations/WaitStrategies/IWaitForContainerOS.cs index cacefc5dd..e34ed7a57 100644 --- a/src/Testcontainers/Configurations/WaitStrategies/IWaitForContainerOS.cs +++ b/src/Testcontainers/Configurations/WaitStrategies/IWaitForContainerOS.cs @@ -2,7 +2,9 @@ namespace DotNet.Testcontainers.Configurations { using System; using System.Collections.Generic; + using System.Data.Common; using System.Text.RegularExpressions; + using DotNet.Testcontainers.Containers; using JetBrains.Annotations; /// @@ -112,6 +114,18 @@ public interface IWaitForContainerOS [PublicAPI] IWaitForContainerOS UntilContainerIsHealthy(long failingStreak = 3, Action waitStrategyModifier = null); + /// + /// Waits until a successful connection to the database can be established. + /// + /// + /// To use this wait strategy, the container must implement the interface. + /// + /// The used to create the database connection. + /// The wait strategy modifier to cancel the readiness check. + /// A configured instance of . + [PublicAPI] + IWaitForContainerOS UntilDatabaseIsAvailable(DbProviderFactory dbProviderFactory, Action waitStrategyModifier = null); + /// /// Returns a collection with all configured wait strategies. /// diff --git a/src/Testcontainers/Configurations/WaitStrategies/UntilDatabaseIsAvailable.cs b/src/Testcontainers/Configurations/WaitStrategies/UntilDatabaseIsAvailable.cs new file mode 100644 index 000000000..c36e43bc2 --- /dev/null +++ b/src/Testcontainers/Configurations/WaitStrategies/UntilDatabaseIsAvailable.cs @@ -0,0 +1,52 @@ +namespace DotNet.Testcontainers.Configurations +{ + using System; + using System.Data.Common; + using System.Threading.Tasks; + using DotNet.Testcontainers.Containers; + + internal class UntilDatabaseIsAvailable : IWaitUntil + { + private readonly DbProviderFactory _dbProviderFactory; + + public UntilDatabaseIsAvailable(DbProviderFactory dbProviderFactory) + { + _dbProviderFactory = dbProviderFactory; + } + + public async Task UntilAsync(IContainer container) + { + if (container is not IDatabaseContainer dbContainer) + { + throw new NotSupportedException( + $"The 'UntilDatabaseIsAvailable' wait strategy can only be used with database containers. " + + $"The provided container type '{container.GetType().FullName}' does not implement '{nameof(IDatabaseContainer)}'."); + } + + var connection = _dbProviderFactory.CreateConnection(); + if (connection == null) + { + throw new InvalidOperationException( + $"Failed to create a database connection. The factory '{_dbProviderFactory.GetType().FullName}' returned null from 'CreateConnection()'."); + } + + try + { + connection.ConnectionString = dbContainer.GetConnectionString(); + + await connection.OpenAsync() + .ConfigureAwait(false); + + return true; + } + catch + { + return false; + } + finally + { + connection.Dispose(); + } + } + } +} diff --git a/src/Testcontainers/Configurations/WaitStrategies/WaitForContainerOS.cs b/src/Testcontainers/Configurations/WaitStrategies/WaitForContainerOS.cs index c5306d1c2..91777eecd 100644 --- a/src/Testcontainers/Configurations/WaitStrategies/WaitForContainerOS.cs +++ b/src/Testcontainers/Configurations/WaitStrategies/WaitForContainerOS.cs @@ -2,6 +2,7 @@ namespace DotNet.Testcontainers.Configurations { using System; using System.Collections.Generic; + using System.Data.Common; using System.Text.RegularExpressions; /// @@ -80,6 +81,12 @@ public virtual IWaitForContainerOS UntilContainerIsHealthy(long failingStreak = return AddCustomWaitStrategy(new UntilContainerIsHealthy(failingStreak), waitStrategyModifier); } + /// + public virtual IWaitForContainerOS UntilDatabaseIsAvailable(DbProviderFactory dbProviderFactory, Action waitStrategyModifier = null) + { + return AddCustomWaitStrategy(new UntilDatabaseIsAvailable(dbProviderFactory), waitStrategyModifier); + } + /// public IEnumerable Build() { diff --git a/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs index e31915dcb..d351eab42 100644 --- a/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs +++ b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs @@ -1,26 +1,14 @@ namespace Testcontainers.Cassandra; -public sealed class CassandraContainerTest : IAsyncLifetime +public abstract class CassandraContainerTest(CassandraContainerTest.CassandraDefaultFixture fixture) { // # --8<-- [start:UseCassandraContainer] - private readonly CassandraContainer _cassandraContainer = new CassandraBuilder().Build(); - - public Task InitializeAsync() - { - return _cassandraContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _cassandraContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new CqlConnection(_cassandraContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -36,7 +24,7 @@ public void ExecuteCqlStatementReturnsExpectedResult() // Given const string selectFromSystemLocalStatement = "SELECT * FROM system.local WHERE key = ?;"; - using var cluster = Cluster.Builder().WithConnectionString(_cassandraContainer.GetConnectionString()).Build(); + using var cluster = Cluster.Builder().WithConnectionString(fixture.Container.GetConnectionString()).Build(); // When using var session = cluster.Connect(); @@ -61,11 +49,34 @@ public async Task ExecScriptAsyncReturnsSuccess() const string selectFromSystemLocalStatement = "SELECT * FROM system.local;"; // When - var execResult = await _cassandraContainer.ExecScriptAsync(selectFromSystemLocalStatement) + var execResult = await fixture.Container.ExecScriptAsync(selectFromSystemLocalStatement) .ConfigureAwait(true); // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + public class CassandraDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => CqlProviderFactory.Instance; + } + + [UsedImplicitly] + public class CassandraWaitForDatabaseFixture(IMessageSink messageSink) + : CassandraDefaultFixture(messageSink) + { + protected override CassandraBuilder Configure(CassandraBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); + } + + [UsedImplicitly] + public sealed class CassandraDefaultConfiguration(CassandraDefaultFixture fixture) + : CassandraContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class CassandraWaitForDatabaseConfiguration(CassandraWaitForDatabaseFixture fixture) + : CassandraContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj b/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj index 6e7c4262b..217e1ed9f 100644 --- a/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj +++ b/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj @@ -15,6 +15,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.Cassandra.Tests/Usings.cs b/tests/Testcontainers.Cassandra.Tests/Usings.cs index 3c66e0be2..0ade8d3c1 100644 --- a/tests/Testcontainers.Cassandra.Tests/Usings.cs +++ b/tests/Testcontainers.Cassandra.Tests/Usings.cs @@ -4,5 +4,9 @@ global using System.Threading.Tasks; global using Cassandra; global using Cassandra.Data; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; -global using Xunit; \ No newline at end of file +global using JetBrains.Annotations; +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs b/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs index 7d423065b..cd3fb97b3 100644 --- a/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs +++ b/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs @@ -1,25 +1,13 @@ namespace Testcontainers.ClickHouse; -public sealed class ClickHouseContainerTest : IAsyncLifetime +public abstract class ClickHouseContainerTest(ClickHouseContainerTest.ClickHouseDefaultFixture fixture) { - private readonly ClickHouseContainer _clickHouseContainer = new ClickHouseBuilder().Build(); - - public Task InitializeAsync() - { - return _clickHouseContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _clickHouseContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new ClickHouseConnection(_clickHouseContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -36,11 +24,34 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _clickHouseContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + public class ClickHouseDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => ClickHouseConnectionFactory.Instance; + } + + [UsedImplicitly] + public class ClickHouseWaitForDatabaseFixture(IMessageSink messageSink) + : ClickHouseDefaultFixture(messageSink) + { + protected override ClickHouseBuilder Configure(ClickHouseBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); + } + + [UsedImplicitly] + public sealed class ClickHouseDefaultConfiguration(ClickHouseDefaultFixture fixture) + : ClickHouseContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class ClickHouseWaitForDatabaseConfiguration(ClickHouseWaitForDatabaseFixture fixture) + : ClickHouseContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.ClickHouse.Tests/Testcontainers.ClickHouse.Tests.csproj b/tests/Testcontainers.ClickHouse.Tests/Testcontainers.ClickHouse.Tests.csproj index b357c04c4..8f0461dc2 100644 --- a/tests/Testcontainers.ClickHouse.Tests/Testcontainers.ClickHouse.Tests.csproj +++ b/tests/Testcontainers.ClickHouse.Tests/Testcontainers.ClickHouse.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.ClickHouse.Tests/Usings.cs b/tests/Testcontainers.ClickHouse.Tests/Usings.cs index 349e20d26..28abe4d11 100644 --- a/tests/Testcontainers.ClickHouse.Tests/Usings.cs +++ b/tests/Testcontainers.ClickHouse.Tests/Usings.cs @@ -2,5 +2,9 @@ global using System.Data.Common; global using System.Threading.Tasks; global using ClickHouse.Client.ADO; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; -global using Xunit; \ No newline at end of file +global using JetBrains.Annotations; +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs b/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs index 9c3f37566..f14917b63 100644 --- a/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs +++ b/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs @@ -1,25 +1,13 @@ namespace Testcontainers.CockroachDb; -public sealed class CockroachDbContainerTest : IAsyncLifetime +public abstract class CockroachDbContainerTest(CockroachDbContainerTest.CockroachDbDefaultFixture fixture) { - private readonly CockroachDbContainer _cockroachDbContainer = new CockroachDbBuilder().Build(); - - public Task InitializeAsync() - { - return _cockroachDbContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _cockroachDbContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new NpgsqlConnection(_cockroachDbContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -36,11 +24,34 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _cockroachDbContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + public class CockroachDbDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => NpgsqlFactory.Instance; + } + + [UsedImplicitly] + public class CockroachDbWaitForDatabaseFixture(IMessageSink messageSink) + : CockroachDbDefaultFixture(messageSink) + { + protected override CockroachDbBuilder Configure(CockroachDbBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); + } + + [UsedImplicitly] + public sealed class CockroachDbDefaultConfiguration(CockroachDbDefaultFixture fixture) + : CockroachDbContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class CockroachDbWaitForDatabaseConfiguration(CockroachDbWaitForDatabaseFixture fixture) + : CockroachDbContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.CockroachDb.Tests/Testcontainers.CockroachDb.Tests.csproj b/tests/Testcontainers.CockroachDb.Tests/Testcontainers.CockroachDb.Tests.csproj index e50fd8d13..8106c3121 100644 --- a/tests/Testcontainers.CockroachDb.Tests/Testcontainers.CockroachDb.Tests.csproj +++ b/tests/Testcontainers.CockroachDb.Tests/Testcontainers.CockroachDb.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.CockroachDb.Tests/Usings.cs b/tests/Testcontainers.CockroachDb.Tests/Usings.cs index 576c631bf..5437a7d93 100644 --- a/tests/Testcontainers.CockroachDb.Tests/Usings.cs +++ b/tests/Testcontainers.CockroachDb.Tests/Usings.cs @@ -1,6 +1,10 @@ global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; +global using JetBrains.Annotations; global using Npgsql; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs b/tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs index 3b74470d7..9b55cca82 100644 --- a/tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs +++ b/tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs @@ -1,26 +1,14 @@ namespace Testcontainers.Db2; -public sealed class Db2ContainerTest : IAsyncLifetime +public abstract class Db2ContainerTest(Db2ContainerTest.Db2DefaultFixture fixture) { // # --8<-- [start:UseDb2Container] - private readonly Db2Container _db2Container = new Db2Builder().WithAcceptLicenseAgreement(true).Build(); - - public Task InitializeAsync() - { - return _db2Container.StartAsync(); - } - - public Task DisposeAsync() - { - return _db2Container.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new DB2Connection(_db2Container.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -37,7 +25,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1 FROM SYSIBM.SYSDUMMY1;"; // When - var execResult = await _db2Container.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -45,4 +33,30 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } // # --8<-- [end:UseDb2Container] + + public class Db2DefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => DB2Factory.Instance; + + protected override Db2Builder Configure(Db2Builder builder) + => builder.WithAcceptLicenseAgreement(true); + } + + [UsedImplicitly] + public class Db2WaitForDatabaseFixture(IMessageSink messageSink) + : Db2DefaultFixture(messageSink) + { + protected override Db2Builder Configure(Db2Builder builder) + => base.Configure(builder).WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); + } + + [UsedImplicitly] + public sealed class Db2DefaultConfiguration(Db2DefaultFixture fixture) + : Db2ContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class Db2WaitForDatabaseConfiguration(Db2WaitForDatabaseFixture fixture) + : Db2ContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj b/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj index 54bfc255b..66f24de5d 100644 --- a/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj +++ b/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj @@ -23,6 +23,7 @@ + diff --git a/tests/Testcontainers.Db2.Tests/Usings.cs b/tests/Testcontainers.Db2.Tests/Usings.cs index 2a9a1fa23..d6b581c47 100644 --- a/tests/Testcontainers.Db2.Tests/Usings.cs +++ b/tests/Testcontainers.Db2.Tests/Usings.cs @@ -3,6 +3,10 @@ global using System.Text.RegularExpressions; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using IBM.Data.Db2; -global using Xunit; \ No newline at end of file +global using JetBrains.Annotations; +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs b/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs index 220b90619..d29af98a4 100644 --- a/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs +++ b/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs @@ -1,30 +1,13 @@ namespace Testcontainers.FirebirdSql; -public abstract class FirebirdSqlContainerTest : IAsyncLifetime +public abstract class FirebirdSqlContainerTest(FirebirdSqlContainerTest.FirebirdSqlDefaultFixture fixture) { - private readonly FirebirdSqlContainer _firebirdSqlContainer; - - private FirebirdSqlContainerTest(FirebirdSqlContainer firebirdSqlContainer) - { - _firebirdSqlContainer = firebirdSqlContainer; - } - - public Task InitializeAsync() - { - return _firebirdSqlContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _firebirdSqlContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new FbConnection(_firebirdSqlContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -41,7 +24,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1 FROM RDB$DATABASE;"; // When - var execResult = await _firebirdSqlContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -49,48 +32,74 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } + public class FirebirdSqlDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => FirebirdClientFactory.Instance; + } + [UsedImplicitly] - public sealed class FirebirdSql25Sc : FirebirdSqlContainerTest + public class FirebirdSqlWaitForDatabaseFixture(IMessageSink messageSink) + : FirebirdSqlDefaultFixture(messageSink) { - public FirebirdSql25Sc() - : base(new FirebirdSqlBuilder().WithImage("jacobalberty/firebird:2.5-sc").Build()) - { - } + protected override FirebirdSqlBuilder Configure(FirebirdSqlBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); } [UsedImplicitly] - public sealed class FirebirdSql25Ss : FirebirdSqlContainerTest + public class FirebirdSql25ScFixture(IMessageSink messageSink) + : FirebirdSqlDefaultFixture(messageSink) { - public FirebirdSql25Ss() - : base(new FirebirdSqlBuilder().WithImage("jacobalberty/firebird:2.5-ss").Build()) - { - } + protected override FirebirdSqlBuilder Configure(FirebirdSqlBuilder builder) + => builder.WithImage("jacobalberty/firebird:2.5-sc"); } [UsedImplicitly] - public sealed class FirebirdSql30 : FirebirdSqlContainerTest + public class FirebirdSql25SsFixture(IMessageSink messageSink) + : FirebirdSqlDefaultFixture(messageSink) { - public FirebirdSql30() - : base(new FirebirdSqlBuilder().WithImage("jacobalberty/firebird:v3.0").Build()) - { - } + protected override FirebirdSqlBuilder Configure(FirebirdSqlBuilder builder) + => builder.WithImage("jacobalberty/firebird:2.5-ss"); } [UsedImplicitly] - public sealed class FirebirdSql40 : FirebirdSqlContainerTest + public class FirebirdSql30Fixture(IMessageSink messageSink) + : FirebirdSqlDefaultFixture(messageSink) { - public FirebirdSql40() - : base(new FirebirdSqlBuilder().WithImage("jacobalberty/firebird:v4.0").Build()) - { - } + protected override FirebirdSqlBuilder Configure(FirebirdSqlBuilder builder) + => builder.WithImage("jacobalberty/firebird:v3.0"); } [UsedImplicitly] - public sealed class FirebirdSqlSysdba : FirebirdSqlContainerTest + public class FirebirdSqlSysdbaFixture(IMessageSink messageSink) + : FirebirdSqlDefaultFixture(messageSink) { - public FirebirdSqlSysdba() - : base(new FirebirdSqlBuilder().WithUsername("sysdba").WithPassword("some-password").Build()) - { - } + protected override FirebirdSqlBuilder Configure(FirebirdSqlBuilder builder) + => builder.WithUsername("sysdba").WithPassword("some-password"); } + + [UsedImplicitly] + public sealed class FirebirdSqlDefaultConfiguration(FirebirdSqlDefaultFixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class FirebirdSqlWaitForDatabaseConfiguration(FirebirdSqlWaitForDatabaseFixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class FirebirdSql25ScConfiguration(FirebirdSql25ScFixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class FirebirdSql25SsConfiguration(FirebirdSql25SsFixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class FirebirdSql30Configuration(FirebirdSql30Fixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class FirebirdSqlSysdbaConfiguration(FirebirdSqlSysdbaFixture fixture) + : FirebirdSqlContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.FirebirdSql.Tests/Testcontainers.FirebirdSql.Tests.csproj b/tests/Testcontainers.FirebirdSql.Tests/Testcontainers.FirebirdSql.Tests.csproj index ab085ac17..902518923 100644 --- a/tests/Testcontainers.FirebirdSql.Tests/Testcontainers.FirebirdSql.Tests.csproj +++ b/tests/Testcontainers.FirebirdSql.Tests/Testcontainers.FirebirdSql.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.FirebirdSql.Tests/Usings.cs b/tests/Testcontainers.FirebirdSql.Tests/Usings.cs index 28861332f..b0801edfa 100644 --- a/tests/Testcontainers.FirebirdSql.Tests/Usings.cs +++ b/tests/Testcontainers.FirebirdSql.Tests/Usings.cs @@ -1,7 +1,10 @@ global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using FirebirdSql.Data.FirebirdClient; global using JetBrains.Annotations; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs b/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs index acd8480c5..ae0056bac 100644 --- a/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs +++ b/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs @@ -1,30 +1,13 @@ namespace Testcontainers.MariaDb; -public abstract class MariaDbContainerTest : IAsyncLifetime +public abstract class MariaDbContainerTest(MariaDbContainerTest.MariaDbDefaultFixture fixture) { - private readonly MariaDbContainer _mariaDbContainer; - - protected MariaDbContainerTest(MariaDbContainer mariaDbContainer) - { - _mariaDbContainer = mariaDbContainer; - } - - public Task InitializeAsync() - { - return _mariaDbContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _mariaDbContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new MySqlConnection(_mariaDbContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -41,7 +24,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _mariaDbContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -49,21 +32,26 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } - [UsedImplicitly] - public sealed class MariaDbUserConfiguration : MariaDbContainerTest + public class MariaDbDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) { - public MariaDbUserConfiguration() - : base(new MariaDbBuilder().Build()) - { - } + public override DbProviderFactory DbProviderFactory + => MySqlConnectorFactory.Instance; } [UsedImplicitly] - public sealed class MariaDbRootConfiguration : MariaDbContainerTest + public class MariaDbWaitForDatabaseFixture(IMessageSink messageSink) + : MariaDbDefaultFixture(messageSink) { - public MariaDbRootConfiguration() - : base(new MariaDbBuilder().WithUsername("root").Build()) - { - } + protected override MariaDbBuilder Configure(MariaDbBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); } + + [UsedImplicitly] + public sealed class MariaDbDefaultConfiguration(MariaDbDefaultFixture fixture) + : MariaDbContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class MariaDbWaitForDatabaseConfiguration(MariaDbWaitForDatabaseFixture fixture) + : MariaDbContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.MariaDb.Tests/Testcontainers.MariaDb.Tests.csproj b/tests/Testcontainers.MariaDb.Tests/Testcontainers.MariaDb.Tests.csproj index bb56ee175..7de9413cc 100644 --- a/tests/Testcontainers.MariaDb.Tests/Testcontainers.MariaDb.Tests.csproj +++ b/tests/Testcontainers.MariaDb.Tests/Testcontainers.MariaDb.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.MariaDb.Tests/Usings.cs b/tests/Testcontainers.MariaDb.Tests/Usings.cs index 5ae65ce1e..63dd23a10 100644 --- a/tests/Testcontainers.MariaDb.Tests/Usings.cs +++ b/tests/Testcontainers.MariaDb.Tests/Usings.cs @@ -1,7 +1,10 @@ global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using JetBrains.Annotations; global using MySqlConnector; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs index 36a144af4..affe00681 100644 --- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs +++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs @@ -1,31 +1,14 @@ namespace Testcontainers.MsSql; -public abstract class MsSqlContainerTest : IAsyncLifetime +public abstract class MsSqlContainerTest(MsSqlContainerTest.MsSqlDefaultFixture fixture) { - private readonly MsSqlContainer _msSqlContainer; - - public MsSqlContainerTest(MsSqlContainer msSqlContainer) - { - _msSqlContainer = msSqlContainer; - } - // # --8<-- [start:UseMsSqlContainer] - public Task InitializeAsync() - { - return _msSqlContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _msSqlContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new SqlConnection(_msSqlContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -42,7 +25,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _msSqlContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -51,14 +34,26 @@ public async Task ExecScriptReturnsSuccessful() } // # --8<-- [end:UseMsSqlContainer] - // # --8<-- [start:CreateMsSqlContainer] + public class MsSqlDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => SqlClientFactory.Instance; + } + [UsedImplicitly] - public sealed class MsSqlDefaultConfiguration : MsSqlContainerTest + public class MsSqlWaitForDatabaseFixture(IMessageSink messageSink) + : MsSqlDefaultFixture(messageSink) { - public MsSqlDefaultConfiguration() - : base(new MsSqlBuilder().Build()) - { - } + protected override MsSqlBuilder Configure(MsSqlBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); } - // # --8<-- [end:CreateMsSqlContainer] + + [UsedImplicitly] + public sealed class MsSqlDefaultConfiguration(MsSqlDefaultFixture fixture) + : MsSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class MsSqlWaitForDatabaseConfiguration(MsSqlWaitForDatabaseFixture fixture) + : MsSqlContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/Testcontainers.MsSql.Tests.csproj b/tests/Testcontainers.MsSql.Tests/Testcontainers.MsSql.Tests.csproj index ad2862d61..550fa255e 100644 --- a/tests/Testcontainers.MsSql.Tests/Testcontainers.MsSql.Tests.csproj +++ b/tests/Testcontainers.MsSql.Tests/Testcontainers.MsSql.Tests.csproj @@ -15,6 +15,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/Usings.cs b/tests/Testcontainers.MsSql.Tests/Usings.cs index 493aff2cb..8f7dd6f0b 100644 --- a/tests/Testcontainers.MsSql.Tests/Usings.cs +++ b/tests/Testcontainers.MsSql.Tests/Usings.cs @@ -1,7 +1,10 @@ global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using JetBrains.Annotations; global using Microsoft.Data.SqlClient; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs b/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs index c5e6d5e3a..6f93506cd 100644 --- a/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs +++ b/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs @@ -1,30 +1,13 @@ namespace Testcontainers.MySql; -public abstract class MySqlContainerTest : IAsyncLifetime +public abstract class MySqlContainerTest(MySqlContainerTest.MySqlDefaultFixture fixture) { - private readonly MySqlContainer _mySqlContainer; - - protected MySqlContainerTest(MySqlContainer mySqlContainer) - { - _mySqlContainer = mySqlContainer; - } - - public Task InitializeAsync() - { - return _mySqlContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _mySqlContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new MySqlConnection(_mySqlContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -41,7 +24,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _mySqlContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -49,31 +32,51 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } + public class MySqlDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => MySqlConnectorFactory.Instance; + } + [UsedImplicitly] - public sealed class MySqlUserConfiguration : MySqlContainerTest + public class MySqlWaitForDatabaseFixture(IMessageSink messageSink) + : MySqlDefaultFixture(messageSink) { - public MySqlUserConfiguration() - : base(new MySqlBuilder().Build()) - { - } + protected override MySqlBuilder Configure(MySqlBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); } [UsedImplicitly] - public sealed class MySqlRootConfiguration : MySqlContainerTest + public class MySqlRootFixture(IMessageSink messageSink) + : MySqlDefaultFixture(messageSink) { - public MySqlRootConfiguration() - : base(new MySqlBuilder().WithUsername("root").Build()) - { - } + protected override MySqlBuilder Configure(MySqlBuilder builder) + => builder.WithUsername("root"); } [UsedImplicitly] - public sealed class GitHubIssue1142 : MySqlContainerTest + public class MySqlGitHubIssue1142Fixture(IMessageSink messageSink) + : MySqlDefaultFixture(messageSink) { // https://github.com/testcontainers/testcontainers-dotnet/issues/1142. - public GitHubIssue1142() - : base(new MySqlBuilder().WithImage("mysql:8.0.28").Build()) - { - } + protected override MySqlBuilder Configure(MySqlBuilder builder) + => builder.WithImage("mysql:8.0.28"); } + + [UsedImplicitly] + public sealed class MySqlDefaultConfiguration(MySqlDefaultFixture fixture) + : MySqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class MySqlWaitForDatabaseConfiguration(MySqlWaitForDatabaseFixture fixture) + : MySqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class MySqlRootConfiguration(MySqlRootFixture fixture) + : MySqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class MySqlGitHubIssue1142Configuration(MySqlGitHubIssue1142Fixture fixture) + : MySqlContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.MySql.Tests/Testcontainers.MySql.Tests.csproj b/tests/Testcontainers.MySql.Tests/Testcontainers.MySql.Tests.csproj index ef10916ae..fd0c3798e 100644 --- a/tests/Testcontainers.MySql.Tests/Testcontainers.MySql.Tests.csproj +++ b/tests/Testcontainers.MySql.Tests/Testcontainers.MySql.Tests.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/tests/Testcontainers.MySql.Tests/Usings.cs b/tests/Testcontainers.MySql.Tests/Usings.cs index 5ae65ce1e..63dd23a10 100644 --- a/tests/Testcontainers.MySql.Tests/Usings.cs +++ b/tests/Testcontainers.MySql.Tests/Usings.cs @@ -1,7 +1,10 @@ global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using JetBrains.Annotations; global using MySqlConnector; -global using Xunit; \ No newline at end of file +global using Testcontainers.Xunit; +global using Xunit; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs index e0bf81335..0fcbb099c 100644 --- a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs +++ b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs @@ -1,13 +1,13 @@ namespace Testcontainers.Oracle; -public abstract class OracleContainerTest(OracleContainerTest.OracleFixture oracleFixture) +public abstract class OracleContainerTest(OracleContainerTest.OracleFixture fixture) { [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = oracleFixture.CreateConnection(); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -24,7 +24,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1 FROM DUAL;"; // When - var execResult = await oracleFixture.Container.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -32,7 +32,7 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } - public abstract class OracleFixture(IMessageSink messageSink, string edition, int? version, string database = null) : DbContainerFixture(messageSink) + public abstract class OracleFixture(IMessageSink messageSink, string edition, int? version, string database = null, bool waitForDatabase = false) : DbContainerFixture(messageSink) { public override DbProviderFactory DbProviderFactory => OracleClientFactory.Instance; @@ -40,11 +40,16 @@ protected override OracleBuilder Configure(OracleBuilder builder) { if (edition == null && version == null) { - return builder; + return Apply(builder, oracle => oracle); } var image = $"gvenzl/oracle-{edition}:{version}-slim-faststart"; - return database == null ? builder.WithImage(image) : builder.WithImage(image).WithDatabase(database); + return database == null ? Apply(builder, oracle => oracle.WithImage(image)) : Apply(builder, oracle => oracle.WithImage(image).WithDatabase(database)); + } + + private OracleBuilder Apply(OracleBuilder builder, Func configure) + { + return waitForDatabase ? configure(builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory))) : configure(builder); } } @@ -56,6 +61,7 @@ protected override OracleBuilder Configure(OracleBuilder builder) #if ORACLE_11 [UsedImplicitly] public sealed class Oracle11(Oracle11Fixture fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle11Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 11); + [UsedImplicitly] public sealed class Oracle11FixtureWaitForDatabase(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 11, waitForDatabase: true); #endif #if ORACLE_18 @@ -63,7 +69,7 @@ protected override OracleBuilder Configure(OracleBuilder builder) [UsedImplicitly] public sealed class Oracle18Default(Oracle18FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle18Scott(Oracle18FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle18Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18); - [UsedImplicitly] public sealed class Oracle18FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18, "XEPDB1"); + [UsedImplicitly] public sealed class Oracle18FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18, "XEPDB1", waitForDatabase: true); [UsedImplicitly] public sealed class Oracle18FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 18, "SCOTT"); #endif @@ -72,7 +78,7 @@ protected override OracleBuilder Configure(OracleBuilder builder) [UsedImplicitly] public sealed class Oracle21Default(Oracle21FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle21Scott(Oracle21FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle21Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21); - [UsedImplicitly] public sealed class Oracle21FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21, "XEPDB1"); + [UsedImplicitly] public sealed class Oracle21FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21, "XEPDB1", waitForDatabase: true); [UsedImplicitly] public sealed class Oracle21FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "xe", 21, "SCOTT"); #endif @@ -81,7 +87,7 @@ protected override OracleBuilder Configure(OracleBuilder builder) [UsedImplicitly] public sealed class Oracle23Default(Oracle23FixtureDefault fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle23Scott(Oracle23FixtureScott fixture) : OracleContainerTest(fixture), IClassFixture; [UsedImplicitly] public sealed class Oracle23Fixture(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23); - [UsedImplicitly] public sealed class Oracle23FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23, "FREEPDB1"); + [UsedImplicitly] public sealed class Oracle23FixtureDefault(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23, "FREEPDB1", waitForDatabase: true); [UsedImplicitly] public sealed class Oracle23FixtureScott(IMessageSink messageSink) : OracleFixture(messageSink, "free", 23, "SCOTT"); #endif } \ No newline at end of file diff --git a/tests/Testcontainers.Oracle.Tests/Usings.cs b/tests/Testcontainers.Oracle.Tests/Usings.cs index e1e61a204..17d816308 100644 --- a/tests/Testcontainers.Oracle.Tests/Usings.cs +++ b/tests/Testcontainers.Oracle.Tests/Usings.cs @@ -1,6 +1,8 @@ +global using System; global using System.Data; global using System.Data.Common; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using JetBrains.Annotations; global using Oracle.ManagedDataAccess.Client; diff --git a/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs b/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs index 7488c8312..3a29d6916 100644 --- a/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs +++ b/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs @@ -1,26 +1,14 @@ namespace Testcontainers.PostgreSql; -public sealed class PostgreSqlContainerTest : IAsyncLifetime +public abstract class PostgreSqlContainerTest(PostgreSqlContainerTest.PostgreSqlDefaultFixture fixture) { // # --8<-- [start:UsePostgreSqlContainer] - private readonly PostgreSqlContainer _postgreSqlContainer = new PostgreSqlBuilder().Build(); - - public Task InitializeAsync() - { - return _postgreSqlContainer.StartAsync(); - } - - public Task DisposeAsync() - { - return _postgreSqlContainer.DisposeAsync().AsTask(); - } - [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] public void ConnectionStateReturnsOpen() { // Given - using DbConnection connection = new NpgsqlConnection(_postgreSqlContainer.GetConnectionString()); + using DbConnection connection = fixture.CreateConnection(); // When connection.Open(); @@ -37,7 +25,7 @@ public async Task ExecScriptReturnsSuccessful() const string scriptContent = "SELECT 1;"; // When - var execResult = await _postgreSqlContainer.ExecScriptAsync(scriptContent) + var execResult = await fixture.Container.ExecScriptAsync(scriptContent) .ConfigureAwait(true); // Then @@ -46,13 +34,13 @@ public async Task ExecScriptReturnsSuccessful() } // # --8<-- [end:UsePostgreSqlContainer] - public sealed class ReuseContainerTest : IClassFixture, IDisposable + public sealed class ReuseContainerTest : IClassFixture, IDisposable { private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); - private readonly PostgreSqlFixture _fixture; + private readonly PostgreSqlDefaultFixture _fixture; - public ReuseContainerTest(PostgreSqlFixture fixture) + public ReuseContainerTest(PostgreSqlDefaultFixture fixture) { _fixture = fixture; } @@ -78,12 +66,26 @@ await _fixture.Container.StartAsync(_cts.Token) } } + public class PostgreSqlDefaultFixture(IMessageSink messageSink) + : DbContainerFixture(messageSink) + { + public override DbProviderFactory DbProviderFactory + => NpgsqlFactory.Instance; + } + [UsedImplicitly] - public sealed class PostgreSqlFixture : ContainerFixture + public class PostgreSqlWaitForDatabaseFixture(IMessageSink messageSink) + : PostgreSqlDefaultFixture(messageSink) { - public PostgreSqlFixture(IMessageSink messageSink) - : base(messageSink) - { - } + protected override PostgreSqlBuilder Configure(PostgreSqlBuilder builder) + => builder.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory)); } + + [UsedImplicitly] + public sealed class PostgreSqlDefaultConfiguration(PostgreSqlDefaultFixture fixture) + : PostgreSqlContainerTest(fixture), IClassFixture; + + [UsedImplicitly] + public sealed class PostgreSqlWaitForDatabaseConfiguration(PostgreSqlWaitForDatabaseFixture fixture) + : PostgreSqlContainerTest(fixture), IClassFixture; } \ No newline at end of file diff --git a/tests/Testcontainers.PostgreSql.Tests/Usings.cs b/tests/Testcontainers.PostgreSql.Tests/Usings.cs index 2d2ea46e3..a9392be0b 100644 --- a/tests/Testcontainers.PostgreSql.Tests/Usings.cs +++ b/tests/Testcontainers.PostgreSql.Tests/Usings.cs @@ -3,6 +3,7 @@ global using System.Data.Common; global using System.Threading; global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Commons; global using JetBrains.Annotations; global using Npgsql; diff --git a/tests/Testcontainers.Qdrant.Tests/Usings.cs b/tests/Testcontainers.Qdrant.Tests/Usings.cs index 4923c28cb..ff80cf90b 100644 --- a/tests/Testcontainers.Qdrant.Tests/Usings.cs +++ b/tests/Testcontainers.Qdrant.Tests/Usings.cs @@ -6,7 +6,6 @@ global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; global using Grpc.Core; -global using Grpc.Core.Interceptors; global using Grpc.Net.Client; global using Qdrant.Client; global using Qdrant.Client.Grpc;