using System.Text.Json; using Omika.App.Local.Abstractions; using Omika.App.Local.Infrastructure; using Omika.App.Local.Models; 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; using Omika.Core.Contracts.Sites; namespace Omika.Tests.Mobile.Local; /// /// Tests du ViewModel de la fiche Site / lieu : chargement, état introuvable, /// coordonnées, relations (client, contacts, interventions) et navigation vers /// les entités liées. Aucun appel API. /// public class SiteDetailViewModelTests : LocalTestBase { private readonly SiteDetailViewModel _viewModel; private readonly NavigationService _navigation; public SiteDetailViewModelTests() { var service = new SiteLocalService(SiteRepository, ClientRepository, PersonneRepository, RendezVousRepository, InterventionRepository, ContextInterventionCacheRepository, SiteStateRepository, ApiClient); _navigation = new NavigationService(); var permissions = new PermissionService(); permissions.SetRights(MobilePermissionKeys.All.ToDictionary(k => k, _ => true)); _viewModel = new SiteDetailViewModel(service, _navigation, permissions, new FakeMapLauncher()); } private sealed class FakeMapLauncher : IMapLauncher { public Task OpenItineraireAsync(string address) => Task.FromResult(true); } private static LocalSite CreateSite( Guid id, string nom, string? code = null, string? ville = null, string? type = null, Guid? clientId = null, IReadOnlyList? contactIds = null) { var dto = new SiteDetailsDto { Id = id.ToString(), Nom = nom, Code = code, Type = type is null ? null : new ReferenceValueDto { Label = type }, ClientId = clientId?.ToString(), Adresse = ville is null ? null : new AddressDto { City = ville, OneLine = ville }, ContactIds = contactIds ?? [] }; return new LocalSite { Id = id, ClientId = clientId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalClient CreateClient(Guid id, string nom, string? code = null) { var dto = new ClientDetailsDto { Id = id.ToString(), Nom = nom, Code = code }; return new LocalClient { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalPersonne CreatePersonne(Guid id, string nomAffiche) { var dto = new ContactDto { Id = id.ToString(), NomAffiche = nomAffiche }; return new LocalPersonne { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalIntervention CreateIntervention(Guid id, Guid siteId, string reference, string titre) { var dto = new InterventionDetailsDto { Id = id.ToString(), Reference = reference, Titre = titre, SiteId = siteId.ToString(), Etat = new ReferenceValueDto { Label = "Planifiée" } }; return new LocalIntervention { Id = id, SiteId = siteId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private async Task CacheSiteInterventionsAsync(Guid siteId, params LocalIntervention[] interventions) { var details = interventions .Select(i => JsonSerializer.Deserialize(i.PayloadJson)!) .ToList(); await ContextInterventionCacheRepository.ReplaceAsync(InterventionContextTypes.Site, siteId, details); } [Fact] public async Task Load_ChargementPuisDetail() { // Arrange Guid siteId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site Alpha")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert : après chargement, détail affiché et chargement terminé. Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.Site); Assert.Equal("Site Alpha", _viewModel.Site.Nom); } [Fact] public async Task Load_SiteIntrouvable_MessageErreur() { // Act await _viewModel.LoadCommand.ExecuteAsync(Guid.NewGuid()); // Assert Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.ErrorMessage); Assert.Null(_viewModel.Site); } [Fact] public async Task Load_CoordonneesAffichees() { // Arrange Guid siteId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site Alpha", "S001", "Paris", "Entrepôt")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert Assert.NotNull(_viewModel.Site); Assert.Equal("S001", _viewModel.Site.Code); Assert.Equal("Entrepôt", _viewModel.Site.Type); Assert.True(_viewModel.Site.HasAdresse); Assert.Equal("Paris", _viewModel.Site.Adresse!.City); } [Fact] public async Task Load_ContactsLiees() { // Arrange Guid siteId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site A", contactIds: [contactId.ToString()])); await PersonneRepository.UpsertAsync(CreatePersonne(contactId, "Contact A")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert Assert.True(_viewModel.HasContacts); var contact = Assert.Single(_viewModel.Site!.Contacts); Assert.Equal(contactId, contact.ContactGuid); } [Fact] public async Task Load_InterventionsLiees() { // Arrange Guid siteId = Guid.NewGuid(); Guid interventionId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site A")); await CacheSiteInterventionsAsync(siteId, CreateIntervention(interventionId, siteId, "INT-001", "Intervention A")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert Assert.True(_viewModel.HasInterventions); var intervention = Assert.Single(_viewModel.Site!.Interventions); Assert.Equal(interventionId, intervention.InterventionGuid); } [Fact] public async Task Load_RelationsVides_PasDErreur() { // Arrange : site sans relations. Guid siteId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site A")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert : collections vides, pas d'erreur. Assert.NotNull(_viewModel.Site); Assert.False(_viewModel.HasClients); Assert.False(_viewModel.HasContacts); Assert.False(_viewModel.HasInterventions); Assert.Null(_viewModel.ErrorMessage); } [Fact] public async Task OpenClient_NavigueVersClientDetail() { // Arrange Guid clientId = Guid.NewGuid(); var client = new SiteClient { ClientGuid = clientId, Code = "C001", Nom = "Client A" }; // Act _viewModel.OpenClientCommand.Execute(client); // Assert : navigation vers le détail du client avec le GUID public. Assert.Equal(AppPage.ClientDetail, _navigation.CurrentPage); Assert.Equal(clientId, _navigation.CurrentParameter); } [Fact] public async Task OpenClient_Null_NeNaviguePas() { // Act _viewModel.OpenClientCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task OpenContact_NavigueVersContactDetail() { // Arrange Guid siteId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site A", contactIds: [contactId.ToString()])); await PersonneRepository.UpsertAsync(CreatePersonne(contactId, "Contact A")); await _viewModel.LoadCommand.ExecuteAsync(siteId); // Act _viewModel.OpenContactCommand.Execute(_viewModel.Site!.Contacts[0]); // Assert : navigation vers le détail du contact avec le GUID public et le retour vers la fiche Site. Assert.Equal(AppPage.ContactDetail, _navigation.CurrentPage); var nav = Assert.IsType(_navigation.CurrentParameter); Assert.Equal(contactId, nav.Id); Assert.Equal(AppPage.SiteDetail, nav.ReturnContext.Page); Assert.Equal(siteId, nav.ReturnContext.Parameter); } [Fact] public async Task OpenIntervention_NavigueVersInterventionDetail() { // Arrange Guid siteId = Guid.NewGuid(); Guid interventionId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site A")); await CacheSiteInterventionsAsync(siteId, CreateIntervention(interventionId, siteId, "INT-001", "Intervention A")); await _viewModel.LoadCommand.ExecuteAsync(siteId); // Act _viewModel.OpenInterventionCommand.Execute(_viewModel.Site!.Interventions[0]); // Assert : navigation vers le détail de l'intervention avec le GUID public et le retour vers la fiche Site. Assert.Equal(AppPage.InterventionDetail, _navigation.CurrentPage); var nav = Assert.IsType(_navigation.CurrentParameter); Assert.Equal(interventionId, nav.Id); Assert.Equal(AppPage.SiteDetail, nav.ReturnContext.Page); Assert.Equal(siteId, nav.ReturnContext.Parameter); } [Fact] public async Task OpenContact_Null_NeNaviguePas() { // Act _viewModel.OpenContactCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task OpenIntervention_Null_NeNaviguePas() { // Act _viewModel.OpenInterventionCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task Back_RetourneALaListe() { // Act _viewModel.BackCommand.Execute(null); // Assert Assert.Equal(AppPage.Sites, _navigation.CurrentPage); } [Fact] public async Task Load_IdentitePublique_Conservee() { // Arrange Guid siteId = Guid.NewGuid(); await SiteRepository.UpsertAsync(CreateSite(siteId, "Site Alpha")); // Act await _viewModel.LoadCommand.ExecuteAsync(siteId); // Assert Assert.NotNull(_viewModel.Site); Assert.Equal(siteId, _viewModel.Site.SiteGuid); } }