Premiere commit - Omika

This commit is contained in:
2026-10-23 18:46:50 +02:00
commit 8ea76c1b24
2155 changed files with 984301 additions and 0 deletions
@@ -0,0 +1,174 @@
using System.Data.Common;
using Omika.Core.Domain.Commands;
using Omika.Data.Configuration;
using Omika.Data.Connection;
using Omika.Data.Repositories.Batik;
using Omika.Data.Repositories.Omika;
namespace Omika.Tests.Repositories;
/// <summary>
/// Vérifie que l'INSERT dans <c>Client_Adresse</c> n'écrit AUCUNE colonne d'audit
/// (CreationDate/User, ModifDate/User) — absentes du schéma canonique Batik et
/// Omika — et que le mapping des colonnes métier est correct dans chaque schéma.
/// </summary>
public class ClientAdresseInsertTests
{
[Fact]
public async Task Omika_ClientAdresse_Insert_SansColonneAudit()
{
var connection = new SpyingHfsqlConnection();
var repository = new OmikaClientRepository(connection);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
repository.CreateAsync(CreateCommandWithAddress()));
var insert = connection.Commands.Single(c => c.Sql.StartsWith("INSERT INTO Client_Adresse"));
Assert.DoesNotContain("CreationDate", insert.Sql);
Assert.DoesNotContain("CreationUser", insert.Sql);
Assert.DoesNotContain("ModifDate", insert.Sql);
Assert.DoesNotContain("ModifUser", insert.Sql);
Assert.Contains("CA_IDAdresse", insert.Sql);
Assert.Contains("CA_IDClient", insert.Sql);
Assert.Contains("CA_IDTypeAdresse", insert.Sql);
Assert.Contains("CA_Principale", insert.Sql);
// Clé composée HFSQL auto-calculée : jamais écrite explicitement en Omika.
Assert.DoesNotContain("CA_IDCli_IDAd", insert.Sql);
}
[Fact]
public async Task Omika_ClientAdresse_ParametresCorrects()
{
var connection = new SpyingHfsqlConnection();
var repository = new OmikaClientRepository(connection);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
repository.CreateAsync(CreateCommandWithAddress()));
var insert = connection.Commands.Single(c => c.Sql.StartsWith("INSERT INTO Client_Adresse"));
object?[] p = insert.Parameters;
Assert.Equal(4, p.Length);
Assert.Equal(1L, p[0]); // CA_IDAdresse (identité auto-générée = 1)
Assert.Equal(1L, p[1]); // CA_IDClient (identité auto-générée = 1)
Assert.Equal(0, p[2]); // CA_IDTypeAdresse
Assert.Equal(true, p[3]); // CA_Principale
}
[Fact]
public async Task Batik_ClientAdresse_Insert_SansColonneAudit()
{
var connection = new SpyingHfsqlConnection();
var repository = new BatikClientRepository(connection);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
repository.CreateAsync(CreateCommandWithAddress()));
var insert = connection.Commands.Single(c => c.Sql.StartsWith("INSERT INTO Client_Adresse"));
Assert.DoesNotContain("CreationDate", insert.Sql);
Assert.DoesNotContain("CreationUser", insert.Sql);
Assert.DoesNotContain("ModifDate", insert.Sql);
Assert.DoesNotContain("ModifUser", insert.Sql);
Assert.Contains("CA_IDAdresse", insert.Sql);
Assert.Contains("CA_IDClient", insert.Sql);
Assert.Contains("CA_IDTypeAdresse", insert.Sql);
Assert.Contains("CA_Principale", insert.Sql);
// Batik écrit la clé composée explicitement (comportement historique).
Assert.Contains("CA_IDCli_IDAd", insert.Sql);
}
[Fact]
public async Task Batik_ClientAdresse_ParametresCorrects()
{
var connection = new SpyingHfsqlConnection();
var repository = new BatikClientRepository(connection);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
repository.CreateAsync(CreateCommandWithAddress()));
var insert = connection.Commands.Single(c => c.Sql.StartsWith("INSERT INTO Client_Adresse"));
object?[] p = insert.Parameters;
Assert.Equal(5, p.Length);
Assert.Equal(1L, p[0]); // CA_IDAdresse
Assert.Equal(1L, p[1]); // CA_IDClient
Assert.Equal("11", p[2]); // CA_IDCli_IDAd = clientId + adresseId
Assert.Equal(0, p[3]); // CA_IDTypeAdresse
Assert.Equal(true, p[4]); // CA_Principale
}
private static ClientCreateCommand CreateCommandWithAddress() => new()
{
Nom = "Test",
Code = "T-001",
AdresseLigne1 = "1 rue du Test",
AdresseCodePostal = "75000",
AdresseVille = "Paris"
};
private sealed record ExecutedCommand(string Sql, object?[] Parameters);
private sealed class SpyingHfsqlConnection : IHfsqlConnection, IHfsqlWriteConnection
{
public HfsqlOptions Options => new();
public HfsqlConnectionState State { get; private set; } = HfsqlConnectionState.Closed;
public DbTransaction? ActiveTransaction => null;
public IReadOnlyList<ExecutedCommand> Commands => _commands;
private readonly List<ExecutedCommand> _commands = [];
private void Record(string sql, IReadOnlyList<object?>? parameters)
=> _commands.Add(new ExecutedCommand(sql, parameters?.ToArray() ?? Array.Empty<object?>()));
public Task OpenAsync(CancellationToken cancellationToken = default)
{
State = HfsqlConnectionState.Open;
return Task.CompletedTask;
}
public Task CloseAsync()
{
State = HfsqlConnectionState.Closed;
return Task.CompletedTask;
}
public ValueTask DisposeAsync()
{
State = HfsqlConnectionState.Closed;
return 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)
{
Record(sql, parameters);
return Task.FromResult(0);
}
public Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
{
Record(sql, parameters);
return Task.FromResult<IReadOnlyList<IReadOnlyDictionary<string, object?>>>([]);
}
public Task<IReadOnlyList<HfsqlColumnMetadata>> GetColumnsAsync(string tableName, CancellationToken cancellationToken = default)
=> Task.FromResult<IReadOnlyList<HfsqlColumnMetadata>>([]);
public Task BeginTransactionAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public Task RollbackAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public Task<IReadOnlyList<long?>> ExecuteInTransactionAsync(IReadOnlyList<HfsqlWriteOperation> operations, CancellationToken cancellationToken = default, DbTransaction? transaction = null)
{
foreach (HfsqlWriteOperation operation in operations)
{
Record(operation.Sql, operation.Parameters);
}
// Une identité auto-générée (1) pour chaque opération.
return Task.FromResult<IReadOnlyList<long?>>(operations.Select(_ => (long?)1L).ToList());
}
}
}