From 175fc429f8f2a6ada6a05d61ada4297c873a5e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 18 Aug 2025 16:29:01 +0200 Subject: [PATCH 1/5] Simplify tests by using Testcontainers.Xunit * EnsureConnectedAsync is not needed anymore, the `UntilDatabaseIsAvailable` wait strategy does exactly this. * Don't blindly swallow exceptions on EnsureCreatedAsync(), only do it for SQL Server where it actually happens and with the precise expected exception. * Use in-memory databases for SQLite so that the file system doesn't get polluted with test databases. * Switch from postgis/postgis to imresamu/postgis:17-3.5 in order to support Apple Silicon Macs. * Use the new `Platform` feature of Testconainers 4.10.0 in order to support vibs2006/sql_server_fts on Apple Silicon Macs. --- ...yFrameworkCore.BulkInsert.Benchmark.csproj | 8 +- .../Providers/LibComparatorMySql.cs | 2 +- .../Providers/LibComparatorOracle.cs | 3 +- .../Providers/LibComparatorPostgreSql.cs | 2 +- .../Providers/LibComparatorSqlServer.cs | 2 +- .../DbContainer/IDbContextFactory.cs | 9 ++ .../DbContainer/TestDbContainer.cs | 83 ++++++------------- .../DbContainer/TestDbContainerMySql.cs | 35 ++++---- .../DbContainer/TestDbContainerOracle.cs | 31 ++----- .../DbContainer/TestDbContainerPostgreSql.cs | 29 ++----- .../DbContainer/TestDbContainerSqlServer.cs | 34 ++++---- .../DbContainer/TestDbContainerSqlite.cs | 33 ++++---- .../DbContext/TestDbContextBase.cs | 4 +- ...ntityFrameworkCore.BulkInsert.Tests.csproj | 15 ++-- .../Tests/Basic/BasicTestsBase.cs | 2 +- .../Tests/Geo/GeoTestsBase.cs | 2 +- .../Tests/Merge/MergeTestsBase.cs | 2 +- .../Tests/Various/VariousTestsBase.cs | 2 +- 18 files changed, 134 insertions(+), 164 deletions(-) create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/IDbContextFactory.cs diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj index b359d6d..cff5d4e 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorMySql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorMySql.cs index 9504eb5..03ecdd8 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorMySql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorMySql.cs @@ -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(); } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorOracle.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorOracle.cs index b6eb92f..24b412e 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorOracle.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorOracle.cs @@ -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(); } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorPostgreSql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorPostgreSql.cs index 3bb3595..2cf7410 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorPostgreSql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorPostgreSql.cs @@ -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") diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorSqlServer.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorSqlServer.cs index 8db3e14..769a825 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorSqlServer.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/Providers/LibComparatorSqlServer.cs @@ -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(); } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/IDbContextFactory.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/IDbContextFactory.cs new file mode 100644 index 0000000..f295a84 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/IDbContextFactory.cs @@ -0,0 +1,9 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; + +public interface IDbContextFactory +{ + Task CreateContextAsync(string databaseName) + where TDbContext : TestDbContextBase, new(); +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainer.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainer.cs index ac63439..8c396cd 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainer.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainer.cs @@ -1,5 +1,8 @@ using System.Data.Common; +using System.Reflection; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; using Microsoft.EntityFrameworkCore; @@ -7,47 +10,42 @@ 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(IMessageSink messageSink) : DbContainerFixture(messageSink), IDbContextFactory + where TBuilderEntity : IContainerBuilder, new() + where TContainerEntity : IContainer, IDatabaseContainer { - private readonly TimeSpan _waitTime = TimeSpan.FromSeconds(30); - private readonly HashSet _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().FirstOrDefault(e => e.Key == "TargetFramework")?.Value ?? "NA"; + return CreateBuilder() + .WithReuse(true) + .WithName($"PhenX.EntityFrameworkCore.BulkInsert.Tests.{DbmsName}-{targetFramework}") + .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 CreateContextAsync(string databaseName) @@ -62,39 +60,8 @@ public async Task CreateContextAsync(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; } - - protected virtual async Task EnsureConnectedAsync(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(); - } - } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs index 904c616..8dcdfb4 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs @@ -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; @@ -16,16 +21,22 @@ public class TestDbContainerMySqlCollection : ICollectionFixture(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}"); + + 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) @@ -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 context, string databaseName) - { - var container = (MySqlContainer)DbContainer!; - - await container.ExecScriptAsync($"CREATE DATABASE `{databaseName}`"); - await base.EnsureConnectedAsync(context, databaseName); - } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerOracle.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerOracle.cs index 0ccbbaa..6e6d765 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerOracle.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerOracle.cs @@ -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; @@ -16,32 +19,16 @@ public class TestDbContainerOracleCollection : ICollectionFixture(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"; - } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs index b03a7cc..95e7835 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs @@ -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; @@ -16,18 +19,12 @@ public class TestDbContainerPostgreSqlCollection : ICollectionFixture(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) + protected override PostgreSqlBuilder CreateBuilder() => new("imresamu/postgis:17-3.5"); protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName) { @@ -38,12 +35,4 @@ protected override void Configure(DbContextOptionsBuilder optionsBuilder, string }) .UseBulkInsertPostgreSql(); } - - protected override async Task EnsureConnectedAsync(TDbContext context, string databaseName) - { - var container = (PostgreSqlContainer)DbContainer!; - - await container.ExecScriptAsync($"CREATE DATABASE \"{databaseName}\""); - await base.EnsureConnectedAsync(context, databaseName); - } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlServer.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlServer.cs index a11f7ac..b6fdd11 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlServer.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlServer.cs @@ -1,5 +1,8 @@ -using DotNet.Testcontainers.Containers; +using System.Data.Common; +using DotNet.Testcontainers.Images; + +using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using PhenX.EntityFrameworkCore.BulkInsert.SqlServer; @@ -7,6 +10,7 @@ using Testcontainers.MsSql; using Xunit; +using Xunit.Abstractions; namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; @@ -16,15 +20,12 @@ public class TestDbContainerSqlServerCollection : ICollectionFixture(messageSink) { - protected override IDatabaseContainer? GetDbContainer() - { - return new MsSqlBuilder() - .WithImage("vibs2006/sql_server_fts") // Geo Geospatial support - .WithReuse(true) - .Build(); - } + public override DbProviderFactory DbProviderFactory => SqlClientFactory.Instance; + + // GeoSpatial support + protected override MsSqlBuilder CreateBuilder() => new(new DockerImage("vibs2006/sql_server_fts", new Platform("amd64"))); protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName) { @@ -36,11 +37,16 @@ protected override void Configure(DbContextOptionsBuilder optionsBuilder, string .UseBulkInsertSqlServer(); } - protected override async Task EnsureConnectedAsync(TDbContext context, string databaseName) + protected override async Task EnsureDatabaseCreatedAsync(Microsoft.EntityFrameworkCore.DbContext dbContext) { - var container = (MsSqlContainer)DbContainer!; - - await container.ExecScriptAsync($"CREATE DATABASE [{databaseName}]"); - await base.EnsureConnectedAsync(context, databaseName); + try + { + await base.EnsureDatabaseCreatedAsync(dbContext); + } + catch (SqlException ex) when (ex.Number == 1801) // Database '%.*ls' already exists. Choose a different database name. + { + // Ignore, it means that the database was already created in the (reused) container + // https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors-1000-to-1999 + } } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs index c0245c5..91be0b6 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs @@ -1,8 +1,8 @@ -using DotNet.Testcontainers.Containers; - +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using PhenX.EntityFrameworkCore.BulkInsert.Sqlite; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; using Xunit; @@ -14,24 +14,29 @@ public class TestDbContainerSqliteCollection : ICollectionFixture null; + private SqliteConnection? _connection; - protected override string GetConnectionString(string databaseName) + public async Task CreateContextAsync(string databaseName) where TDbContext : TestDbContextBase, new() { - return $"Data Source={Guid.NewGuid()}.db"; - } + _connection = new SqliteConnection("DataSource=:memory:"); + await _connection.OpenAsync(); - protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName) - { - optionsBuilder - .UseSqlite(GetConnectionString(databaseName)) - .UseBulkInsertSqlite(); + await using var attachCommand = new SqliteCommand($"ATTACH DATABASE ':memory:' as {databaseName}", _connection); + await attachCommand.ExecuteNonQueryAsync(); + + var dbContext = new TDbContext + { + ConfigureOptions = builder => builder.UseSqlite(_connection).UseBulkInsertSqlite(), + }; + await dbContext.Database.EnsureCreatedAsync(); + + return dbContext; } - protected override Task EnsureConnectedAsync(TDbContext context, string databaseName) + public void Dispose() { - return Task.CompletedTask; + _connection?.Dispose(); } } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContextBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContextBase.cs index a18ab68..ab17cec 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContextBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContextBase.cs @@ -4,7 +4,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; public abstract class TestDbContextBase : Microsoft.EntityFrameworkCore.DbContext { - public Action ConfigureOptions { get; init; } = null!; + public Action? ConfigureOptions { get; init; } - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => ConfigureOptions(optionsBuilder); + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => (ConfigureOptions ?? throw new InvalidOperationException("ConfigureOptions must be set")).Invoke(optionsBuilder); } diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj index 9ffca17..32533a4 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj @@ -8,6 +8,10 @@ true + + + + all @@ -25,11 +29,12 @@ - - - - - + + + + + + diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs index b09493c..d44781a 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs @@ -12,7 +12,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Basic; -public abstract class BasicTestsBase(TestDbContainer dbContainer) : IAsyncLifetime +public abstract class BasicTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs index 4fa047f..78665e5 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs @@ -11,7 +11,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Geo; -public abstract class GeoTestsBase(TestDbContainer dbContainer) : IAsyncLifetime +public abstract class GeoTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime where TDbContext : TestDbContextGeo, new() { private readonly Guid _run = Guid.NewGuid(); diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs index 49f02ec..a5275a7 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs @@ -10,7 +10,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Merge; -public abstract class MergeTestsBase(TestDbContainer dbContainer) : IAsyncLifetime +public abstract class MergeTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs index ca5724e..86a50c2 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs @@ -10,7 +10,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; -public abstract class VariousTestsBase(TestDbContainer dbContainer) : IAsyncLifetime +public abstract class VariousTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); From 0e0bdc6d2a53853cb1bc829ce71b8047d9b43bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 7 Feb 2026 14:35:10 +0100 Subject: [PATCH 2/5] Rename dbContainer into dbContextFactory, which is more accurate --- .../Tests/Basic/BasicTestsBase.cs | 4 ++-- .../Tests/Geo/GeoTestsBase.cs | 4 ++-- .../Tests/Merge/MergeTestsBase.cs | 4 ++-- .../Tests/Various/VariousTestsBase.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs index d44781a..ccf26d9 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Basic/BasicTestsBase.cs @@ -12,7 +12,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Basic; -public abstract class BasicTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime +public abstract class BasicTestsBase(IDbContextFactory dbContextFactory) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); @@ -20,7 +20,7 @@ public abstract class BasicTestsBase(IDbContextFactory dbContainer) public async Task InitializeAsync() { - _context = await dbContainer.CreateContextAsync("basic"); + _context = await dbContextFactory.CreateContextAsync("basic"); } public Task DisposeAsync() diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs index 78665e5..095d8f1 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Geo/GeoTestsBase.cs @@ -11,7 +11,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Geo; -public abstract class GeoTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime +public abstract class GeoTestsBase(IDbContextFactory dbContextFactory) : IAsyncLifetime where TDbContext : TestDbContextGeo, new() { private readonly Guid _run = Guid.NewGuid(); @@ -19,7 +19,7 @@ public abstract class GeoTestsBase(IDbContextFactory dbContainer) : public async Task InitializeAsync() { - _context = await dbContainer.CreateContextAsync("geo"); + _context = await dbContextFactory.CreateContextAsync("geo"); } public Task DisposeAsync() diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs index a5275a7..69661c6 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Merge/MergeTestsBase.cs @@ -10,7 +10,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Merge; -public abstract class MergeTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime +public abstract class MergeTestsBase(IDbContextFactory dbContextFactory) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); @@ -18,7 +18,7 @@ public abstract class MergeTestsBase(IDbContextFactory dbContainer) public async Task InitializeAsync() { - _context = await dbContainer.CreateContextAsync("basic"); + _context = await dbContextFactory.CreateContextAsync("basic"); } public Task DisposeAsync() diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs index 86a50c2..c1c21c7 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs @@ -10,7 +10,7 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; -public abstract class VariousTestsBase(IDbContextFactory dbContainer) : IAsyncLifetime +public abstract class VariousTestsBase(IDbContextFactory dbContextFactory) : IAsyncLifetime where TDbContext : TestDbContext, new() { private readonly Guid _run = Guid.NewGuid(); @@ -18,7 +18,7 @@ public abstract class VariousTestsBase(IDbContextFactory dbContainer public async Task InitializeAsync() { - _context = await dbContainer.CreateContextAsync("various"); + _context = await dbContextFactory.CreateContextAsync("various"); } public Task DisposeAsync() From f8c26a6e9f33c863c0ef45a876cf51807adc25be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 7 Feb 2026 14:37:40 +0100 Subject: [PATCH 3/5] Remove extra closing parenthesis at the end of the URL Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../DbContainer/TestDbContainerPostgreSql.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs index 95e7835..89592ec 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs @@ -23,7 +23,7 @@ public class TestDbContainerPostgreSql(IMessageSink messageSink) : TestDbContain { 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) + // GeoSpatial support, using imresamu/postgis instead of postgis/postgis for arm64 support, see https://github.com/postgis/docker-postgis/issues/216#issuecomment-2936824962 protected override PostgreSqlBuilder CreateBuilder() => new("imresamu/postgis:17-3.5"); protected override void Configure(DbContextOptionsBuilder optionsBuilder, string databaseName) From 74e0ced5ccceba3105c94c91d5299154753467ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 7 Feb 2026 14:38:47 +0100 Subject: [PATCH 4/5] Rename ServerVersion into MySqlServerVersion --- .../DbContainer/TestDbContainerMySql.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs index 8dcdfb4..6cabc1d 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs @@ -23,13 +23,13 @@ public class TestDbContainerMySqlCollection : ICollectionFixture(messageSink) { - private static readonly ServerVersion ServerVersion = ServerVersion.Create(new Version(8, 0), ServerType.MySql); + private static readonly ServerVersion MySqlServerVersion = ServerVersion.Create(new Version(8, 0), ServerType.MySql); public override DbProviderFactory DbProviderFactory => MySqlConnectorFactory.Instance; - protected override MySqlBuilder CreateBuilder() => new($"{ServerVersion.TypeIdentifier}:{ServerVersion.Version}"); + protected override MySqlBuilder CreateBuilder() => new($"{MySqlServerVersion.TypeIdentifier}:{MySqlServerVersion.Version}"); - protected override string DbmsName => ServerVersion.Type.ToString(); + protected override string DbmsName => MySqlServerVersion.Type.ToString(); protected override MySqlBuilder Configure() { @@ -49,7 +49,7 @@ protected override void Configure(DbContextOptionsBuilder optionsBuilder, string var connectionString = GetConnectionString(databaseName); optionsBuilder - .UseMySql(connectionString, ServerVersion, o => + .UseMySql(connectionString, MySqlServerVersion, o => { o.UseNetTopologySuite(); }) From 541442b2eed6c4701609809e206bb95d9a7e18a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 7 Feb 2026 14:41:45 +0100 Subject: [PATCH 5/5] Remove the ATTACH DATABASE command It's not even actually needed for SQLite tests --- .../DbContainer/TestDbContainerSqlite.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs index 91be0b6..24faf3f 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlite.cs @@ -23,9 +23,6 @@ public sealed class TestDbContainerSqlite : IDbContextFactory, IDisposable _connection = new SqliteConnection("DataSource=:memory:"); await _connection.OpenAsync(); - await using var attachCommand = new SqliteCommand($"ATTACH DATABASE ':memory:' as {databaseName}", _connection); - await attachCommand.ExecuteNonQueryAsync(); - var dbContext = new TDbContext { ConfigureOptions = builder => builder.UseSqlite(_connection).UseBulkInsertSqlite(),