@@ -7,9 +7,11 @@ namespace Tiku.IntegrationTests.Infrastructure;
|
||||
internal sealed class PostgresTestDatabase : IDisposable
|
||||
{
|
||||
private const string DatabasePrefix = "tiku_it_";
|
||||
|
||||
private static readonly Lazy<PostgresTestDatabaseTemplate> Template = new(
|
||||
PostgresTestDatabaseTemplate.Create,
|
||||
LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
|
||||
private readonly string adminConnectionString;
|
||||
private bool disposed;
|
||||
|
||||
@@ -24,6 +26,28 @@ internal sealed class PostgresTestDatabase : IDisposable
|
||||
|
||||
public string ConnectionString { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
disposed = true;
|
||||
var builder = new NpgsqlConnectionStringBuilder(adminConnectionString);
|
||||
using var adminConnection = new NpgsqlConnection(builder.ConnectionString);
|
||||
adminConnection.Open();
|
||||
|
||||
using (var terminateConnections = adminConnection.CreateCommand())
|
||||
{
|
||||
terminateConnections.CommandText =
|
||||
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()";
|
||||
terminateConnections.Parameters.AddWithValue(DatabaseName);
|
||||
terminateConnections.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using var dropDatabase = adminConnection.CreateCommand();
|
||||
dropDatabase.CommandText = $"DROP DATABASE IF EXISTS {QuoteIdentifier(DatabaseName)}";
|
||||
dropDatabase.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public static PostgresTestDatabase Create()
|
||||
{
|
||||
var template = Template.Value;
|
||||
@@ -54,38 +78,11 @@ internal sealed class PostgresTestDatabase : IDisposable
|
||||
return database;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
var builder = new NpgsqlConnectionStringBuilder(adminConnectionString);
|
||||
using var adminConnection = new NpgsqlConnection(builder.ConnectionString);
|
||||
adminConnection.Open();
|
||||
|
||||
using (var terminateConnections = adminConnection.CreateCommand())
|
||||
{
|
||||
terminateConnections.CommandText =
|
||||
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()";
|
||||
terminateConnections.Parameters.AddWithValue(DatabaseName);
|
||||
terminateConnections.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using var dropDatabase = adminConnection.CreateCommand();
|
||||
dropDatabase.CommandText = $"DROP DATABASE IF EXISTS {QuoteIdentifier(DatabaseName)}";
|
||||
dropDatabase.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
internal static string QuoteIdentifier(string identifier)
|
||||
{
|
||||
if (!identifier.StartsWith(DatabasePrefix, StringComparison.Ordinal) ||
|
||||
identifier.Any(character => !char.IsAsciiLetterOrDigit(character) && character != '_'))
|
||||
{
|
||||
throw new InvalidOperationException("Refusing to use an unsafe integration-test database name.");
|
||||
}
|
||||
|
||||
return $"\"{identifier}\"";
|
||||
}
|
||||
@@ -106,16 +103,27 @@ internal sealed class PostgresTestDatabaseTemplate : IDisposable
|
||||
|
||||
public string DatabaseName { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
disposed = true;
|
||||
using var adminConnection = new NpgsqlConnection(AdminConnectionString);
|
||||
adminConnection.Open();
|
||||
using var dropDatabase = adminConnection.CreateCommand();
|
||||
dropDatabase.CommandText =
|
||||
$"DROP DATABASE IF EXISTS {PostgresTestDatabase.QuoteIdentifier(DatabaseName)}";
|
||||
dropDatabase.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public static PostgresTestDatabaseTemplate Create()
|
||||
{
|
||||
var adminConnectionString = Environment.GetEnvironmentVariable("TIKU_TEST_POSTGRES_ADMIN") ??
|
||||
$"Host=localhost;Database=postgres;Username={Environment.UserName};Pooling=false;Timeout=5;Command Timeout=60";
|
||||
var adminBuilder = new NpgsqlConnectionStringBuilder(adminConnectionString);
|
||||
if (!string.Equals(adminBuilder.Database, "postgres", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"TIKU_TEST_POSTGRES_ADMIN must target the postgres maintenance database.");
|
||||
}
|
||||
|
||||
var databaseName = $"{TemplatePrefix}{Environment.ProcessId}_{Guid.NewGuid():N}";
|
||||
var template = new PostgresTestDatabaseTemplate(adminBuilder.ConnectionString, databaseName);
|
||||
@@ -163,20 +171,4 @@ internal sealed class PostgresTestDatabaseTemplate : IDisposable
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
using var adminConnection = new NpgsqlConnection(AdminConnectionString);
|
||||
adminConnection.Open();
|
||||
using var dropDatabase = adminConnection.CreateCommand();
|
||||
dropDatabase.CommandText =
|
||||
$"DROP DATABASE IF EXISTS {PostgresTestDatabase.QuoteIdentifier(DatabaseName)}";
|
||||
dropDatabase.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user