diff --git a/Packages.props b/Packages.props index 7e46ba472..7d2e1a990 100644 --- a/Packages.props +++ b/Packages.props @@ -28,7 +28,7 @@ - + diff --git a/src/Testcontainers/Configurations/Modules/Databases/MariaDbTestcontainerConfiguration.cs b/src/Testcontainers/Configurations/Modules/Databases/MariaDbTestcontainerConfiguration.cs new file mode 100644 index 000000000..624959f5f --- /dev/null +++ b/src/Testcontainers/Configurations/Modules/Databases/MariaDbTestcontainerConfiguration.cs @@ -0,0 +1,66 @@ +namespace DotNet.Testcontainers.Configurations +{ + using DotNet.Testcontainers.Builders; + using JetBrains.Annotations; + + /// + [PublicAPI] + public class MariaDbTestcontainerConfiguration : TestcontainerDatabaseConfiguration + { + private const string MariaDbImage = "mariadb:10.8"; + + private const int MariaDbPort = 3306; + + /// + /// Initializes a new instance of the class. + /// + public MariaDbTestcontainerConfiguration() + : this(MariaDbImage) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker image. + public MariaDbTestcontainerConfiguration(string image) + : base(image, MariaDbPort) + { + this.Environments["MYSQL_ALLOW_EMPTY_PASSWORD"] = "yes"; + } + + /// + public override string Database + { + get => this.Environments["MYSQL_DATABASE"]; + set => this.Environments["MYSQL_DATABASE"] = value; + } + + /// + public override string Username + { + get => this.Environments["MYSQL_USER"]; + set => this.Environments["MYSQL_USER"] = value; + } + + /// + public override string Password + { + get => this.Environments["MYSQL_PASSWORD"]; + set => this.Environments["MYSQL_PASSWORD"] = value; + } + + /// + /// Gets or sets this specifies the password that will be set for the MariaDB root superuser account. + /// + public string RootPassword + { + get => this.Environments["MARIADB_ROOT_PASSWORD"]; + set => this.Environments["MARIADB_ROOT_PASSWORD"] = value; + } + + /// + public override IWaitForContainerOS WaitStrategy => Wait.ForUnixContainer() + .UntilCommandIsCompleted("mysql", "--host=localhost", $"--port={this.DefaultPort}", $"--user={this.Username}", $"--password={this.Password}", "--protocol=TCP", "--execute=SHOW DATABASES"); + } +} diff --git a/src/Testcontainers/Containers/Modules/Databases/MariaDbTestcontainer.cs b/src/Testcontainers/Containers/Modules/Databases/MariaDbTestcontainer.cs new file mode 100644 index 000000000..3935669ce --- /dev/null +++ b/src/Testcontainers/Containers/Modules/Databases/MariaDbTestcontainer.cs @@ -0,0 +1,45 @@ +namespace DotNet.Testcontainers.Containers +{ + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using DotNet.Testcontainers.Configurations; + using JetBrains.Annotations; + using Microsoft.Extensions.Logging; + + /// + [PublicAPI] + public sealed class MariaDbTestcontainer : TestcontainerDatabase + { + /// + /// Initializes a new instance of the class. + /// + /// The Testcontainers configuration. + /// The logger. + internal MariaDbTestcontainer(ITestcontainersConfiguration configuration, ILogger logger) + : base(configuration, logger) + { + } + + /// + public override string ConnectionString + => $"Server={this.Hostname};Port={this.Port};Database={this.Database};Uid={this.Username};Pwd={this.Password};"; + + /// + /// Executes a SQL script in the database container. + /// + /// The content of the SQL script to be executed. + /// Cancellation token. + /// Task that completes when the script has been executed. + public override async Task ExecScriptAsync(string scriptContent, CancellationToken ct = default) + { + var tempScriptFile = this.GetTempScriptFile(); + + await this.CopyFileAsync(tempScriptFile, Encoding.Default.GetBytes(scriptContent), 493, 0, 0, ct) + .ConfigureAwait(false); + + return await this.ExecAsync(new[] { "mysql", $"--host={this.Hostname}", $"--port={this.ContainerPort}", $"--user={this.Username}", $"--password={this.Password}", this.Database, $"--execute=source {tempScriptFile}" }, ct) + .ConfigureAwait(false); + } + } +} diff --git a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MariaDbFixture.cs b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MariaDbFixture.cs new file mode 100644 index 000000000..042768ace --- /dev/null +++ b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MariaDbFixture.cs @@ -0,0 +1,42 @@ +namespace DotNet.Testcontainers.Tests.Fixtures +{ + using System.Data.Common; + using System.Threading.Tasks; + using DotNet.Testcontainers.Builders; + using DotNet.Testcontainers.Configurations; + using DotNet.Testcontainers.Containers; + using MySqlConnector; + + public sealed class MariaDbFixture : DatabaseFixture + { + private readonly TestcontainerDatabaseConfiguration configuration = new MariaDbTestcontainerConfiguration { Database = "db", Username = "mysql", Password = "mysql" }; + + public MariaDbFixture() + { + this.Container = new TestcontainersBuilder() + .WithDatabase(this.configuration) + .Build(); + } + + public override async Task InitializeAsync() + { + await this.Container.StartAsync() + .ConfigureAwait(false); + + this.Connection = new MySqlConnection(this.Container.ConnectionString); + } + + public override async Task DisposeAsync() + { + this.Connection.Dispose(); + + await this.Container.DisposeAsync() + .ConfigureAwait(false); + } + + public override void Dispose() + { + this.configuration.Dispose(); + } + } +} diff --git a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MySqlFixture.cs b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MySqlFixture.cs index d17569522..e60060dcc 100644 --- a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MySqlFixture.cs +++ b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/MySqlFixture.cs @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Tests.Fixtures using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; - using MySql.Data.MySqlClient; + using MySqlConnector; public sealed class MySqlFixture : DatabaseFixture { diff --git a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj index aaa05f53c..af26f2c7f 100644 --- a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj +++ b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj @@ -13,6 +13,7 @@ + @@ -24,7 +25,6 @@ - diff --git a/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/MariaDbTestcontainerTest.cs b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/MariaDbTestcontainerTest.cs new file mode 100644 index 000000000..f45fbf76d --- /dev/null +++ b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/MariaDbTestcontainerTest.cs @@ -0,0 +1,69 @@ +namespace DotNet.Testcontainers.Tests.Unit +{ + using System.Data; + using System.Threading.Tasks; + using DotNet.Testcontainers.Tests.Fixtures; + using Xunit; + + [Collection(nameof(Testcontainers))] + public sealed class MariaDbTestcontainerTest : IClassFixture + { + private readonly MariaDbFixture mariaDbFixture; + + public MariaDbTestcontainerTest(MariaDbFixture mariaDbFixture) + { + this.mariaDbFixture = mariaDbFixture; + } + + [Fact] + public async Task ConnectionEstablished() + { + // Given + var connection = this.mariaDbFixture.Connection; + + // When + await connection.OpenAsync() + .ConfigureAwait(false); + + // Then + Assert.Equal(ConnectionState.Open, connection.State); + } + + [Fact] + public async Task ExecScriptInRunningContainer() + { + // Given + const string script = @" + CREATE TABLE MyTable ( + id INT(6) UNSIGNED PRIMARY KEY, + name VARCHAR(30) NOT NULL + ); + INSERT INTO MyTable (id, name) VALUES (1, 'MyName'); + SELECT * FROM MyTable; + "; + + // When + var result = await this.mariaDbFixture.Container.ExecScriptAsync(script) + .ConfigureAwait(false); + + // Then + Assert.Equal(0, result.ExitCode); + Assert.Contains("MyName", result.Stdout); + } + + [Fact] + public async Task ThrowErrorInRunningContainerWithInvalidScript() + { + // Given + const string script = "invalid SQL command"; + + // When + var result = await this.mariaDbFixture.Container.ExecScriptAsync(script) + .ConfigureAwait(false); + + // Then + Assert.Equal(0, result.ExitCode); // exit code is 0 because MariaDB docker image does not have a proper error handler + Assert.Contains("ERROR 1064 (42000)", result.Stderr); + } + } +}