138 lines
3.7 KiB
C#
138 lines
3.7 KiB
C#
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Data;
|
|
using Docker.DotNet;
|
|
using Docker.DotNet.Models;
|
|
using LCU.API.Internal.DocumentIngestorLib.Persistence;
|
|
using Microsoft.Data.SqlClient;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace LCU.API.Internal.DocumentIngestorLibTests;
|
|
|
|
public class SqlServerDbContextFactory : IAsyncDisposable
|
|
{
|
|
private const int port = 1434;
|
|
private const string password = "yourStrong(!)Password";
|
|
|
|
private readonly IList<AppDbContext> _contexts = new List<AppDbContext>();
|
|
|
|
private static readonly SemaphoreSlim ContainerUse = new(1);
|
|
private static CreateContainerResponse? Container;
|
|
private static readonly IDictionary<AppDbContext, bool> _activeContexts = new ConcurrentDictionary<AppDbContext, bool>();
|
|
|
|
public async Task<AppDbContext> CreateContext()
|
|
{
|
|
await ContainerUse.WaitAsync();
|
|
Container ??= await DatabaseContainerFactory();
|
|
ContainerUse.Release();
|
|
|
|
var connectionString =
|
|
$"Data Source=localhost,{port};User Id=sa;Password={password};Database={Guid.NewGuid()};MultipleActiveResultSets=True;App=EntityFramework;Trust Server Certificate=true";
|
|
|
|
var options = new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseSqlServer(connectionString)
|
|
.EnableSensitiveDataLogging()
|
|
.Options;
|
|
|
|
var context = new AppDbContext(options);
|
|
await DatabaseConnectionReady(connectionString);
|
|
await context.Database.EnsureCreatedAsync();
|
|
|
|
_contexts.Add(context);
|
|
_activeContexts[context] = true;
|
|
return context;
|
|
}
|
|
|
|
private static async Task<CreateContainerResponse> DatabaseContainerFactory()
|
|
{
|
|
const string ImageName = "mcr.microsoft.com/mssql/server:2019-CU18-ubuntu-20.04";
|
|
const int containerPort = 1433;
|
|
|
|
using var docker = new DockerClientConfiguration().CreateClient();
|
|
|
|
await docker.Images.CreateImageAsync
|
|
(
|
|
new ImagesCreateParameters
|
|
{
|
|
FromImage = ImageName
|
|
},
|
|
null,
|
|
new Progress<JSONMessage>()
|
|
);
|
|
var container = await docker.Containers.CreateContainerAsync
|
|
(
|
|
new CreateContainerParameters
|
|
{
|
|
Image = ImageName,
|
|
Name = "LCU.API.Internal.DocumentIngestor.IntegrationTestDatabase",
|
|
Env = new List<string>
|
|
{
|
|
"ACCEPT_EULA=Y",
|
|
$"MSSQL_SA_PASSWORD={password}"
|
|
},
|
|
ExposedPorts = new Dictionary<string, EmptyStruct>
|
|
{
|
|
{$"{containerPort}", default}
|
|
},
|
|
HostConfig = new HostConfig
|
|
{
|
|
PortBindings = new Dictionary<string, IList<PortBinding>>
|
|
{
|
|
{ $"{containerPort}", new List<PortBinding> { new() { HostPort = $"{port}" } } }
|
|
}
|
|
}
|
|
}
|
|
);
|
|
await docker.Containers.StartContainerAsync(container.ID, new ContainerStartParameters());
|
|
|
|
return container;
|
|
}
|
|
|
|
private static async Task DatabaseConnectionReady(string connectionString)
|
|
{
|
|
await using var connection = new SqlConnection(connectionString);
|
|
|
|
var ready = false;
|
|
var triesRemaining = 20;
|
|
while (!ready && triesRemaining > 0)
|
|
{
|
|
triesRemaining--;
|
|
|
|
try
|
|
{
|
|
await connection.OpenAsync();
|
|
}
|
|
catch
|
|
{
|
|
await Task.Delay(TimeSpan.FromMilliseconds(200));
|
|
}
|
|
|
|
ready = connection.State == ConnectionState.Open;
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
foreach (var context in _contexts)
|
|
{
|
|
await context.DisposeAsync();
|
|
_activeContexts[context] = false;
|
|
}
|
|
|
|
await ContainerUse.WaitAsync();
|
|
if (_activeContexts.Values.All(a => a == false))
|
|
{
|
|
await DisposeContainer(Container);
|
|
Container = null;
|
|
}
|
|
ContainerUse.Release();
|
|
}
|
|
|
|
private static async Task DisposeContainer(CreateContainerResponse container)
|
|
{
|
|
using var docker = new DockerClientConfiguration().CreateClient();
|
|
|
|
await docker.Containers.StopContainerAsync(container.ID, new ContainerStopParameters());
|
|
await docker.Containers.RemoveContainerAsync(container.ID, new ContainerRemoveParameters());
|
|
}
|
|
} |