175 lines
7.3 KiB
C#
175 lines
7.3 KiB
C#
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());
|
|
}
|
|
}
|
|
}
|