Files
Omika/tests/Omika.Tests/Repositories/HfSqlAuditUserResolverTests.cs
2026-10-23 18:46:50 +02:00

149 lines
5.4 KiB
C#

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;
/// <summary>
/// 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.
/// </summary>
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<string> Queries { get; } = [];
public List<Dictionary<string, object?>> 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<object?> ExecuteScalarAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
=> Task.FromResult<object?>(null);
public Task<int> ExecuteNonQueryAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
=> Task.FromResult(0);
public Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(
string sql,
IReadOnlyList<object?>? parameters = null,
CancellationToken cancellationToken = default)
{
Queries.Add(sql);
return Task.FromResult<IReadOnlyList<IReadOnlyDictionary<string, object?>>>(
sql.Contains("FROM Utilisateur", StringComparison.OrdinalIgnoreCase)
? UtilisateurRows.Cast<IReadOnlyDictionary<string, object?>>().ToList()
: []);
}
public Task<IReadOnlyList<HfsqlColumnMetadata>> GetColumnsAsync(string tableName, CancellationToken cancellationToken = default)
=> Task.FromResult<IReadOnlyList<HfsqlColumnMetadata>>([]);
}
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<string, object?> { ["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<string, object?> { ["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<string, object?> { ["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);
}
}