Files
jsmr/JSMR.Tests/Fixtures/MariaDbFixture.cs
Brian Bicknell b06eadef1d
Some checks failed
ci / build-test (push) Has been cancelled
Updated MariaDbContainerFixture building process.
2025-11-02 09:34:25 -05:00

198 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using JSMR.Infrastructure.Data;
using JSMR.Tests.Fixtures;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;
using Testcontainers.MariaDb;
using Testcontainers.Xunit;
using Xunit.Sdk;
[assembly: AssemblyFixture(typeof(MariaDbContainerFixture))]
namespace JSMR.Tests.Fixtures;
public sealed class MariaDbContainerFixture : IAsyncLifetime
{
const int MajorVersion = 10;
const int MinorVersion = 11;
const int Build = 6;
private MariaDbContainer _container = default!;
public string RootConnectionString { get; private set; } = default!;
public async ValueTask InitializeAsync()
{
//_container = new ContainerBuilder()
// .WithImage("mariadb:11")
// .WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
// .WithPortBinding(3307, 3306)
// .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3306))
// .Build();
//_container = new ContainerBuilder()
// .WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
// .WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
// //.WithPortBinding(3307, 3306)
// .WithPortBinding(3306, assignRandomHostPort: true)
// .WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(3306))
// .Build();
_container = new MariaDbBuilder()
.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
.WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
.WithUsername("root")
.WithPassword("rootpw")
// no explicit port binding
.WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(3306))
.Build();
await _container.StartAsync();
// No database specified: well create per-test DBs
//RootConnectionString = "Server=127.0.0.1;Port=3307;User=root;Password=rootpw;SslMode=none;";
RootConnectionString = _container.GetConnectionString();
var port = _container.GetMappedPublicPort(3306);
//RootConnectionString = $"Server=127.0.0.1;Port={port};User=root;Password=rootpw;SslMode=none;";
}
public async ValueTask DisposeAsync() => await _container.DisposeAsync();
}
public class MariaDbFixture : IAsyncLifetime
{
const int MajorVersion = 10;
const int MinorVersion = 11;
const int Build = 6;
public MariaDbContainer? MariaDbContainer { get; private set; }
public string ConnectionString { get; private set; } = default!;
public async ValueTask InitializeAsync()
{
MariaDbContainer = new MariaDbBuilder()
.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
.Build();
await MariaDbContainer.StartAsync();
ConnectionString = MariaDbContainer.GetConnectionString();
await using AppDbContext context = CreateDbContext();
await context.Database.EnsureCreatedAsync();
//await context.Database.MigrateAsync(); // Testing
await OnInitializedAsync(context);
}
protected virtual Task OnInitializedAsync(AppDbContext context)
{
return Task.FromResult(Task.CompletedTask);
}
public async ValueTask DisposeAsync()
{
if (MariaDbContainer is not null)
{
await MariaDbContainer.StopAsync();
await MariaDbContainer.DisposeAsync();
}
GC.SuppressFinalize(this);
}
public AppDbContext CreateDbContext()
{
MySqlServerVersion serverVersion = new(new Version(MajorVersion, MinorVersion, Build));
DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
.UseMySql(ConnectionString, serverVersion,
o => o.EnableRetryOnFailure())
.EnableSensitiveDataLogging()
.Options;
return new AppDbContext(options);
}
public async Task ResetAsync()
{
await using AppDbContext context = CreateDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
}
}
[CollectionDefinition("db")]
public sealed class MariaDbCollection : ICollectionFixture<MariaDbContainerFixture> { }
//public class MariaDbAssemblyFixtureDefinition : IAssemblyFixture<MariaDbContainerFixture> { }
//[UsedImplicitly]
public sealed class MariaDbContainerFixture2(IMessageSink messageSink)
: ContainerFixture<MariaDbBuilder, MariaDbContainer>(messageSink)
{
const int MajorVersion = 10;
const int MinorVersion = 11;
const int Build = 6;
public string RootConnectionString => $"Server={Container.IpAddress};Port=3306;User=root;Password=rootpw;SslMode=none;";
protected override MariaDbBuilder Configure(MariaDbBuilder builder)
{
return builder.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
.WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
.WithPortBinding(3307, 3306)
//.WithPortBinding(3306, assignRandomHostPort: true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(3306));
}
}
public static class MariaTestDb
{
public static async Task<AppDbContext> CreateIsolatedAsync(string rootConnectionString, Func<AppDbContext, Task>? seed = null)
{
string databaseName = $"t_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}";
await CreateDatabaseAsync(rootConnectionString, databaseName);
MySqlConnectionStringBuilder connectionStringBuilder = new(rootConnectionString)
{
Database = databaseName
};
AppDbContext dbContext = CreateDbContext(connectionStringBuilder.ConnectionString);
await dbContext.Database.EnsureCreatedAsync();
if (seed != null)
await seed(dbContext);
return dbContext;
}
private static async Task CreateDatabaseAsync(string rootConnectionString, string databaseName)
{
await using MySqlConnection connection = new(rootConnectionString);
await connection.OpenAsync();
await using MySqlCommand command = connection.CreateCommand();
command.CommandText = $"CREATE DATABASE `{databaseName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;";
await command.ExecuteNonQueryAsync();
}
private static AppDbContext CreateDbContext(string connectionString)
{
DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString),
o => o.EnableRetryOnFailure())
.EnableSensitiveDataLogging()
.Options;
return new AppDbContext(options);
}
}