Files
Omika/tests/Omika.Tests/Services/CompteRenduServiceTests.cs
T
2026-10-23 18:46:50 +02:00

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);
}
}