using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; using Microsoft.EntityFrameworkCore; using Testcontainers.MsSql; using Xunit; namespace Tests; public abstract class MsSqlTestBase : IAsyncLifetime { private static bool IsCiSystem => !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TF_BUILD")); protected MsSqlContainer MsSqlContainer { get; } = new MsSqlBuilder() .WithAutoRemove(true) .WithCleanUp(!IsCiSystem) .WithWaitStrategy( Wait.ForUnixContainer() .UntilMessageIsLogged("SQL Server is now ready for client connections") .AddCustomWaitStrategy(new WaitSqlCmdSuccess()) ) .Build(); public Task InitializeAsync() => MsSqlContainer.StartAsync(); public virtual async Task DisposeAsync() { try { await MsSqlContainer.StopAsync(); await MsSqlContainer.DisposeAsync(); } catch { // Ignore dispose errors } } public DbContextOptions CreateDbContextOptions() where TContext : DbContext => new DbContextOptionsBuilder().UseSqlServer(MsSqlContainer.GetConnectionString()).Options; /// /// Uses the sqlcmd utility scripting variables to detect readiness of the MsSql container: /// . /// private sealed class WaitSqlCmdSuccess : IWaitUntil { /// public Task UntilAsync(IContainer container) { return UntilAsync((MsSqlContainer)container); } /// private static async Task UntilAsync(MsSqlContainer container) { var sqlCmdFilePath = await container.GetSqlCmdFilePathAsync().ConfigureAwait(false); var execResult = await container .ExecAsync(new[] { sqlCmdFilePath, "-C", "-Q", "SELECT 1;" }) .ConfigureAwait(false); return 0L.Equals(execResult.ExitCode); } } }