using Microsoft.Data.Sqlite; using Microsoft.Extensions.Logging.Abstractions; using Moq; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services; using ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts; namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Scripts; public class AuditingDbTransactionTests { [Theory] [InlineData(false)] [InlineData(true)] public async Task SR11_TransactionConnectionAndCommandReadbackRemainAudited(bool asynchronous) { var inner = new SqliteConnection("Data Source=:memory:"); await inner.OpenAsync(); using (var seed = inner.CreateCommand()) { seed.CommandText = "CREATE TABLE t (value INTEGER); INSERT INTO t VALUES (0)"; seed.ExecuteNonQuery(); } var writer = new Mock(); writer.Setup(w => w.WriteAsync(It.IsAny(), It.IsAny())) .Returns(Task.CompletedTask); await using var connection = new AuditingDbConnection(inner, writer.Object, "db", "site", "instance", "script", NullLogger.Instance, Guid.NewGuid()); await using var transaction = asynchronous ? await connection.BeginTransactionAsync() : connection.BeginTransaction(); Assert.Same(connection, transaction.Connection); using var command = transaction.Connection!.CreateCommand(); Assert.Same(connection, command.Connection); command.Transaction = transaction; Assert.Same(transaction, command.Transaction); Assert.Same(connection, command.Transaction!.Connection); command.CommandText = "UPDATE t SET value = 42"; Assert.Equal(1, asynchronous ? await command.ExecuteNonQueryAsync() : command.ExecuteNonQuery()); if (asynchronous) await transaction.CommitAsync(); else transaction.Commit(); writer.Verify(w => w.WriteAsync(It.IsAny(), It.IsAny()), Times.Once); using var check = inner.CreateCommand(); check.CommandText = "SELECT value FROM t"; Assert.Equal(42L, check.ExecuteScalar()); } [Fact] public void SR11_CommandConnectionCannotExposeProvider() { using var inner = new SqliteConnection("Data Source=:memory:"); using var connection = new AuditingDbConnection(inner, Mock.Of(), "db", "site", "instance", "script", NullLogger.Instance, Guid.NewGuid()); using var command = connection.CreateCommand(); Assert.Same(connection, command.Connection); } [Fact] public void SR4_ConnectionCannotBeRetargetedOrRevealCredentials() { using var inner = new SqliteConnection("Data Source=:memory:;Password=secret-value"); using var connection = new AuditingDbConnection(inner, Mock.Of(), "MES-Prod", "site", "instance", "script", NullLogger.Instance, Guid.NewGuid()); Assert.DoesNotContain("secret-value", connection.ConnectionString); Assert.Throws(() => connection.ConnectionString = "Data Source=other.db"); Assert.Throws(() => connection.ChangeDatabase("other")); Assert.Equal("Data Source=:memory:;Password=secret-value", inner.ConnectionString); } }