166 lines
5.6 KiB
C#
166 lines
5.6 KiB
C#
using System.Reflection;
|
|
using Omika.Core.Contracts.Clients;
|
|
using Omika.Core.Contracts.Common;
|
|
using Omika.Core.Domain.Commands;
|
|
using Omika.Core.Domain.Entities.Clients;
|
|
using Omika.Core.Interfaces;
|
|
|
|
|
|
namespace Omika.Tests.Architecture;
|
|
|
|
/// <summary>
|
|
/// Tests du contrat canonique <see cref="IClientRepository"/> : indépendance
|
|
/// vis-à-vis d'Omika.Data, de HFSQL/ODBC et des modèles physiques, périmètre
|
|
/// lecture seule et compatibilité avec une implémentation en mémoire.
|
|
/// </summary>
|
|
public class IClientRepositoryContractTests
|
|
{
|
|
private static Type RepositoryType => typeof(IClientRepository);
|
|
|
|
private static Assembly CoreAssembly => typeof(IClientRepository).Assembly;
|
|
|
|
[Fact]
|
|
public void Interface_ExistsInOmikaCore()
|
|
{
|
|
Assert.True(typeof(IClientRepository).IsInterface);
|
|
Assert.Equal("Omika.Core", CoreAssembly.GetName().Name);
|
|
}
|
|
|
|
[Fact]
|
|
public void Core_DoesNotReferenceOmikaData()
|
|
{
|
|
Assert.DoesNotContain(
|
|
CoreAssembly.GetReferencedAssemblies(),
|
|
assembly => assembly.Name == "Omika.Data");
|
|
}
|
|
|
|
[Fact]
|
|
public void Core_DoesNotReferenceOdbcOrHfSql()
|
|
{
|
|
Assert.DoesNotContain(
|
|
CoreAssembly.GetReferencedAssemblies(),
|
|
assembly => assembly.Name == "System.Data.Odbc");
|
|
}
|
|
|
|
[Fact]
|
|
public void Signatures_DoNotExposePhysicalRowTypes()
|
|
{
|
|
foreach (MethodInfo method in RepositoryType.GetMethods())
|
|
{
|
|
Assert.False(
|
|
method.ReturnType.Name.EndsWith("Row", StringComparison.Ordinal),
|
|
$"Le retour de « {method.Name} » expose un type physique « {method.ReturnType.Name} ».");
|
|
|
|
foreach (ParameterInfo parameter in method.GetParameters())
|
|
{
|
|
Assert.False(
|
|
parameter.ParameterType.Name.EndsWith("Row", StringComparison.Ordinal),
|
|
$"Le paramètre « {parameter.Name} » de « {method.Name} » expose un type physique « {parameter.ParameterType.Name} ».");
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void HasSearchMethod_ReturningCanonicalPagedResult()
|
|
{
|
|
MethodInfo? search = RepositoryType.GetMethod("SearchAsync");
|
|
Assert.NotNull(search);
|
|
Assert.Equal(typeof(Task<PagedResult<Client>>), search.ReturnType);
|
|
}
|
|
|
|
[Fact]
|
|
public void HasDetailsMethod_ReturningNullableClient()
|
|
{
|
|
MethodInfo? details = RepositoryType.GetMethod("GetDetailsAsync");
|
|
Assert.NotNull(details);
|
|
Assert.Equal(typeof(Task<Client>), details.ReturnType);
|
|
}
|
|
|
|
[Fact]
|
|
public void AllMethods_AcceptCancellationToken()
|
|
{
|
|
foreach (MethodInfo method in RepositoryType.GetMethods())
|
|
{
|
|
Assert.Contains(
|
|
method.GetParameters(),
|
|
parameter => parameter.ParameterType == typeof(CancellationToken));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void HasNoDeleteMethods()
|
|
{
|
|
// CreateAsync, UpdateAsync et ArchiveAsync existent maintenant (écriture
|
|
// supportée). Seule la suppression physique (Delete) reste interdite.
|
|
foreach (MethodInfo method in RepositoryType.GetMethods())
|
|
{
|
|
Assert.DoesNotContain("Delete", method.Name, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
|
|
[Fact]
|
|
public void HasNoBatikOrOmikaSpecificMethods()
|
|
{
|
|
foreach (MethodInfo method in RepositoryType.GetMethods())
|
|
{
|
|
Assert.DoesNotContain("Batik", method.Name, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("Omika", method.Name, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("Legacy", method.Name, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CanBeImplementedByInMemoryFake()
|
|
{
|
|
var fake = new InMemoryClientRepository();
|
|
|
|
PagedResult<Client> result = fake.SearchAsync(new ClientSearchCriteria()).GetAwaiter().GetResult();
|
|
Client? missing = fake.GetDetailsAsync("CL-001").GetAwaiter().GetResult();
|
|
|
|
Assert.Empty(result.Items);
|
|
Assert.Null(missing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implémentation en mémoire minimale prouvant que le contrat est
|
|
/// implémentable sans aucune dépendance physique.
|
|
/// </summary>
|
|
private sealed class InMemoryClientRepository : IClientRepository
|
|
{
|
|
public Task<Client> CreateAsync(
|
|
ClientCreateCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(command);
|
|
return Task.FromResult(new Client { Nom = command.Nom });
|
|
}
|
|
|
|
public Task<PagedResult<Client>> SearchAsync(
|
|
ClientSearchCriteria criteria,
|
|
CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new PagedResult<Client>([], 1, 20, 0, 0));
|
|
|
|
public Task<PagedResult<Client>> SearchDetailsAsync(
|
|
ClientSearchCriteria criteria,
|
|
CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new PagedResult<Client>([], 1, 20, 0, 0));
|
|
|
|
public Task<Client?> GetDetailsAsync(
|
|
|
|
string id,
|
|
CancellationToken cancellationToken = default)
|
|
=> Task.FromResult<Client?>(null);
|
|
|
|
public Task<Client?> UpdateAsync(
|
|
string id, Omika.Core.Domain.Commands.ClientUpdateCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
=> Task.FromResult<Client?>(null);
|
|
|
|
public Task<bool> ArchiveAsync(
|
|
string id,
|
|
CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(false);
|
|
}
|
|
}
|