using Microsoft.Data.SqlClient; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.SqlServer.IntegrationTests; /// /// Env-gated throwaway database for the SQL historian backend. HISTORIAN_SQL_TEST_CONNECTION absent ⇒ /// is set, nothing connects, and every test calls Assert.Skip. Present ⇒ /// creates OtOpcUaHistorian_T_<guid> (never touches another database) and drops it on dispose. /// Tests isolate from each other with . /// public sealed class SqlHistorianFixture : IAsyncLifetime { /// Env var holding a connection string with CREATE/DROP DATABASE rights. public const string ConnectionEnvVar = "HISTORIAN_SQL_TEST_CONNECTION"; private string? _master; private string? _dbName; /// Why tests must skip, or null when the database is available. public string? SkipReason { get; private set; } /// Connection string targeting the throwaway database. Valid only when is null. public string ConnectionString { get; private set; } = ""; /// Skips the calling test when the gate is closed. public void SkipIfUnavailable() { if (SkipReason is not null) Assert.Skip(SkipReason); } /// A fresh, valid schema name (nothing exists under it yet). /// A schema identifier unique to this call. public static string UniqueSchema() => "h" + Guid.NewGuid().ToString("N")[..16]; /// public async ValueTask InitializeAsync() { var cs = Environment.GetEnvironmentVariable(ConnectionEnvVar); if (string.IsNullOrWhiteSpace(cs)) { SkipReason = $"{ConnectionEnvVar} is not set; SQL Server historian integration tests skipped."; return; } var b = new SqlConnectionStringBuilder(cs) { InitialCatalog = "master" }; _master = b.ConnectionString; _dbName = "OtOpcUaHistorian_T_" + Guid.NewGuid().ToString("N"); await ExecAsync(_master, $"CREATE DATABASE [{_dbName}]").ConfigureAwait(false); b.InitialCatalog = _dbName; ConnectionString = b.ConnectionString; } /// public async ValueTask DisposeAsync() { if (_master is null || _dbName is null) return; SqlConnection.ClearAllPools(); await ExecAsync(_master, $"ALTER DATABASE [{_dbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [{_dbName}]").ConfigureAwait(false); } private static async Task ExecAsync(string cs, string sql) { await using var c = new SqlConnection(cs); await c.OpenAsync().ConfigureAwait(false); await using var cmd = c.CreateCommand(); cmd.CommandText = sql; await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); } } /// Shares one throwaway database across the integration tests. [CollectionDefinition(Name)] public sealed class SqlHistorianCollection : ICollectionFixture { /// Collection name. public const string Name = "SqlHistorian"; }