Browse Source

Fix test fixtures: use EnsureCreated+Dialect.InitializeAsync and per-prefix migration history

Two bugs fixed in the EF Core test fixtures (PostgresFixture, MySqlFixture, SqlServerFixture):

1. Replace DatabaseMigrator with EnsureCreatedAsync + Dialect.InitializeAsync

   TestDbContext* are test-only contexts with no EF migration files, so
   DatabaseMigrator<TestDbContext*>.InitializeAsync called MigrateAsync which was
   a complete no-op — no tables were ever created and all integration tests failed
   with 'relation does not exist'.

   EnsureCreatedAsync builds the schema directly from the EF Core model, which is
   the correct approach for contexts without migrations. Dialect.InitializeAsync is
   then called explicitly to create the database-specific JSON functions (json_exists
   etc.) that EnsureCreated does not set up.

2. Add per-prefix MigrationsHistoryTable for named ContentDbContext registrations

   DynamicTables.PrepareAsync calls MigrateAsync on the named ContentDbContext
   (e.g. PostgresContentDbContext) to create per-app/schema dedicated tables such
   as '__c5_ContentsAll'. The migration (AddInitial) reads TableName.Prefix to
   build the table name at runtime.

   All named contexts shared the default '__EFMigrationsHistory' table, so after
   the first prefix ran AddInitial and recorded it, every subsequent prefix saw the
   migration as already applied and skipped it — leaving its dedicated tables
   uncreated and causing 'relation __cN_ContentsAll does not exist' failures in
   all but the first dedicated-table test.

   Setting options.MigrationsHistoryTable(\$"{name}MigrationHistory") gives each
   prefix its own independent migration history, so AddInitial runs once per prefix
   and creates the correct tables each time.

Also add *.lscache to .gitignore (C# language server cache files).
pull/1313/head
Joe de Ronde 3 months ago
parent
commit
b7b995f63a
  1. 3
      .gitignore
  2. 9
      backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs
  3. 13
      backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs
  4. 13
      backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs

3
.gitignore

@ -35,3 +35,6 @@ launchSettings.json
/frontend/app-config/localhost-key.pem /frontend/app-config/localhost-key.pem
/frontend/app-config/localhost.pem /frontend/app-config/localhost.pem
# C# language server cache
*.lscache

9
backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs

@ -59,13 +59,13 @@ public class MySqlFixture(string? reuseId = null) : IAsyncLifetime, ISqlContentF
builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options =>
{ {
options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically);
options.MigrationsHistoryTable($"{name}MigrationHistory");
}); });
builder.ConfigureWarnings(w => builder.ConfigureWarnings(w =>
w.Ignore(RelationalEventId.PendingModelChangesWarning)); w.Ignore(RelationalEventId.PendingModelChangesWarning));
}) })
.AddSingleton<ConnectionStringParser, MySqlConnectionStringParser>() .AddSingleton<ConnectionStringParser, MySqlConnectionStringParser>()
.AddSingleton<DatabaseMigrator<TestDbContextMySql>>()
.AddSingleton(TestUtils.DefaultSerializer) .AddSingleton(TestUtils.DefaultSerializer)
.BuildServiceProvider(); .BuildServiceProvider();
@ -74,7 +74,12 @@ public class MySqlFixture(string? reuseId = null) : IAsyncLifetime, ISqlContentF
await service.InitializeAsync(default); await service.InitializeAsync(default);
} }
await services.GetRequiredService<DatabaseMigrator<TestDbContextMySql>>().InitializeAsync(default); await using var dbContext = await services.GetRequiredService<IDbContextFactory<TestDbContextMySql>>().CreateDbContextAsync();
await dbContext.Database.EnsureCreatedAsync();
if (dbContext is IDbContextWithDialect withDialect)
{
await withDialect.Dialect.InitializeAsync(dbContext, default);
}
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()

13
backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs

@ -54,13 +54,15 @@ public class PostgresFixture(string? reuseId) : IAsyncLifetime, ISqlContentFixtu
.AddNamedDbContext<PostgresContentDbContext>((builder, name) => .AddNamedDbContext<PostgresContentDbContext>((builder, name) =>
{ {
builder.UseBulkInsertPostgreSql(); builder.UseBulkInsertPostgreSql();
builder.UseNpgsql(connectionString); builder.UseNpgsql(connectionString, options =>
{
options.MigrationsHistoryTable($"{name}MigrationHistory");
});
builder.ConfigureWarnings(w => builder.ConfigureWarnings(w =>
w.Ignore(RelationalEventId.PendingModelChangesWarning)); w.Ignore(RelationalEventId.PendingModelChangesWarning));
}) })
.AddSingleton<ConnectionStringParser, PostgresConnectionStringParser>() .AddSingleton<ConnectionStringParser, PostgresConnectionStringParser>()
.AddSingleton<DatabaseMigrator<TestDbContextPostgres>>()
.AddSingleton(TestUtils.DefaultSerializer) .AddSingleton(TestUtils.DefaultSerializer)
.BuildServiceProvider(); .BuildServiceProvider();
@ -69,7 +71,12 @@ public class PostgresFixture(string? reuseId) : IAsyncLifetime, ISqlContentFixtu
await service.InitializeAsync(default); await service.InitializeAsync(default);
} }
await services.GetRequiredService<DatabaseMigrator<TestDbContextPostgres>>().InitializeAsync(default); await using var dbContext = await services.GetRequiredService<IDbContextFactory<TestDbContextPostgres>>().CreateDbContextAsync();
await dbContext.Database.EnsureCreatedAsync();
if (dbContext is IDbContextWithDialect withDialect)
{
await withDialect.Dialect.InitializeAsync(dbContext, default);
}
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()

13
backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs

@ -56,13 +56,15 @@ public class SqlServerFixture(string? reuseId = null) : IAsyncLifetime, ISqlCont
.AddNamedDbContext<SqlServerContentDbContext>((builder, name) => .AddNamedDbContext<SqlServerContentDbContext>((builder, name) =>
{ {
builder.UseBulkInsertSqlServer(); builder.UseBulkInsertSqlServer();
builder.UseSqlServer(connectionString); builder.UseSqlServer(connectionString, options =>
{
options.MigrationsHistoryTable($"{name}MigrationHistory");
});
builder.ConfigureWarnings(w => builder.ConfigureWarnings(w =>
w.Ignore(RelationalEventId.PendingModelChangesWarning)); w.Ignore(RelationalEventId.PendingModelChangesWarning));
}) })
.AddSingleton<ConnectionStringParser, SqlServerConnectionStringParser>() .AddSingleton<ConnectionStringParser, SqlServerConnectionStringParser>()
.AddSingleton<DatabaseMigrator<TestDbContextSqlServer>>()
.AddSingleton(TestUtils.DefaultSerializer) .AddSingleton(TestUtils.DefaultSerializer)
.BuildServiceProvider(); .BuildServiceProvider();
@ -71,7 +73,12 @@ public class SqlServerFixture(string? reuseId = null) : IAsyncLifetime, ISqlCont
await service.InitializeAsync(default); await service.InitializeAsync(default);
} }
await services.GetRequiredService<DatabaseMigrator<TestDbContextSqlServer>>().InitializeAsync(default); await using var dbContext = await services.GetRequiredService<IDbContextFactory<TestDbContextSqlServer>>().CreateDbContextAsync();
await dbContext.Database.EnsureCreatedAsync();
if (dbContext is IDbContextWithDialect withDialect)
{
await withDialect.Dialect.InitializeAsync(dbContext, default);
}
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()

Loading…
Cancel
Save