119 lines
4.0 KiB
C#
119 lines
4.0 KiB
C#
using BuildFlow.Shared.Domain.Common;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
using Monitoring.Infrastructure.Persistence;
|
|
using Npgsql;
|
|
using Xunit;
|
|
|
|
namespace BuildFlow.Monitoring.Tests.Sync;
|
|
|
|
/// <summary>
|
|
/// 为 Monitoring 集成测试创建并销毁独立 PostgreSQL 数据库。
|
|
/// </summary>
|
|
public sealed class PostgresMonitoringFixture : IAsyncLifetime
|
|
{
|
|
private const string DefaultConnection =
|
|
"Host=127.0.0.1;Port=55432;Username=buildflow;Password=buildflow;Database=postgres";
|
|
|
|
private string _databaseName = default!;
|
|
private string _adminConnectionString = default!;
|
|
|
|
/// <summary>当前测试数据库连接串。</summary>
|
|
public string ConnectionString { get; private set; } = default!;
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
var configured = Environment.GetEnvironmentVariable("BUILDFLOW_MONITORING_TEST_CONNECTION")
|
|
?? DefaultConnection;
|
|
var adminBuilder = new NpgsqlConnectionStringBuilder(configured)
|
|
{
|
|
Database = "postgres",
|
|
Pooling = false,
|
|
};
|
|
_adminConnectionString = adminBuilder.ConnectionString;
|
|
_databaseName = $"monitoring_upsert_{Guid.NewGuid():N}";
|
|
|
|
await using (var connection = new NpgsqlConnection(_adminConnectionString))
|
|
{
|
|
await connection.OpenAsync();
|
|
await using var command = connection.CreateCommand();
|
|
command.CommandText = $"CREATE DATABASE \"{_databaseName}\"";
|
|
await command.ExecuteNonQueryAsync();
|
|
}
|
|
|
|
var databaseBuilder = new NpgsqlConnectionStringBuilder(configured)
|
|
{
|
|
Database = _databaseName,
|
|
Pooling = true,
|
|
};
|
|
ConnectionString = databaseBuilder.ConnectionString;
|
|
|
|
await using var db = CreateDbContext();
|
|
await db.Database.EnsureCreatedAsync();
|
|
}
|
|
|
|
/// <summary>创建指向独立测试数据库的 MonitoringDbContext。</summary>
|
|
public MonitoringDbContext CreateDbContext(params IInterceptor[] interceptors)
|
|
{
|
|
var optionsBuilder = new DbContextOptionsBuilder<MonitoringDbContext>()
|
|
.UseNpgsql(ConnectionString)
|
|
.UseSnakeCaseNamingConvention();
|
|
if (interceptors.Length > 0)
|
|
{
|
|
optionsBuilder.AddInterceptors(interceptors);
|
|
}
|
|
|
|
var db = new MonitoringDbContext(optionsBuilder.Options);
|
|
db.SavingChanges += FillAuditFields;
|
|
return db;
|
|
}
|
|
|
|
public async Task DisposeAsync()
|
|
{
|
|
NpgsqlConnection.ClearAllPools();
|
|
await using var connection = new NpgsqlConnection(_adminConnectionString);
|
|
await connection.OpenAsync();
|
|
|
|
await using (var terminate = connection.CreateCommand())
|
|
{
|
|
terminate.CommandText =
|
|
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = @database AND pid <> pg_backend_pid()";
|
|
terminate.Parameters.AddWithValue("database", _databaseName);
|
|
await terminate.ExecuteNonQueryAsync();
|
|
}
|
|
|
|
await using var drop = connection.CreateCommand();
|
|
drop.CommandText = $"DROP DATABASE IF EXISTS \"{_databaseName}\"";
|
|
await drop.ExecuteNonQueryAsync();
|
|
}
|
|
|
|
private static void FillAuditFields(object? sender, EventArgs _)
|
|
{
|
|
if (sender is not DbContext db)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var now = DateTime.UtcNow;
|
|
foreach (var entry in db.ChangeTracker.Entries())
|
|
{
|
|
if (entry.Entity is IAuditable auditable)
|
|
{
|
|
auditable.CreatedBy ??= "test";
|
|
auditable.UpdatedBy ??= "test";
|
|
if (auditable.CreatedAt == default)
|
|
{
|
|
auditable.CreatedAt = now;
|
|
}
|
|
|
|
auditable.UpdatedAt = now;
|
|
}
|
|
|
|
if (entry.Entity is IHasOperatorIP operatorEntity)
|
|
{
|
|
operatorEntity.OperatorIP ??= "127.0.0.1";
|
|
}
|
|
}
|
|
}
|
|
}
|