using System.Text.Json; using Omika.App.Local.Models; using Omika.App.Local.Infrastructure; using Omika.App.Local.Services; using Omika.App.Navigation; using Omika.App.Services; using Omika.App.ViewModels; using Omika.Core.Constants; using Omika.Core.Contracts.Clients; using Omika.Core.Contracts.Common; using Omika.Core.Contracts.Interventions; namespace Omika.Tests.Mobile.Local; /// /// Tests du ViewModel de la fiche Client : chargement, état introuvable, /// coordonnées, relations (contacts, dossiers, interventions, sites) et /// navigation vers les entités liées. Aucun appel API. /// public class ClientDetailViewModelTests : LocalTestBase { private readonly ClientDetailViewModel _viewModel; private readonly NavigationService _navigation; public ClientDetailViewModelTests() { var service = new ClientLocalService(ClientRepository, DossierRepository, ContextInterventionCacheRepository, ClientStateRepository, ApiClient); _navigation = new NavigationService(); var permissions = new PermissionService(); permissions.SetRights(MobilePermissionKeys.All.ToDictionary(k => k, _ => true)); _viewModel = new ClientDetailViewModel(service, _navigation, permissions); } private static LocalClient CreateClient( Guid id, string nom, string? code = null, string? ville = null, string? statut = null, string? telephone = null, string? email = null, IReadOnlyList? sites = null, IReadOnlyList? contacts = null) { var dto = new ClientDetailsDto { Id = id.ToString(), Nom = nom, Code = code, Statut = statut is null ? null : new ReferenceValueDto { Label = statut }, Coordonnees = telephone is null && email is null ? null : new ContactDetailsDto { Phone = telephone, Email = email }, AdressePrincipale = ville is null ? null : new AdresseDto { AdressePostale = new AddressDto { City = ville } }, Sites = sites ?? [], Contacts = contacts ?? [] }; return new LocalClient { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalDossier CreateDossier(Guid id, Guid clientId, string reference, string titre) { var dto = new DossierDetailsDto { Id = id.ToString(), ReferenceMetier = reference, Titre = titre, ClientId = clientId.ToString(), Statut = new ReferenceValueDto { Label = "Ouvert" } }; return new LocalDossier { Id = id, ClientId = clientId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalIntervention CreateIntervention(Guid id, Guid clientId, string reference, string titre) { var dto = new InterventionDetailsDto { Id = id.ToString(), Reference = reference, Titre = titre, ClientId = clientId.ToString(), Etat = new ReferenceValueDto { Label = "Planifiée" } }; return new LocalIntervention { Id = id, ClientId = clientId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private async Task CacheClientInterventionsAsync(Guid clientId, params LocalIntervention[] interventions) { var details = interventions .Select(i => JsonSerializer.Deserialize(i.PayloadJson)!) .ToList(); await ContextInterventionCacheRepository.ReplaceAsync(InterventionContextTypes.Client, clientId, details); } [Fact] public async Task Load_ChargementPuisDetail() { // Arrange Guid clientId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert : après chargement, détail affiché et chargement terminé. Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.Client); Assert.Equal("Client Alpha", _viewModel.Client.Nom); } [Fact] public async Task Load_ClientIntrouvable_MessageErreur() { // Act await _viewModel.LoadCommand.ExecuteAsync(Guid.NewGuid()); // Assert Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.ErrorMessage); Assert.Null(_viewModel.Client); Assert.True(_viewModel.IsNotFound); } [Fact] public async Task Load_CoordonneesAffichees() { // Arrange Guid clientId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha", "C001", "Paris", "Actif", "0102030405", "a@b.fr")); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert Assert.True(_viewModel.HasCoordonnees); Assert.Equal("0102030405", _viewModel.Client!.Phone); Assert.Equal("a@b.fr", _viewModel.Client.Email); } [Fact] public async Task Load_ContactsLiees() { // Arrange Guid clientId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha", contacts: [ new ContactDto { Id = contactId.ToString(), NomAffiche = "Contact A" } ])); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert Assert.True(_viewModel.HasContacts); var contact = Assert.Single(_viewModel.Client!.Contacts); Assert.Equal(contactId, contact.ContactGuid); } [Fact] public async Task Load_DossiersLiees() { // Arrange Guid clientId = Guid.NewGuid(); Guid dossierId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); await DossierRepository.UpsertAsync(CreateDossier(dossierId, clientId, "DOS-001", "Dossier A")); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert Assert.True(_viewModel.HasDossiers); var dossier = Assert.Single(_viewModel.Client!.Dossiers); Assert.Equal(dossierId, dossier.DossierGuid); } [Fact] public async Task Load_SitesDisponibles() { // Arrange Guid clientId = Guid.NewGuid(); Guid siteId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha", sites: [ new SiteDto { Id = siteId.ToString(), Nom = "Site A" } ])); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert Assert.True(_viewModel.HasSites); var site = Assert.Single(_viewModel.Client!.Sites); Assert.Equal(siteId, site.SiteGuid); } [Fact] public async Task Load_RelationsVides_PasDErreur() { // Arrange : client sans relations. Guid clientId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert : collections vides, pas d'erreur. Assert.NotNull(_viewModel.Client); Assert.False(_viewModel.HasContacts); Assert.False(_viewModel.HasDossiers); Assert.False(_viewModel.HasSites); Assert.Null(_viewModel.ErrorMessage); } [Fact] public async Task OpenContact_NavigueVersContactDetail() { // Arrange Guid clientId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha", contacts: [ new ContactDto { Id = contactId.ToString(), NomAffiche = "Contact A" } ])); await _viewModel.LoadCommand.ExecuteAsync(clientId); // Act _viewModel.OpenContactCommand.Execute(_viewModel.Client!.Contacts[0]); // Assert : navigation vers le détail du contact avec le GUID public et le retour vers la fiche Client. Assert.Equal(AppPage.ContactDetail, _navigation.CurrentPage); var nav = Assert.IsType(_navigation.CurrentParameter); Assert.Equal(contactId, nav.Id); Assert.Equal(AppPage.ClientDetail, nav.ReturnContext.Page); Assert.Equal(clientId, nav.ReturnContext.Parameter); } [Fact] public async Task OpenDossier_NavigueVersDossierDetail() { // Arrange Guid clientId = Guid.NewGuid(); Guid dossierId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); await DossierRepository.UpsertAsync(CreateDossier(dossierId, clientId, "DOS-001", "Dossier A")); await _viewModel.LoadCommand.ExecuteAsync(clientId); // Act _viewModel.OpenDossierCommand.Execute(_viewModel.Dossiers[0]); // Assert : navigation vers le détail du dossier avec le GUID public. Assert.Equal(AppPage.DossierDetail, _navigation.CurrentPage); Assert.Equal(dossierId, _navigation.CurrentParameter); } [Fact] public async Task OpenContact_Null_NeNaviguePas() { // Act _viewModel.OpenContactCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task OpenDossier_Null_NeNaviguePas() { // Act _viewModel.OpenDossierCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task Back_RetourneALaListe() { // Act _viewModel.BackCommand.Execute(null); // Assert Assert.Equal(AppPage.Clients, _navigation.CurrentPage); } [Fact] public async Task Load_IdentitePublique_Conservee() { // Arrange Guid clientId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); // Act await _viewModel.LoadCommand.ExecuteAsync(clientId); // Assert Assert.NotNull(_viewModel.Client); Assert.Equal(clientId, _viewModel.Client.ClientGuid); } }