189 lines
6.3 KiB
C#
189 lines
6.3 KiB
C#
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 IContainer _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();
|
||
|
||
await _container.StartAsync();
|
||
|
||
// No database specified: we’ll 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);
|
||
}
|
||
} |