Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user