Public Access
Make the end-to-end suite self-contained with Testcontainers
It needed a hand-started stack and an opt-in flag, so it ran on one machine and never in CI. It now brings up PostgreSQL, Keycloak and an OpenSSH server itself, applies the committed migrations and starts the API as a child process, which makes it part of the ordinary test run at ~25s. The API runs as a process rather than through WebApplicationFactory. The client builds its own HttpClient for a URL the user typed, so there is no seam to hand a test handler through without inventing one that exists only for tests — and a test host would replace the entry point, Kestrel and the content root, so it would never prove that Program.cs composes or that the committed appsettings is found and layered in the documented order. Running out of the API's own output directory is what makes its configuration real. The suite still consumes what ships: the realm file from deploy/keycloak, the EF migrations, the API's own appsettings. Only Oidc:Authority is overridden, because the container's port is assigned at start. Falsified by reintroducing the wildcard-port redirect URI the realm once had — Keycloak rejects the authorization request and the suite fails at sign-in, which is what proves the committed file is the one imported. Skipping the migration step likewise fails, and the failure names the pending migration. A fresh Keycloak per run also sidesteps the --import-realm trap: editing the realm file and rerunning now always tests the edit. DodoDbContextFactory gains a Create(connectionString) so the fixture and dotnet ef place the migrations history table in exactly one place. If they disagreed the API would report every migration pending, which is how the readiness gate catches it.
This commit is contained in:
@@ -7,18 +7,36 @@ namespace DodoSSH.Infrastructure;
|
||||
/// Builds a context for <c>dotnet ef</c> at design time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deliberately independent of the API host: generating a migration should not require the web
|
||||
/// application to start, nor a reachable database. The connection string here is never used to
|
||||
/// connect — only to select the provider so the model can be built.
|
||||
/// Deliberately independent of the API host: generating or applying a migration should not require the web
|
||||
/// application to start. The API never migrates in production — it fails readiness instead — so this is the
|
||||
/// one place that knows how to open the database without a web host, and the end-to-end suite uses it for
|
||||
/// the same reason <c>dotnet ef</c> does.
|
||||
/// </remarks>
|
||||
public sealed class DodoDbContextFactory : IDesignTimeDbContextFactory<DodoDbContext>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DodoDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var connectionString = Environment.GetEnvironmentVariable("DODOSSH_DESIGN_CONNECTION")
|
||||
?? "Host=localhost;Database=dodossh_design;Username=postgres";
|
||||
/// <summary>Environment variable naming the database to work against.</summary>
|
||||
/// <remarks>
|
||||
/// Only <c>database update</c> and <c>dbcontext script</c> actually connect. Generating a migration
|
||||
/// needs a provider but not a reachable server, which is why there is a usable default at all.
|
||||
/// </remarks>
|
||||
public const string ConnectionVariable = "DODOSSH_DESIGN_CONNECTION";
|
||||
|
||||
/// <inheritdoc />
|
||||
public DodoDbContext CreateDbContext(string[] args) =>
|
||||
Create(Environment.GetEnvironmentVariable(ConnectionVariable)
|
||||
?? "Host=localhost;Database=dodossh_design;Username=postgres");
|
||||
|
||||
/// <summary>
|
||||
/// Builds a context for one database, configured as the API configures its own.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">Where to connect.</param>
|
||||
/// <remarks>
|
||||
/// The migrations history table has to be placed exactly where <c>AddDodoPersistence</c> looks for it.
|
||||
/// Defined here once so that a schema applied outside the API is a schema the API agrees is current,
|
||||
/// rather than one it reports as entirely pending.
|
||||
/// </remarks>
|
||||
public static DodoDbContext Create(string connectionString)
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<DodoDbContext>()
|
||||
.UseNpgsql(connectionString, npgsql =>
|
||||
npgsql.MigrationsHistoryTable("__EFMigrationsHistory", DodoDbContext.SchemaName))
|
||||
|
||||
Reference in New Issue
Block a user