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