228 lines
8.6 KiB
C#
228 lines
8.6 KiB
C#
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);
|
|
}
|
|
}
|