Skip to content

Commit

Permalink
feat: Add MariaDB module
Browse files Browse the repository at this point in the history
  • Loading branch information
renemadsen authored and HofmeisterAn committed Jul 27, 2022
1 parent 1550fe6 commit f5d471c
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<PackageReference Update="CouchbaseNetClient" Version="3.2.8" />
<PackageReference Update="MongoDB.Driver" Version="2.15.0" />
<PackageReference Update="MyCouch" Version="7.6.0" />
<PackageReference Update="MySql.Data" Version="8.0.28" />
<PackageReference Update="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
<PackageReference Update="Npgsql" Version="6.0.3" />
<PackageReference Update="Oracle.ManagedDataAccess.Core" Version="3.21.50" />
<PackageReference Update="RabbitMQ.Client" Version="6.2.4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace DotNet.Testcontainers.Configurations
{
using DotNet.Testcontainers.Builders;
using JetBrains.Annotations;

/// <inheritdoc cref="TestcontainerDatabaseConfiguration" />
[PublicAPI]
public class MariaDbTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
private const string MariaDbImage = "mariadb:10.8";

private const int MariaDbPort = 3306;

/// <summary>
/// Initializes a new instance of the <see cref="MariaDbTestcontainerConfiguration" /> class.
/// </summary>
public MariaDbTestcontainerConfiguration()
: this(MariaDbImage)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MariaDbTestcontainerConfiguration" /> class.
/// </summary>
/// <param name="image">The Docker image.</param>
public MariaDbTestcontainerConfiguration(string image)
: base(image, MariaDbPort)
{
this.Environments["MYSQL_ALLOW_EMPTY_PASSWORD"] = "yes";
}

/// <inheritdoc />
public override string Database
{
get => this.Environments["MYSQL_DATABASE"];
set => this.Environments["MYSQL_DATABASE"] = value;
}

/// <inheritdoc />
public override string Username
{
get => this.Environments["MYSQL_USER"];
set => this.Environments["MYSQL_USER"] = value;
}

/// <inheritdoc />
public override string Password
{
get => this.Environments["MYSQL_PASSWORD"];
set => this.Environments["MYSQL_PASSWORD"] = value;
}

/// <summary>
/// Gets or sets this specifies the password that will be set for the MariaDB root superuser account.
/// </summary>
public string RootPassword
{
get => this.Environments["MARIADB_ROOT_PASSWORD"];
set => this.Environments["MARIADB_ROOT_PASSWORD"] = value;
}

/// <inheritdoc />
public override IWaitForContainerOS WaitStrategy => Wait.ForUnixContainer()
.UntilCommandIsCompleted("mysql", "--host=localhost", $"--port={this.DefaultPort}", $"--user={this.Username}", $"--password={this.Password}", "--protocol=TCP", "--execute=SHOW DATABASES");
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <inheritdoc cref="TestcontainerDatabase" />
[PublicAPI]
public sealed class MariaDbTestcontainer : TestcontainerDatabase
{
/// <summary>
/// Initializes a new instance of the <see cref="MariaDbTestcontainer" /> class.
/// </summary>
/// <param name="configuration">The Testcontainers configuration.</param>
/// <param name="logger">The logger.</param>
internal MariaDbTestcontainer(ITestcontainersConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
}

/// <inheritdoc />
public override string ConnectionString
=> $"Server={this.Hostname};Port={this.Port};Database={this.Database};Uid={this.Username};Pwd={this.Password};";

/// <summary>
/// Executes a SQL script in the database container.
/// </summary>
/// <param name="scriptContent">The content of the SQL script to be executed.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the script has been executed.</returns>
public override async Task<ExecResult> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<MariaDbTestcontainer, DbConnection>
{
private readonly TestcontainerDatabaseConfiguration configuration = new MariaDbTestcontainerConfiguration { Database = "db", Username = "mysql", Password = "mysql" };

public MariaDbFixture()
{
this.Container = new TestcontainersBuilder<MariaDbTestcontainer>()
.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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<MySqlTestcontainer, DbConnection>
{
Expand Down
2 changes: 1 addition & 1 deletion tests/Testcontainers.Tests/Testcontainers.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="coverlet.msbuild" />
<PackageReference Include="Moq" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Console" />
Expand All @@ -24,7 +25,6 @@
<PackageReference Include="CouchbaseNetClient" />
<PackageReference Include="MongoDB.Driver" />
<PackageReference Include="MyCouch" />
<PackageReference Include="MySql.Data" />
<PackageReference Include="Npgsql" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" />
<PackageReference Include="RabbitMQ.Client" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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<MariaDbFixture>
{
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);
}
}
}

0 comments on commit f5d471c

Please sign in to comment.