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; /// /// Tests unitaires de avec des fakes en mémoire. /// Aucune base réelle, aucune dépendance Omika.Data. /// 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 GetByRendezVousAsync(string rendezVousId, CancellationToken cancellationToken = default) => Task.FromResult(_existing); public Task 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 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(); 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); } }