Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
using Omika.Core.Contracts.Interventions;
|
||||
using Omika.Core.Contracts.Mobile.Sync;
|
||||
using Omika.Core.Domain.Commands;
|
||||
using Omika.Core.Domain.Entities.Interventions;
|
||||
using Omika.Core.Interfaces;
|
||||
using Omika.Core.Interfaces.Mobile;
|
||||
using Omika.Core.Services;
|
||||
|
||||
namespace Omika.Tests.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Tests unitaires de <see cref="CompteRenduService"/> avec des fakes en mémoire.
|
||||
/// Aucune base réelle, aucune dépendance Omika.Data.
|
||||
/// </summary>
|
||||
public class CompteRenduServiceTests
|
||||
{
|
||||
// =========================================================================
|
||||
// GET
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task GetByRendezVousAsync_RepositoryReturnsNull_ReturnsNull()
|
||||
{
|
||||
var repo = new FakeCompteRenduRepository(existing: null);
|
||||
var service = new CompteRenduService(repo, new FakeIdentityResolver());
|
||||
|
||||
CompteRenduDto? result = await service.GetByRendezVousAsync("rdv-1");
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByRendezVousAsync_MapsAndEchoesRendezVousId()
|
||||
{
|
||||
var existing = new CompteRendu
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
NoteCollaborateur = "Intervention terminée",
|
||||
NomSignataire = "Jean Client",
|
||||
DevisConforme = true
|
||||
};
|
||||
var repo = new FakeCompteRenduRepository(existing);
|
||||
var service = new CompteRenduService(repo, new FakeIdentityResolver());
|
||||
|
||||
CompteRenduDto? result = await service.GetByRendezVousAsync("rdv-1");
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("rdv-1", result!.RendezVousId);
|
||||
Assert.Equal("Intervention terminée", result.NoteCollaborateur);
|
||||
Assert.Equal("Jean Client", result.NomSignataire);
|
||||
Assert.True(result.DevisConforme);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CREATE
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_Idempotent_ReturnsExistingWithoutCreating()
|
||||
{
|
||||
var existing = new CompteRendu { Id = Guid.NewGuid(), NoteCollaborateur = "Existant" };
|
||||
var repo = new FakeCompteRenduRepository(existing);
|
||||
var service = new CompteRenduService(repo, new FakeIdentityResolver());
|
||||
|
||||
CompteRenduDto result = await service.CreateAsync("rdv-1", new CreateCompteRenduRequest());
|
||||
|
||||
Assert.Equal("Existant", result.NoteCollaborateur);
|
||||
Assert.Equal(0, repo.CreateCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_MapsRequestFieldsToCommand()
|
||||
{
|
||||
var repo = new FakeCompteRenduRepository(existing: null);
|
||||
var service = new CompteRenduService(repo, new FakeIdentityResolver());
|
||||
|
||||
await service.CreateAsync("rdv-1", new CreateCompteRenduRequest
|
||||
{
|
||||
CollaborateurId = "collab-1",
|
||||
NoteCollaborateur = "Note collab",
|
||||
NomSignataire = "Client",
|
||||
NoteSignataire = "Note client",
|
||||
NomSignataireTech = "Tech",
|
||||
DevisConforme = true,
|
||||
AvecReserve = true,
|
||||
ReserveCommentaire = "Réserve",
|
||||
TypeSignataire = new Omika.Core.Contracts.Common.ReferenceValueDto { Key = "CLIENT", LegacyId = 42 }
|
||||
});
|
||||
|
||||
Assert.Equal(1, repo.CreateCallCount);
|
||||
CompteRenduCreateCommand? command = repo.LastCommand;
|
||||
Assert.NotNull(command);
|
||||
Assert.Equal("collab-1", command!.CollaborateurId);
|
||||
Assert.Equal("Note collab", command.NoteCollaborateur);
|
||||
Assert.Equal("Client", command.NomSignataire);
|
||||
Assert.Equal("Note client", command.NoteSignataire);
|
||||
Assert.Equal("Tech", command.NomSignataireTech);
|
||||
Assert.True(command.DevisConforme);
|
||||
Assert.True(command.AvecReserve);
|
||||
Assert.Equal("Réserve", command.ReserveCommentaire);
|
||||
Assert.Equal("CLIENT", command.TypeSignataireKey);
|
||||
Assert.Equal(42, command.TypeSignataireLegacyId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_ResolvesSyntheticGuidToNumericId()
|
||||
{
|
||||
var repo = new FakeCompteRenduRepository(existing: null);
|
||||
Guid publicGuid = Guid.NewGuid();
|
||||
var resolver = new FakeIdentityResolver(numericId: 12345);
|
||||
var service = new CompteRenduService(repo, resolver);
|
||||
|
||||
await service.CreateAsync(publicGuid.ToString("D"), new CreateCompteRenduRequest());
|
||||
|
||||
Assert.Equal(1, repo.CreateCallCount);
|
||||
Assert.Equal("12345", repo.LastRendezVousId);
|
||||
Assert.Equal(publicGuid, resolver.LastResolvedGuid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Service_ImplementsICompteRenduService()
|
||||
{
|
||||
Assert.True(typeof(ICompteRenduService).IsAssignableFrom(typeof(CompteRenduService)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Service_HasNoOmikaDataReferences()
|
||||
{
|
||||
var refs = GetReferencedTypes(typeof(CompteRenduService));
|
||||
|
||||
Assert.DoesNotContain(refs, t =>
|
||||
t.Namespace is not null && t.Namespace.StartsWith("Omika.Data"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// FAKES
|
||||
// =========================================================================
|
||||
|
||||
private sealed class FakeCompteRenduRepository : ICompteRenduRepository
|
||||
{
|
||||
private readonly CompteRendu? _existing;
|
||||
|
||||
public FakeCompteRenduRepository(CompteRendu? existing)
|
||||
{
|
||||
_existing = existing;
|
||||
}
|
||||
|
||||
public int CreateCallCount { get; private set; }
|
||||
|
||||
public string? LastRendezVousId { get; private set; }
|
||||
|
||||
public CompteRenduCreateCommand? LastCommand { get; private set; }
|
||||
|
||||
public Task<CompteRendu?> GetByRendezVousAsync(string rendezVousId, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(_existing);
|
||||
|
||||
public Task<CompteRendu> CreateAsync(string rendezVousId, CompteRenduCreateCommand command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
CreateCallCount++;
|
||||
LastRendezVousId = rendezVousId;
|
||||
LastCommand = command;
|
||||
|
||||
var created = new CompteRendu
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
NoteCollaborateur = command.NoteCollaborateur,
|
||||
NomSignataire = command.NomSignataire,
|
||||
NoteSignataire = command.NoteSignataire,
|
||||
NomSignataireTech = command.NomSignataireTech,
|
||||
DevisConforme = command.DevisConforme,
|
||||
AvecReserve = command.AvecReserve,
|
||||
ReserveCommentaire = command.ReserveCommentaire
|
||||
};
|
||||
|
||||
return Task.FromResult(created);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeIdentityResolver : IMobileEntityIdentityResolver
|
||||
{
|
||||
private readonly long? _numericId;
|
||||
|
||||
public FakeIdentityResolver(long? numericId = null)
|
||||
{
|
||||
_numericId = numericId;
|
||||
}
|
||||
|
||||
public Guid? LastResolvedGuid { get; private set; }
|
||||
|
||||
public Guid CreateCanonicalIdentity(string entityType, PhysicalEntityIdentity physical)
|
||||
=> physical.Guid ?? Guid.NewGuid();
|
||||
|
||||
public PhysicalEntityIdentity? ResolvePhysicalIdentity(string entityType, Guid publicGuid)
|
||||
{
|
||||
LastResolvedGuid = publicGuid;
|
||||
return _numericId is { } numericId
|
||||
? new PhysicalEntityIdentity { NumericId = numericId }
|
||||
: null;
|
||||
}
|
||||
|
||||
public bool IsCanonical(string entityType, Guid publicGuid) => true;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetReferencedTypes(Type type)
|
||||
{
|
||||
var members = type.GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static)
|
||||
.Select(m => m.DeclaringType)
|
||||
.Where(t => t is not null)
|
||||
.Cast<Type>();
|
||||
|
||||
var parameters = type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.SelectMany(m => m.GetParameters())
|
||||
.Select(p => p.ParameterType);
|
||||
|
||||
var returnTypes = type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.Select(m => m.ReturnType);
|
||||
|
||||
var properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.Select(p => p.PropertyType);
|
||||
|
||||
var ctorParams = type.GetConstructors()
|
||||
.SelectMany(c => c.GetParameters())
|
||||
.Select(p => p.ParameterType);
|
||||
|
||||
return members.Concat(parameters).Concat(returnTypes).Concat(properties).Concat(ctorParams);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user