using System.Data; using System.Data.Common; using Omika.Data.Configuration; using Omika.Data.Connection; using Omika.Data.HfSql.Connection; namespace Omika.Tests.Connection; public class LicenseOleDbConnectionTests { private static LicenseConnectionOptions CreateOptions() => new() { Server = "127.0.0.1", Port = 4900, DatabaseName = "BASE_LICENSES", Login = "susana", Password = "secret", FilePasswords = new LicenseFilePasswordOptions { ProtectionStd = "STD_PASSWORD", ProtectionPlus = null } }; [Fact] public void CreateForLicense_MapsAllFields() { var options = CreateOptions(); var result = HfSqlConnectionOptionsFactory.CreateForLicense(options); Assert.Equal("127.0.0.1", result.Server); Assert.Equal(4900, result.Port); Assert.Equal("BASE_LICENSES", result.Database); Assert.Equal("susana", result.User); Assert.Equal("secret", result.Credential); Assert.Equal("STD_PASSWORD", result.FilePasswords.ProtectionStd); Assert.Null(result.FilePasswords.ProtectionPlus); } [Fact] public void CreateForLicense_NullOptions_Throws() { Assert.Throws(() => HfSqlConnectionOptionsFactory.CreateForLicense(null!)); } [Fact] public async Task OpenAsync_OpensConnection() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); Assert.Equal(HfsqlConnectionState.Closed, connection.State); await connection.OpenAsync(); Assert.Equal(HfsqlConnectionState.Open, connection.State); } [Fact] public async Task OpenAsync_Twice_DoesNotReopen() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); await connection.OpenAsync(); await connection.OpenAsync(); Assert.Equal(HfsqlConnectionState.Open, connection.State); Assert.Equal(1, factory.OpenCount); } [Fact] public async Task CloseAsync_ClosesConnection() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); await connection.OpenAsync(); await connection.CloseAsync(); Assert.Equal(HfsqlConnectionState.Closed, connection.State); } [Fact] public async Task QueryAsync_ReturnsRows() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); await connection.OpenAsync(); var rows = await connection.QueryAsync("SELECT * FROM Licence"); Assert.Single(rows); Assert.Equal("LIC-001", rows[0]["NumeroLicence"]); } [Fact] public async Task ExecuteScalarAsync_ReturnsValue() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); await connection.OpenAsync(); var value = await connection.ExecuteScalarAsync("SELECT COUNT(*) FROM Licence"); Assert.Equal(1L, value); } [Fact] public async Task QueryAsync_NotOpen_Throws() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); await Assert.ThrowsAsync( () => connection.QueryAsync("SELECT * FROM Licence")); } [Fact] public async Task Options_ExposesConnectionName() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); Assert.Equal(LicenseConnectionOptions.ConnectionName, connection.Options.ConnectionName); Assert.Equal("BASE_LICENSES", connection.Options.DatabaseName); Assert.Equal("susana", connection.Options.Login); } [Fact] public async Task Options_DoesNotExposePassword() { var factory = new FakeFactory(); await using var connection = new LicenseOleDbConnection(CreateOptions(), factory); var text = connection.Options.ToString(); Assert.DoesNotContain("secret", text, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("STD_PASSWORD", text, StringComparison.OrdinalIgnoreCase); } /// /// Fabrique simulée qui retourne une connexion factice sans serveur HFSQL. /// private sealed class FakeFactory : IHfSqlOleDbConnectionFactory { public int OpenCount { get; private set; } public DbConnection Create(HfSqlConnectionOptions options, string? extendedProperties = null) { return new FakeConnection(() => OpenCount++); } } private sealed class FakeConnection : DbConnection { private readonly Action _onOpen; private ConnectionState _state = ConnectionState.Closed; public FakeConnection(Action onOpen) { _onOpen = onOpen; } public override string ConnectionString { get; set; } = ""; public override string Database => "BASE_LICENSES"; public override string DataSource => "127.0.0.1:4900"; public override string ServerVersion => "1.0"; public override ConnectionState State => _state; public override void ChangeDatabase(string databaseName) { } public override void Close() => _state = ConnectionState.Closed; public override void Open() { _onOpen(); _state = ConnectionState.Open; } protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => throw new NotSupportedException(); protected override DbCommand CreateDbCommand() => new FakeCommand(); protected override void Dispose(bool disposing) { _state = ConnectionState.Closed; base.Dispose(disposing); } } private sealed class FakeCommand : DbCommand { public override string CommandText { get; set; } = ""; public override int CommandTimeout { get; set; } public override CommandType CommandType { get; set; } public override bool DesignTimeVisible { get; set; } public override UpdateRowSource UpdatedRowSource { get; set; } protected override DbConnection? DbConnection { get; set; } protected override DbParameterCollection DbParameterCollection { get; } = new FakeParameterCollection(); protected override DbTransaction? DbTransaction { get; set; } public override void Cancel() { } public override int ExecuteNonQuery() => 0; public override object? ExecuteScalar() => 1L; public override void Prepare() { } protected override DbParameter CreateDbParameter() => new FakeParameter(); protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => new FakeReader(); } private sealed class FakeReader : DbDataReader { private bool _read; public override int FieldCount => 1; public override bool HasRows => true; public override bool IsClosed => false; public override int RecordsAffected => 0; public override object this[int ordinal] => GetValue(ordinal); public override object this[string name] => GetValue(GetOrdinal(name)); public override int Depth => 0; public override bool GetBoolean(int ordinal) => throw new NotSupportedException(); public override byte GetByte(int ordinal) => throw new NotSupportedException(); public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length) => throw new NotSupportedException(); public override char GetChar(int ordinal) => throw new NotSupportedException(); public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length) => throw new NotSupportedException(); public override string GetDataTypeName(int ordinal) => throw new NotSupportedException(); public override DateTime GetDateTime(int ordinal) => throw new NotSupportedException(); public override decimal GetDecimal(int ordinal) => throw new NotSupportedException(); public override double GetDouble(int ordinal) => throw new NotSupportedException(); public override Type GetFieldType(int ordinal) => typeof(string); public override float GetFloat(int ordinal) => throw new NotSupportedException(); public override Guid GetGuid(int ordinal) => throw new NotSupportedException(); public override short GetInt16(int ordinal) => throw new NotSupportedException(); public override int GetInt32(int ordinal) => throw new NotSupportedException(); public override long GetInt64(int ordinal) => throw new NotSupportedException(); public override string GetName(int ordinal) => "NumeroLicence"; public override int GetOrdinal(string name) => name == "NumeroLicence" ? 0 : throw new IndexOutOfRangeException(); public override string GetString(int ordinal) => "LIC-001"; public override object GetValue(int ordinal) => "LIC-001"; public override int GetValues(object[] values) { values[0] = "LIC-001"; return 1; } public override bool IsDBNull(int ordinal) => false; public override bool NextResult() => false; public override bool Read() { if (_read) return false; _read = true; return true; } public override System.Collections.IEnumerator GetEnumerator() => Array.Empty().GetEnumerator(); } private sealed class FakeParameterCollection : DbParameterCollection { public override int Count => 0; public override object SyncRoot => this; public override int Add(object value) => 0; public override void AddRange(Array values) { } public override void Clear() { } public override bool Contains(object value) => false; public override bool Contains(string value) => false; public override void CopyTo(Array array, int index) { } public override System.Collections.IEnumerator GetEnumerator() => Array.Empty().GetEnumerator(); public override int IndexOf(object value) => -1; public override int IndexOf(string value) => -1; public override void Insert(int index, object value) { } public override void Remove(object value) { } public override void RemoveAt(int index) { } public override void RemoveAt(string parameterName) { } protected override DbParameter GetParameter(int index) => new FakeParameter(); protected override DbParameter GetParameter(string parameterName) => new FakeParameter(); protected override void SetParameter(int index, DbParameter value) { } protected override void SetParameter(string parameterName, DbParameter value) { } } private sealed class FakeParameter : DbParameter { public override DbType DbType { get; set; } public override ParameterDirection Direction { get; set; } public override bool IsNullable { get; set; } public override string ParameterName { get; set; } = ""; public override string SourceColumn { get; set; } = ""; public override object? Value { get; set; } public override bool SourceColumnNullMapping { get; set; } public override int Size { get; set; } public override void ResetDbType() { } } }