using System.Reflection; using Omika.Core.Contracts.Common; using Omika.Core.Contracts.Planning; using Omika.Core.Domain; using Omika.Core.Domain.Commands; using Omika.Core.Domain.Entities.Planning; using Omika.Core.Domain.ValueObjects; using Omika.Core.Interfaces; using Omika.Core.Services; namespace Omika.Tests.Services; /// /// Tests unitaires de avec un fake /// en mémoire. Aucune base réelle. /// public class RendezVousServiceTests { // ========================================================================= // SEARCH // ========================================================================= [Fact] public async Task SearchAsync_CallsRepositoryOnce() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest()); Assert.Equal(1, repo.SearchCallCount); } [Fact] public async Task SearchAsync_TransmitsPage() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { Page = 3 }); Assert.Equal(3, repo.LastSearchCriteria?.Page); } [Fact] public async Task SearchAsync_TransmitsPageSize() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { PageSize = 50 }); Assert.Equal(50, repo.LastSearchCriteria?.PageSize); } [Fact] public async Task SearchAsync_TransmitsSearch() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { Search = "test" }); Assert.Equal("test", repo.LastSearchCriteria?.Search); } [Fact] public async Task SearchAsync_TransmitsTypeActivite() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { TypeActivite = "Réunion" }); Assert.Equal("Réunion", repo.LastSearchCriteria?.TypeActivite); } [Fact] public async Task SearchAsync_TransmitsStatut() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { Statut = "Confirmé" }); Assert.Equal("Confirmé", repo.LastSearchCriteria?.Statut); } [Fact] public async Task SearchAsync_TransmitsDebutMin() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); var debutMin = new DateTime(2026, 1, 1); await service.SearchAsync(new SearchRendezVousRequest { DebutMin = debutMin }); Assert.Equal(debutMin, repo.LastSearchCriteria?.DebutMin); } [Fact] public async Task SearchAsync_TransmitsEstArchive() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { EstArchive = true }); Assert.True(repo.LastSearchCriteria?.EstArchive); } [Fact] public async Task SearchAsync_TransmitsSortBy() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { SortBy = "Date" }); Assert.Equal("Date", repo.LastSearchCriteria?.SortBy); } [Fact] public async Task SearchAsync_TransmitsSortDescending() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.SearchAsync(new SearchRendezVousRequest { SortDescending = true }); Assert.True(repo.LastSearchCriteria?.SortDescending); } [Fact] public async Task SearchAsync_TransmitsCancellationToken() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); using var cts = new CancellationTokenSource(); await service.SearchAsync(new SearchRendezVousRequest(), cts.Token); Assert.Equal(cts.Token, repo.LastCancellationToken); } [Fact] public async Task SearchAsync_EmptyResult_ReturnsEmptyPage() { var repo = new FakeRendezVousRepository { SearchResult = new PagedResult([], 1, 20, 0, 0) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest()); Assert.Empty(result.Items); Assert.Equal(0, result.TotalCount); Assert.Equal(0, result.TotalPages); Assert.Equal(1, result.Page); Assert.Equal(20, result.PageSize); } [Fact] public async Task SearchAsync_SingleRendezVous_MapsToSummaryDto() { var rendezVous = new RendezVous { Titre = "Réunion", TypeActivite = new ReferenceValue("Réunion") { Key = "REUNION" }, Statut = new ReferenceValue("Confirmé") { Key = "CONFIRME" }, Periode = new DateTimeRange(new DateTime(2026, 1, 15, 9, 0, 0), new DateTime(2026, 1, 15, 10, 0, 0)) }; var repo = new FakeRendezVousRepository { SearchResult = new PagedResult([rendezVous], 1, 20, 1, 1) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest()); RendezVousSummaryDto dto = Assert.Single(result.Items); Assert.Equal("Réunion", dto.Titre); Assert.Equal("Réunion", dto.TypeActivite); Assert.Equal("Confirmé", dto.Statut); Assert.Equal(new DateTime(2026, 1, 15, 9, 0, 0), dto.Debut); } [Fact] public async Task SearchAsync_MultipleRendezVous_MapsAllToDtos() { var rendezVousList = new[] { new RendezVous { Titre = "Alpha" }, new RendezVous { Titre = "Beta" }, new RendezVous { Titre = "Gamma" } }; var repo = new FakeRendezVousRepository { SearchResult = new PagedResult(rendezVousList, 1, 20, 3, 1) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest()); Assert.Equal(3, result.Items.Count); Assert.Equal(3, result.TotalCount); Assert.Equal("Alpha", result.Items[0].Titre); Assert.Equal("Beta", result.Items[1].Titre); Assert.Equal("Gamma", result.Items[2].Titre); } [Fact] public async Task SearchAsync_PreservesTotalCount() { var repo = new FakeRendezVousRepository { SearchResult = new PagedResult([], 2, 10, 42, 5) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest()); Assert.Equal(42, result.TotalCount); Assert.Equal(2, result.Page); Assert.Equal(10, result.PageSize); Assert.Equal(5, result.TotalPages); } [Fact] public async Task SearchAsync_IdComesFromRendezVousContractMapper() { var rendezVous = new RendezVous { Titre = "Test" }; var expectedId = rendezVous.Id.ToString(); var repo = new FakeRendezVousRepository { SearchResult = new PagedResult([rendezVous], 1, 20, 1, 1) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest()); Assert.Equal(expectedId, result.Items[0].Id); } [Fact] public async Task SearchAsync_ServiceDoesNotRePaginate() { var repo = new FakeRendezVousRepository { SearchResult = new PagedResult( [new RendezVous { Titre = "A" }, new RendezVous { Titre = "B" }], 3, 15, 100, 7) }; var service = new RendezVousService(repo); PagedResult result = await service.SearchAsync( new SearchRendezVousRequest { Page = 1, PageSize = 5 }); Assert.Equal(2, result.Items.Count); Assert.Equal(100, result.TotalCount); Assert.Equal(7, result.TotalPages); } // ========================================================================= // DETAIL // ========================================================================= [Fact] public async Task GetByIdAsync_TransmitsExactId() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.GetByIdAsync("test-id-123"); Assert.Equal("test-id-123", repo.LastDetailsId); } [Fact] public async Task GetByIdAsync_TransmitsCancellationToken() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); using var cts = new CancellationTokenSource(); await service.GetByIdAsync("42", cts.Token); Assert.Equal(cts.Token, repo.LastCancellationToken); } [Fact] public async Task GetByIdAsync_RepositoryReturnsNull_ReturnsNull() { var repo = new FakeRendezVousRepository { DetailsResult = null }; var service = new RendezVousService(repo); RendezVousDetailsDto? result = await service.GetByIdAsync("999"); Assert.Null(result); } [Fact] public async Task GetByIdAsync_MapsToDetailsDto() { var rendezVous = new RendezVous { Titre = "Réunion", Description = "Description", TypeActivite = new ReferenceValue("Réunion") { Key = "REUNION" }, Statut = new ReferenceValue("Confirmé") { Key = "CONFIRME" } }; var repo = new FakeRendezVousRepository { DetailsResult = rendezVous }; var service = new RendezVousService(repo); RendezVousDetailsDto? result = await service.GetByIdAsync("42"); Assert.NotNull(result); Assert.Equal("Réunion", result.Titre); Assert.Equal("Description", result.Description); Assert.Equal("Réunion", result.TypeActivite?.Label); Assert.Equal("Confirmé", result.Statut?.Label); } [Fact] public async Task GetByIdAsync_CollaborateursMapped() { var rendezVous = new RendezVous { Titre = "Test" }; rendezVous.AddCollaborateur(new AffectationCollaborateur { NomAffiche = "Jean Dupont" }); var repo = new FakeRendezVousRepository { DetailsResult = rendezVous }; var service = new RendezVousService(repo); RendezVousDetailsDto? result = await service.GetByIdAsync("42"); Assert.NotNull(result); Assert.Single(result.Collaborateurs); Assert.Equal("Jean Dupont", result.Collaborateurs[0].NomAffiche); } [Fact] public async Task GetByIdAsync_ContactsMapped() { var rendezVous = new RendezVous { Titre = "Test" }; rendezVous.AddContact(new ParticipantRendezVous { NomAffiche = "Marie Curie" }); var repo = new FakeRendezVousRepository { DetailsResult = rendezVous }; var service = new RendezVousService(repo); RendezVousDetailsDto? result = await service.GetByIdAsync("42"); Assert.NotNull(result); Assert.Single(result.Contacts); Assert.Equal("Marie Curie", result.Contacts[0].NomAffiche); } [Fact] public async Task GetByIdAsync_IdComesFromMapper() { var rendezVous = new RendezVous { Titre = "Test" }; var expectedId = rendezVous.Id.ToString(); var repo = new FakeRendezVousRepository { DetailsResult = rendezVous }; var service = new RendezVousService(repo); RendezVousDetailsDto? result = await service.GetByIdAsync("42"); Assert.NotNull(result); Assert.Equal(expectedId, result.Id); } [Fact] public async Task GetByIdAsync_NoGuidParsingInService() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); await service.GetByIdAsync("not-a-guid-nor-int"); Assert.Equal("not-a-guid-nor-int", repo.LastDetailsId); } [Fact] public async Task GetByIdAsync_RepositoryException_Propagated() { var repo = new FakeRendezVousRepository { ThrowOnDetails = new InvalidOperationException("Infrastructure error") }; var service = new RendezVousService(repo); var ex = await Assert.ThrowsAsync( () => service.GetByIdAsync("42")); Assert.Equal("Infrastructure error", ex.Message); } // ========================================================================= // CREATE // ========================================================================= [Fact] public async Task CreateAsync_CallsRepositoryOnce() { var repo = new FakeRendezVousRepository { CreateResult = new RendezVous { Titre = "Nouveau" } }; var service = new RendezVousService(repo); await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" }); Assert.Equal(1, repo.CreateCallCount); } [Fact] public async Task CreateAsync_MapsAllProperties() { var repo = new FakeRendezVousRepository { CreateResult = new RendezVous { Titre = "Nouveau" } }; var service = new RendezVousService(repo); await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau", Description = "Description", Debut = new DateTime(2026, 1, 15, 9, 0, 0), Fin = new DateTime(2026, 1, 15, 10, 0, 0), TypeActivite = new ReferenceValueDto { Key = "REUNION" }, DossierId = "42", InterventionId = "43", SiteId = "44" }); Assert.Equal("Nouveau", repo.LastCreateCommand!.Titre); Assert.Equal("Description", repo.LastCreateCommand!.Description); Assert.Equal(new DateTime(2026, 1, 15, 9, 0, 0), repo.LastCreateCommand!.Debut); Assert.Equal(new DateTime(2026, 1, 15, 10, 0, 0), repo.LastCreateCommand!.Fin); Assert.Equal("REUNION", repo.LastCreateCommand!.TypeActiviteKey); Assert.Equal("42", repo.LastCreateCommand!.DossierId); Assert.Equal("43", repo.LastCreateCommand!.InterventionId); Assert.Equal("44", repo.LastCreateCommand!.SiteId); } [Fact] public async Task CreateAsync_ResultMappedToDetails() { var repo = new FakeRendezVousRepository { CreateResult = new RendezVous { Titre = "Nouveau" } }; var service = new RendezVousService(repo); RendezVousDetailsDto result = await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" }); Assert.Equal("Nouveau", result.Titre); } [Fact] public async Task CreateAsync_CancellationTokenTransmitted() { var repo = new FakeRendezVousRepository { CreateResult = new RendezVous { Titre = "Nouveau" } }; var service = new RendezVousService(repo); using var cts = new CancellationTokenSource(); await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" }, cts.Token); Assert.Equal(cts.Token, repo.LastCreateCancellationToken); } [Fact] public async Task CreateAsync_ExceptionPropagated() { var repo = new FakeRendezVousRepository { ThrowOnCreate = new InvalidOperationException("Panne") }; var service = new RendezVousService(repo); var ex = await Assert.ThrowsAsync( () => service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" })); Assert.Equal("Panne", ex.Message); } // ========================================================================= // UPDATE // ========================================================================= [Fact] public async Task UpdateAsync_MapsAllProperties() { var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "Updated" } }; var service = new RendezVousService(repo); await service.UpdateAsync("42", new UpdateRendezVousRequest { Titre = "Updated", TypeActivite = new ReferenceValueDto { Key = "REUNION" }, Statut = new ReferenceValueDto { Key = "CONFIRME" } }); Assert.Equal(1, repo.UpdateCallCount); Assert.Equal("42", repo.LastUpdateId); Assert.Equal("Updated", repo.LastUpdateCommand!.Titre); Assert.Equal("REUNION", repo.LastUpdateCommand!.TypeActiviteKey); Assert.Equal("CONFIRME", repo.LastUpdateCommand!.StatutKey); } [Fact] public async Task UpdateAsync_NullsPreserved() { var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "X" } }; var service = new RendezVousService(repo); await service.UpdateAsync("42", new UpdateRendezVousRequest()); Assert.Null(repo.LastUpdateCommand!.TypeActiviteKey); Assert.Null(repo.LastUpdateCommand!.StatutKey); } [Fact] public async Task UpdateAsync_RepoReturnsNull_ThrowsKeyNotFound() { var repo = new FakeRendezVousRepository { UpdateResult = null }; var service = new RendezVousService(repo); await Assert.ThrowsAsync( () => service.UpdateAsync("42", new UpdateRendezVousRequest())); } [Fact] public async Task UpdateAsync_IdTransmittedExactly() { var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "XY" } }; var service = new RendezVousService(repo); await service.UpdateAsync("my-guid-123", new UpdateRendezVousRequest()); Assert.Equal("my-guid-123", repo.LastUpdateId); } [Fact] public async Task UpdateAsync_CancellationTokenTransmitted() { var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "X" } }; var service = new RendezVousService(repo); using var cts = new CancellationTokenSource(); await service.UpdateAsync("42", new UpdateRendezVousRequest(), cts.Token); Assert.Equal(cts.Token, repo.LastUpdateCancellationToken); } [Fact] public async Task UpdateAsync_ExceptionPropagated() { var repo = new FakeRendezVousRepository { ThrowOnUpdate = new InvalidOperationException("Panne") }; var service = new RendezVousService(repo); var ex = await Assert.ThrowsAsync( () => service.UpdateAsync("42", new UpdateRendezVousRequest())); Assert.Equal("Panne", ex.Message); } // ========================================================================= // CANCEL // ========================================================================= [Fact] public async Task CancelAsync_TruePropagated() { var repo = new FakeRendezVousRepository { CancelResult = true }; var service = new RendezVousService(repo); await service.CancelAsync("42", new AnnulationRendezVousDto()); Assert.Equal(1, repo.CancelCallCount); } [Fact] public async Task CancelAsync_False_ThrowsKeyNotFound() { var repo = new FakeRendezVousRepository { CancelResult = false }; var service = new RendezVousService(repo); await Assert.ThrowsAsync( () => service.CancelAsync("42", new AnnulationRendezVousDto())); } [Fact] public async Task CancelAsync_IdTransmittedExactly() { var repo = new FakeRendezVousRepository { CancelResult = true }; var service = new RendezVousService(repo); await service.CancelAsync("my-guid-123", new AnnulationRendezVousDto()); Assert.Equal("my-guid-123", repo.LastCancelId); } [Fact] public async Task CancelAsync_MapsAnnulation() { var repo = new FakeRendezVousRepository { CancelResult = true }; var service = new RendezVousService(repo); await service.CancelAsync("42", new AnnulationRendezVousDto { Origine = new ReferenceValueDto { Key = "CLIENT" }, Motif = new ReferenceValueDto { Key = "DISPONIBILITE" }, Commentaire = "Reporté", CollaborateurId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }); Assert.Equal("CLIENT", repo.LastCancelCommand!.OrigineKey); Assert.Equal("DISPONIBILITE", repo.LastCancelCommand!.MotifKey); Assert.Equal("Reporté", repo.LastCancelCommand!.Commentaire); Assert.Equal("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", repo.LastCancelCommand!.CollaborateurId); } [Fact] public async Task CancelAsync_TransmetLAuteurAuRepository() { var repo = new FakeRendezVousRepository { CancelResult = true }; var service = new RendezVousService(repo); var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"); // L'annulation modifie le rendez-vous : l'auteur doit atteindre le // repository pour estampiller ModifDate/ModifUser du noyau. await service.CancelAsync("42", new AnnulationRendezVousDto(), auteur: auteur); Assert.Same(auteur, repo.LastCancelAuteur); } [Fact] public async Task CancelAsync_SansAuteur_TransmetUnAuteurVide() { var repo = new FakeRendezVousRepository { CancelResult = true }; var service = new RendezVousService(repo); await service.CancelAsync("42", new AnnulationRendezVousDto()); Assert.True(repo.LastCancelAuteur is { UserId: null, CollaborateurId: null }); } // ========================================================================= // ARCHIVE // ========================================================================= [Fact] public async Task ArchiveAsync_TruePropagated() { var repo = new FakeRendezVousRepository { ArchiveResult = true }; var service = new RendezVousService(repo); await service.ArchiveAsync("42"); Assert.Equal(1, repo.ArchiveCallCount); } [Fact] public async Task ArchiveAsync_False_ThrowsKeyNotFound() { var repo = new FakeRendezVousRepository { ArchiveResult = false }; var service = new RendezVousService(repo); await Assert.ThrowsAsync( () => service.ArchiveAsync("42")); } [Fact] public async Task ArchiveAsync_IdTransmittedExactly() { var repo = new FakeRendezVousRepository { ArchiveResult = true }; var service = new RendezVousService(repo); await service.ArchiveAsync("my-guid-123"); Assert.Equal("my-guid-123", repo.LastArchiveId); } [Fact] public async Task ArchiveAsync_CancellationTokenTransmitted() { var repo = new FakeRendezVousRepository { ArchiveResult = true }; var service = new RendezVousService(repo); using var cts = new CancellationTokenSource(); await service.ArchiveAsync("42", cts.Token); Assert.Equal(cts.Token, repo.LastArchiveCancellationToken); } [Fact] public async Task ArchiveAsync_ExceptionPropagated() { var repo = new FakeRendezVousRepository { ThrowOnArchive = new InvalidOperationException("Panne") }; var service = new RendezVousService(repo); var ex = await Assert.ThrowsAsync( () => service.ArchiveAsync("42")); Assert.Equal("Panne", ex.Message); } [Fact] public async Task ArchiveAsync_TransmetLAuteurAuRepository() { var repo = new FakeRendezVousRepository { ArchiveResult = true }; var service = new RendezVousService(repo); var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"); await service.ArchiveAsync("42", auteur: auteur); Assert.Same(auteur, repo.LastArchiveAuteur); } [Fact] public async Task SetTempsValideAsync_TransmetLAuteurAuRepository() { var repo = new FakeRendezVousRepository(); var service = new RendezVousService(repo); var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"); // Le passage au statut « Fait » modifie le noyau du rendez-vous : l'auteur // doit atteindre le repository. await service.SetTempsValideAsync("42", "collab-1", tempsValide: true, auteur: auteur); Assert.Same(auteur, repo.LastSetTempsValideAuteur); } // ========================================================================= // ARCHITECTURE // ========================================================================= [Fact] public void Service_ImplementsIRendezVousService() { Assert.True(typeof(IRendezVousService).IsAssignableFrom(typeof(RendezVousService))); } [Fact] public void Service_DependsOnIRendezVousRepository() { var type = typeof(RendezVousService); bool hasRepoParam = type.GetConstructors() .Any(c => c.GetParameters() .Any(p => p.ParameterType == typeof(IRendezVousRepository))); Assert.True(hasRepoParam, "RendezVousService doit dépendre de IRendezVousRepository."); } [Fact] public void Service_HasNoOmikaDataReferences() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Namespace is not null && t.Namespace.StartsWith("Omika.Data")); } [Fact] public void Service_HasNoBatikReferences() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Name.Contains("Batik", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Service_HasNoHfsqlReferences() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Name.Contains("Row", StringComparison.OrdinalIgnoreCase) && t.Namespace is not null && (t.Namespace.Contains("HfSql", StringComparison.OrdinalIgnoreCase) || t.Namespace.Contains("Models.Omika", StringComparison.OrdinalIgnoreCase))); } [Fact] public void Service_HasNoSqlStatements() { string source = ReadSourceFile(typeof(RendezVousService)); Assert.DoesNotContain("SELECT ", source, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("FROM ", source, StringComparison.OrdinalIgnoreCase); } [Fact] public void Service_HasNoApplicationRuntimeContext() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Name.Contains("ApplicationRuntimeContext", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Service_HasNoDatabaseSchemaVersion() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Name.Contains("DatabaseSchemaVersion", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Service_HasNoOdbcReferences() { var type = typeof(RendezVousService); var refs = GetReferencedTypes(type); Assert.DoesNotContain(refs, t => t.Namespace is not null && t.Namespace.StartsWith("System.Data.Odbc", StringComparison.Ordinal)); } // ========================================================================= // HELPERS // ========================================================================= private static IEnumerable GetReferencedTypes(Type type) { var members = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) .Select(m => m.DeclaringType) .Where(t => t is not null) .Cast(); var parameters = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .SelectMany(m => m.GetParameters()) .Select(p => p.ParameterType); var returnTypes = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Select(m => m.ReturnType); var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | 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); } private static string ReadSourceFile(Type type) { string baseDir = AppDomain.CurrentDomain.BaseDirectory; var dir = new DirectoryInfo(baseDir); while (dir != null && !File.Exists(Path.Combine(dir.FullName, "Omika.slnx"))) dir = dir.Parent; string root = dir?.FullName ?? baseDir; return File.ReadAllText(Path.Combine(root, "src", "Omika.Core", "Services", type.Name + ".cs")); } // ========================================================================= // FAKE REPOSITORY // ========================================================================= private sealed class FakeRendezVousRepository : IRendezVousRepository { public int SearchCallCount { get; private set; } public int DetailsCallCount { get; private set; } public int UpdateCallCount { get; private set; } public int ArchiveCallCount { get; private set; } public int CreateCallCount { get; private set; } public int CancelCallCount { get; private set; } public RendezVousSearchCriteria? LastSearchCriteria { get; private set; } public string? LastDetailsId { get; private set; } public string? LastUpdateId { get; private set; } public string? LastArchiveId { get; private set; } public string? LastCancelId { get; private set; } public RendezVousUpdateCommand? LastUpdateCommand { get; private set; } public RendezVousCreateCommand? LastCreateCommand { get; private set; } public AnnulationRendezVousCommand? LastCancelCommand { get; private set; } public AuditActor? LastCancelAuteur { get; private set; } public AuditActor? LastArchiveAuteur { get; private set; } public AuditActor? LastSetTempsValideAuteur { get; private set; } public CancellationToken LastUpdateCancellationToken { get; private set; } public CancellationToken LastArchiveCancellationToken { get; private set; } public CancellationToken LastCreateCancellationToken { get; private set; } public CancellationToken LastCancelCancellationToken { get; private set; } public CancellationToken LastCancellationToken { get; private set; } public PagedResult? SearchResult { get; set; } public RendezVous? DetailsResult { get; set; } public RendezVous? UpdateResult { get; set; } public RendezVous? CreateResult { get; set; } public bool ArchiveResult { get; set; } public bool CancelResult { get; set; } public Exception? ThrowOnSearch { get; set; } public Exception? ThrowOnDetails { get; set; } public Exception? ThrowOnUpdate { get; set; } public Exception? ThrowOnArchive { get; set; } public Exception? ThrowOnCreate { get; set; } public Exception? ThrowOnCancel { get; set; } public Task CreateAsync( RendezVousCreateCommand command, CancellationToken cancellationToken = default) { CreateCallCount++; LastCreateCommand = command; LastCreateCancellationToken = cancellationToken; if (ThrowOnCreate is not null) throw ThrowOnCreate; return Task.FromResult(CreateResult ?? new RendezVous { Titre = command.Titre }); } public Task> SearchAsync( RendezVousSearchCriteria criteria, CancellationToken cancellationToken = default) { SearchCallCount++; LastSearchCriteria = criteria; LastCancellationToken = cancellationToken; if (ThrowOnSearch is not null) throw ThrowOnSearch; return Task.FromResult(SearchResult ?? new PagedResult([], 1, 20, 0, 0)); } public Task> SearchDetailsAsync( RendezVousSearchCriteria criteria, CancellationToken cancellationToken = default) { DetailsCallCount++; LastSearchCriteria = criteria; LastCancellationToken = cancellationToken; if (ThrowOnSearch is not null) throw ThrowOnSearch; return Task.FromResult(SearchResult ?? new PagedResult([], 1, 20, 0, 0)); } public Task GetDetailsAsync( string id, CancellationToken cancellationToken = default) { DetailsCallCount++; LastDetailsId = id; LastCancellationToken = cancellationToken; if (ThrowOnDetails is not null) throw ThrowOnDetails; return Task.FromResult(DetailsResult); } public Task UpdateAsync( string id, RendezVousUpdateCommand command, CancellationToken cancellationToken = default) { UpdateCallCount++; LastUpdateId = id; LastUpdateCommand = command; LastUpdateCancellationToken = cancellationToken; if (ThrowOnUpdate is not null) throw ThrowOnUpdate; return Task.FromResult(UpdateResult); } public Task CancelAsync( string id, AnnulationRendezVousCommand annulation, CancellationToken cancellationToken = default, AuditActor? auteur = null) { CancelCallCount++; LastCancelId = id; LastCancelCommand = annulation; LastCancelCancellationToken = cancellationToken; LastCancelAuteur = auteur; if (ThrowOnCancel is not null) throw ThrowOnCancel; return Task.FromResult(CancelResult); } public Task ArchiveAsync( string id, CancellationToken cancellationToken = default, AuditActor? auteur = null) { ArchiveCallCount++; LastArchiveId = id; LastArchiveCancellationToken = cancellationToken; LastArchiveAuteur = auteur; if (ThrowOnArchive is not null) throw ThrowOnArchive; return Task.FromResult(ArchiveResult); } public Task SetTempsValideAsync( string rendezVousId, string collaborateurId, bool tempsValide, CancellationToken cancellationToken = default, AuditActor? auteur = null) { LastSetTempsValideAuteur = auteur; return Task.FromResult(true); } } }