w4c-workflows-api/w4c-workflows-api.Tests/WorkflowsPostgresFixture.cs
2026-09-09 16:09:48 +03:00

87 lines
3.7 KiB
C#

using Microsoft.EntityFrameworkCore;
using Testcontainers.PostgreSql;
using w4c_workflows.Data;
using Xunit;
namespace w4c_workflows.Tests;
[CollectionDefinition("WorkflowsPostgres")]
public class WorkflowsPostgresCollection : ICollectionFixture<WorkflowsPostgresFixture>
{
}
/// <summary>
/// One Postgres 17 container per collection; applies the EF Core migration once
/// and hands out fresh (scoped-like) DbContext instances per test.
/// </summary>
public sealed class WorkflowsPostgresFixture : IAsyncLifetime
{
private PostgreSqlContainer? _container;
private string _connectionString = "";
public async Task InitializeAsync()
{
_container = new PostgreSqlBuilder()
.WithImage("postgres:17-alpine")
.WithDatabase("w4c_workflows_test")
.WithUsername("w4c")
.WithPassword("w4c_test_pw")
.Build();
await _container.StartAsync();
_connectionString = _container.GetConnectionString() + ";Timezone=UTC";
await using var context = CreateContext();
await context.Database.MigrateAsync();
// Mirror the production startup ordering (Program.cs): the add-on columns for
// W7 (ArchivedAt) and S8 (Server) are applied by idempotent raw SQL AFTER the
// EF migrations — no new EF migration is introduced (the project's house style).
await context.Database.ExecuteSqlRawAsync(
"ALTER TABLE workflows.\"Tasks\" ADD COLUMN IF NOT EXISTS \"ArchivedAt\" timestamptz NULL;");
await context.Database.ExecuteSqlRawAsync(
"ALTER TABLE workflows.\"Tasks\" ADD COLUMN IF NOT EXISTS \"Server\" text NULL;");
// Workflow repo scoping: the repo name column + per-tenant repo setting table.
await context.Database.ExecuteSqlRawAsync(
"ALTER TABLE workflows.\"Workflows\" ADD COLUMN IF NOT EXISTS \"Repo\" text NOT NULL DEFAULT 'workflows';");
await context.Database.ExecuteSqlRawAsync(
"CREATE TABLE IF NOT EXISTS workflows.\"WorkflowRepos\" (" +
"\"TenantId\" text NOT NULL, \"RepoName\" text NOT NULL, \"UpdatedAt\" timestamptz NULL, " +
"CONSTRAINT \"PK_WorkflowRepos\" PRIMARY KEY (\"TenantId\"));");
// Workflow runtime registry (mirrors Program.cs startup ordering).
await context.Database.ExecuteSqlRawAsync(
"CREATE TABLE IF NOT EXISTS workflows.\"WorkflowRuntimes\" (" +
"\"Id\" uuid NOT NULL, " +
"\"TenantId\" text NOT NULL, " +
"\"Label\" text NOT NULL, " +
"\"Kind\" text NOT NULL, " +
"\"Endpoint\" text NULL, " +
"\"ApiKeyHash\" text NULL, " +
"\"SecretHash\" text NULL, " +
"\"Status\" text NOT NULL DEFAULT 'unknown', " +
"\"LastSeenAt\" timestamptz NULL, " +
"\"LastError\" text NULL, " +
"\"InfoJson\" jsonb NULL, " +
"\"IsDefault\" boolean NOT NULL DEFAULT FALSE, " +
"\"CreatedAt\" timestamptz NOT NULL, " +
"\"UpdatedAt\" timestamptz NOT NULL, " +
"CONSTRAINT \"PK_WorkflowRuntimes\" PRIMARY KEY (\"Id\"));");
await context.Database.ExecuteSqlRawAsync(
"CREATE INDEX IF NOT EXISTS \"IX_WorkflowRuntimes_TenantId\" ON workflows.\"WorkflowRuntimes\" (\"TenantId\");");
}
public async Task DisposeAsync()
{
if (_container is not null)
await _container.DisposeAsync();
}
public WorkflowsDbContext CreateContext()
{
var options = new DbContextOptionsBuilder<WorkflowsDbContext>()
.UseNpgsql(_connectionString)
.Options;
return new WorkflowsDbContext(options);
}
}