using Omika.Core.Compatibility; using Omika.Core.Domain; using Omika.Data.Configuration; using Omika.Data.Connection; using Omika.Data.HfSql.Audit; namespace Omika.Tests.Repositories; /// /// Tests du résolveur central d'audit utilisateur (HfSqlAuditUserResolver) : /// priorité UserId (UT_PKID direct, sans requête), fallback CollaborateurId /// (numérique → UT_PKID, GUID Omika, GUID synthétique Batik), et garantie que /// l'on ne confond JAMAIS utilisateur et collaborateur. /// public class HfSqlAuditUserResolverTests { private sealed class FakeConnection : IHfsqlConnection { public FakeConnection() { State = HfsqlConnectionState.Open; } public HfsqlOptions Options => new(); public HfsqlConnectionState State { get; private set; } public List Queries { get; } = []; public List> UtilisateurRows { get; } = []; public Task OpenAsync(CancellationToken cancellationToken = default) { State = HfsqlConnectionState.Open; return Task.CompletedTask; } public Task CloseAsync() { State = HfsqlConnectionState.Closed; return Task.CompletedTask; } public ValueTask DisposeAsync() => ValueTask.CompletedTask; public Task ExecuteScalarAsync(string sql, IReadOnlyList? parameters = null, CancellationToken cancellationToken = default) => Task.FromResult(null); public Task ExecuteNonQueryAsync(string sql, IReadOnlyList? parameters = null, CancellationToken cancellationToken = default) => Task.FromResult(0); public Task>> QueryAsync( string sql, IReadOnlyList? parameters = null, CancellationToken cancellationToken = default) { Queries.Add(sql); return Task.FromResult>>( sql.Contains("FROM Utilisateur", StringComparison.OrdinalIgnoreCase) ? UtilisateurRows.Cast>().ToList() : []); } public Task> GetColumnsAsync(string tableName, CancellationToken cancellationToken = default) => Task.FromResult>([]); } private static AuditActor Actor(string? userId, string? collaborateurId) => new(userId, collaborateurId); [Fact] public async Task UserIdPrioritaire_RetourneUtPk_SansRequete() { // Arrange : UserId = 57 (UT_PKID), CollaborateurId = 123 (CO_PKID différent). var connection = new FakeConnection(); // Act long result = await HfSqlAuditUserResolver.ResolveAsync( connection, DatabaseSchemaVersion.Omika, Actor("57", "123")); // Assert : 57 (jamais 123), et AUCUNE requête HFSQL. Assert.Equal(57, result); Assert.Empty(connection.Queries); } [Fact] public async Task UserIdValide_NeRequiertAucuneRequete() { var connection = new FakeConnection(); long result = await HfSqlAuditUserResolver.ResolveAsync( connection, DatabaseSchemaVersion.Batik, Actor("42", null)); Assert.Equal(42, result); Assert.Empty(connection.Queries); } [Fact] public async Task UserIdAbsent_CollaborateurNumerique_ResoutUtPk() { // Arrange : UserId vide, CollaborateurId numérique = CO_PKID 123. var connection = new FakeConnection(); connection.UtilisateurRows.Add(new Dictionary { ["UT_PKID"] = 77L }); // Act long result = await HfSqlAuditUserResolver.ResolveAsync( connection, DatabaseSchemaVersion.Omika, Actor(null, "123")); // Assert Assert.Equal(77, result); Assert.Single(connection.Queries); Assert.Contains("UT_IDCollaborateur", connection.Queries[0], StringComparison.OrdinalIgnoreCase); } [Fact] public async Task LegacyGuidOmika_ResoutUtPk() { // Arrange : CollaborateurId = GUID physique Omika. var connection = new FakeConnection(); connection.UtilisateurRows.Add(new Dictionary { ["UT_PKID"] = 88L }); // Act long result = await HfSqlAuditUserResolver.ResolveAsync( connection, DatabaseSchemaVersion.Omika, Actor(null, Guid.NewGuid().ToString("D"))); // Assert Assert.Equal(88, result); Assert.Single(connection.Queries); } [Fact] public async Task LegacyGuidBatik_DecodePuisResoutUtPk() { // Arrange : GUID synthétique Batik (marqueur 0x4B, version 0x01, type 0x08, // PK 123 sur les octets 3..10 big-endian). byte[] bytes = new byte[16]; bytes[0] = 0x4B; bytes[1] = 0x01; bytes[2] = 0x08; bytes[10] = 0x7B; // 123 var batikGuid = new Guid(bytes); var connection = new FakeConnection(); connection.UtilisateurRows.Add(new Dictionary { ["UT_PKID"] = 99L }); // Act long result = await HfSqlAuditUserResolver.ResolveAsync( connection, DatabaseSchemaVersion.Batik, Actor(null, batikGuid.ToString("D"))); // Assert Assert.Equal(99, result); Assert.Single(connection.Queries); } }