Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.7.0" />
<PackageReference Include="Testcontainers.MsSql" Version="4.7.0" />
<PackageReference Include="Testcontainers.MySql" Version="4.7.0" />
<PackageReference Include="Testcontainers.Oracle" Version="4.7.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.10.0" />
<PackageReference Include="Testcontainers.MsSql" Version="4.10.0" />
<PackageReference Include="Testcontainers.MySql" Version="4.10.0" />
<PackageReference Include="Testcontainers.Oracle" Version="4.10.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected override void ConfigureDbContext()

protected override IDatabaseContainer? GetDbContainer()
{
return new MySqlBuilder()
return new MySqlBuilder("mysql:8.0")
.WithCommand("--log-bin-trust-function-creators=1", "--local-infile=1")
.Build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ protected override void ConfigureDbContext()

protected override IDatabaseContainer? GetDbContainer()
{
return new OracleBuilder()
.WithImage("gvenzl/oracle-free:23-slim-faststart")
return new OracleBuilder("gvenzl/oracle-free:23-slim-faststart")
.Build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected override void ConfigureDbContext()

protected override IDatabaseContainer? GetDbContainer()
{
return new PostgreSqlBuilder()
return new PostgreSqlBuilder("postgres:15.1")
.WithDatabase("testdb")
.WithUsername("testuser")
.WithPassword("testpassword")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ protected override void ConfigureDbContext()

protected override IDatabaseContainer? GetDbContainer()
{
return new MsSqlBuilder().Build();
return new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer;

public interface IDbContextFactory
{
Task<TDbContext> CreateContextAsync<TDbContext>(string databaseName)
where TDbContext : TestDbContextBase, new();
}
Original file line number Diff line number Diff line change
@@ -1,53 +1,51 @@
using System.Data.Common;
using System.Reflection;

using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;

using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext;

using Xunit;
using Testcontainers.Xunit;

using Xunit.Abstractions;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer;

public abstract class TestDbContainer : IAsyncLifetime
public abstract class TestDbContainer<TBuilderEntity, TContainerEntity>(IMessageSink messageSink) : DbContainerFixture<TBuilderEntity, TContainerEntity>(messageSink), IDbContextFactory
where TBuilderEntity : IContainerBuilder<TBuilderEntity, TContainerEntity, IContainerConfiguration>, new()
where TContainerEntity : IContainer, IDatabaseContainer
{
private readonly TimeSpan _waitTime = TimeSpan.FromSeconds(30);
private readonly HashSet<string> _connected = [];
protected readonly IDatabaseContainer? DbContainer;
protected abstract void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName);

protected abstract TBuilderEntity CreateBuilder();

protected virtual string DbmsName => typeof(TContainerEntity).Name.Replace("Container", "");

protected TestDbContainer()
protected override TBuilderEntity Configure()
{
DbContainer = GetDbContainer();
var targetFramework = GetType().Assembly.GetCustomAttributes<AssemblyMetadataAttribute>().FirstOrDefault(e => e.Key == "TargetFramework")?.Value ?? "NA";
return CreateBuilder()
.WithReuse(true)
.WithName($"PhenX.EntityFrameworkCore.BulkInsert.Tests.{DbmsName}-{targetFramework}")
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea for the custom name !

.WithWaitStrategy(Wait.ForUnixContainer().UntilDatabaseIsAvailable(DbProviderFactory));
}

protected abstract IDatabaseContainer? GetDbContainer();

protected virtual string GetConnectionString(string databaseName)
{
if (DbContainer == null)
{
return string.Empty;
}

var builder = new DbConnectionStringBuilder()
{
ConnectionString = DbContainer.GetConnectionString()
};

var builder = DbProviderFactory.CreateConnectionStringBuilder() ?? new DbConnectionStringBuilder();
builder.ConnectionString = ConnectionString;
builder["database"] = databaseName;
return builder.ToString();
}

protected abstract void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName);

public async Task InitializeAsync()
protected virtual async Task EnsureDatabaseCreatedAsync(Microsoft.EntityFrameworkCore.DbContext dbContext)
{
if (DbContainer != null)
{
await DbContainer.StartAsync();
}
await dbContext.Database.EnsureCreatedAsync();
}

public async Task<TDbContext> CreateContextAsync<TDbContext>(string databaseName)
Expand All @@ -62,39 +60,8 @@ public async Task<TDbContext> CreateContextAsync<TDbContext>(string databaseName
}
};

if (_connected.Add(databaseName))
{
await EnsureConnectedAsync(dbContext, databaseName);
}

try
{
await dbContext.Database.EnsureCreatedAsync();
}
catch
{
// Often fails with SQL server.
}
await EnsureDatabaseCreatedAsync(dbContext);

return dbContext;
Comment thread
PhenX marked this conversation as resolved.
}

protected virtual async Task EnsureConnectedAsync<TDbContext>(TDbContext context, string databaseName)
where TDbContext : TestDbContextBase
{
using var cts = new CancellationTokenSource(_waitTime);

while (!await context.Database.CanConnectAsync(cts.Token))
{
await Task.Delay(100, cts.Token);
}
}

public async Task DisposeAsync()
{
if (DbContainer != null)
{
await DbContainer.DisposeAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
using DotNet.Testcontainers.Containers;
using System.Data.Common;

using Microsoft.EntityFrameworkCore;

using MySqlConnector;

using PhenX.EntityFrameworkCore.BulkInsert.MySql;

using Pomelo.EntityFrameworkCore.MySql.Infrastructure;

using Testcontainers.MySql;

using Xunit;
using Xunit.Abstractions;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer;

Expand All @@ -16,16 +21,22 @@ public class TestDbContainerMySqlCollection : ICollectionFixture<TestDbContainer
public const string Name = "MySql";
}

public class TestDbContainerMySql() : TestDbContainer
public class TestDbContainerMySql(IMessageSink messageSink) : TestDbContainer<MySqlBuilder, MySqlContainer>(messageSink)
{
protected override IDatabaseContainer? GetDbContainer()
private static readonly ServerVersion ServerVersion = ServerVersion.Create(new Version(8, 0), ServerType.MySql);

public override DbProviderFactory DbProviderFactory => MySqlConnectorFactory.Instance;

protected override MySqlBuilder CreateBuilder() => new($"{ServerVersion.TypeIdentifier}:{ServerVersion.Version}");
Comment thread
0xced marked this conversation as resolved.
Outdated

protected override string DbmsName => ServerVersion.Type.ToString();

protected override MySqlBuilder Configure()
{
return new MySqlBuilder()
return base.Configure()
.WithCommand("--log-bin-trust-function-creators=1", "--local-infile=1", "--innodb-print-all-deadlocks=ON")
.WithReuse(true)
.WithUsername("root")
.WithPassword("root")
.Build();
.WithPassword("root");
}

protected override string GetConnectionString(string databaseName)
Expand All @@ -38,18 +49,10 @@ protected override void Configure(DbContextOptionsBuilder optionsBuilder, string
var connectionString = GetConnectionString(databaseName);

optionsBuilder
.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), o =>
.UseMySql(connectionString, ServerVersion, o =>
{
o.UseNetTopologySuite();
})
.UseBulkInsertMySql();
}

protected override async Task EnsureConnectedAsync<TDbContext>(TDbContext context, string databaseName)
{
var container = (MySqlContainer)DbContainer!;

await container.ExecScriptAsync($"CREATE DATABASE `{databaseName}`");
await base.EnsureConnectedAsync(context, databaseName);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using DotNet.Testcontainers.Containers;
using System.Data.Common;

using Microsoft.EntityFrameworkCore;

using Oracle.ManagedDataAccess.Client;

using PhenX.EntityFrameworkCore.BulkInsert.Oracle;

using Testcontainers.Oracle;

using Xunit;
using Xunit.Abstractions;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer;

Expand All @@ -16,32 +19,16 @@ public class TestDbContainerOracleCollection : ICollectionFixture<TestDbContaine
public const string Name = "Oracle";
}

public class TestDbContainerOracle : TestDbContainer
public class TestDbContainerOracle(IMessageSink messageSink) : TestDbContainer<OracleBuilder, OracleContainer>(messageSink)
{
protected override IDatabaseContainer? GetDbContainer()
{
return new OracleBuilder()
.WithImage("gvenzl/oracle-free:23-slim-faststart")
.WithReuse(true)
.Build();
}
public override DbProviderFactory DbProviderFactory => OracleClientFactory.Instance;

protected override OracleBuilder CreateBuilder() => new("gvenzl/oracle-free:23-slim-faststart");

protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName)
{
optionsBuilder
.UseOracle(GetConnectionString(databaseName))
.UseOracle(ConnectionString)
.UseBulkInsertOracle();
}

protected override string GetConnectionString(string databaseName)
{
if (DbContainer == null)
{
return string.Empty;
}

var port = DbContainer.GetMappedPublicPort(1521);

return $"Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = {port})) ) (CONNECT_DATA = (SERVICE_NAME = FREEPDB1) ) );User ID=oracle;Password=oracle";
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using DotNet.Testcontainers.Containers;
using System.Data.Common;

using Microsoft.EntityFrameworkCore;

using Npgsql;

using PhenX.EntityFrameworkCore.BulkInsert.PostgreSql;

using Testcontainers.PostgreSql;

using Xunit;
using Xunit.Abstractions;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer;

Expand All @@ -16,18 +19,12 @@ public class TestDbContainerPostgreSqlCollection : ICollectionFixture<TestDbCont
public const string Name = "PostgreSql";
}

public class TestDbContainerPostgreSql : TestDbContainer
public class TestDbContainerPostgreSql(IMessageSink messageSink) : TestDbContainer<PostgreSqlBuilder, PostgreSqlContainer>(messageSink)
{
protected override IDatabaseContainer? GetDbContainer()
{
return new PostgreSqlBuilder()
.WithImage("postgis/postgis") // Geo GeoSpatial support.
.WithReuse(true)
.WithDatabase("testdb")
.WithUsername("testuser")
.WithPassword("testpassword")
.Build();
}
public override DbProviderFactory DbProviderFactory => NpgsqlFactory.Instance;

// GeoSpatial support, using imresamu/postgis instead of postgis/postgis for arm64 support, see https://github.com/postgis/docker-postgis/issues/216#issuecomment-2936824962)
Comment thread
0xced marked this conversation as resolved.
Outdated
protected override PostgreSqlBuilder CreateBuilder() => new("imresamu/postgis:17-3.5");

protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName)
{
Expand All @@ -38,12 +35,4 @@ protected override void Configure(DbContextOptionsBuilder optionsBuilder, string
})
.UseBulkInsertPostgreSql();
}

protected override async Task EnsureConnectedAsync<TDbContext>(TDbContext context, string databaseName)
{
var container = (PostgreSqlContainer)DbContainer!;

await container.ExecScriptAsync($"CREATE DATABASE \"{databaseName}\"");
await base.EnsureConnectedAsync(context, databaseName);
}
}
Loading
Loading