using System.Text.Json; using Omika.App.Local.Models; using Omika.App.Local.Services; using Omika.App.Navigation; using Omika.App.ViewModels; 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 du détail d'un dossier : chargement, état introuvable, /// données affichées, interventions liées et navigation vers une intervention. /// public class DossierDetailViewModelTests : LocalTestBase { private readonly DossierDetailViewModel _viewModel; private readonly NavigationService _navigation; public DossierDetailViewModelTests() { var service = new DossierLocalService(DossierRepository, ClientRepository, DossierStateRepository, DossierInterventionCacheRepository, ApiClient); var clientService = new ClientLocalService(ClientRepository, DossierRepository, ContextInterventionCacheRepository, ClientStateRepository, ApiClient); _navigation = new NavigationService(); _viewModel = new DossierDetailViewModel(service, clientService, _navigation); } private static LocalDossier CreateDossier(Guid id, string reference, string titre, Guid? clientId = null, Guid? siteId = null) { var dto = new DossierDetailsDto { Id = id.ToString(), ReferenceMetier = reference, Titre = titre, ClientId = clientId?.ToString(), SiteId = siteId?.ToString(), Statut = new ReferenceValueDto { Label = "Ouvert" } }; return new LocalDossier { Id = id, ClientId = clientId, SiteId = siteId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalSite CreateSite(Guid id, string nom) { var dto = new SiteDetailsDto { Id = id.ToString(), Nom = nom }; return new LocalSite { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalClient CreateClient(Guid id, string nom) { var dto = new ClientDetailsDto { Id = id.ToString(), Nom = nom }; return new LocalClient { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } private static LocalIntervention CreateIntervention(Guid id, Guid dossierId, string reference, string titre) { var dto = new InterventionDetailsDto { Id = id.ToString(), Reference = reference, Titre = titre, DossierId = dossierId.ToString() }; return new LocalIntervention { Id = id, DossierId = dossierId, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } [Fact] public async Task Load_ChargementPuisDetail() { // Arrange Guid dossierId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "Dossier A")); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert : après chargement, détail affiché et chargement terminé. Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.Dossier); Assert.Equal("DOS-001", _viewModel.Dossier.Reference); } [Fact] public async Task Load_DossierIntrouvable_MessageErreur() { // Act await _viewModel.LoadCommand.ExecuteAsync(Guid.NewGuid()); // Assert Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.ErrorMessage); Assert.Null(_viewModel.Dossier); } [Fact] public async Task Load_DonneesAffichees() { // Arrange Guid dossierId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "Dossier A")); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert Assert.NotNull(_viewModel.Dossier); Assert.Equal("DOS-001", _viewModel.Dossier.Reference); Assert.Equal("Dossier A", _viewModel.Dossier.Titre); Assert.Equal("Ouvert", _viewModel.Dossier.Statut); } [Fact] public async Task Load_InterventionsLiees() { // Arrange : cache contextuel (pas la table historique Intervention). Guid dossierId = Guid.NewGuid(); Guid interventionId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A")); await DossierInterventionCacheRepository.ReplaceAsync( dossierId, [new InterventionDetailsDto { Id = interventionId.ToString(), Reference = "INT-001", Titre = "Intervention" }]); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert Assert.True(_viewModel.HasInterventions); var intervention = Assert.Single(_viewModel.Interventions); Assert.Equal(interventionId, intervention.InterventionGuid); } [Fact] public async Task OpenIntervention_NavigueVersDetail() { // Arrange : cache contextuel. Guid dossierId = Guid.NewGuid(); Guid interventionId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A")); await DossierInterventionCacheRepository.ReplaceAsync( dossierId, [new InterventionDetailsDto { Id = interventionId.ToString(), Reference = "INT-001", Titre = "Intervention" }]); await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Act _viewModel.OpenInterventionCommand.Execute(_viewModel.Interventions[0]); // Assert : navigation vers le détail de l'intervention avec le GUID public et le retour vers la fiche Dossier. Assert.Equal(AppPage.InterventionDetail, _navigation.CurrentPage); var nav = Assert.IsType(_navigation.CurrentParameter); Assert.Equal(interventionId, nav.Id); Assert.Equal(AppPage.DossierDetail, nav.ReturnContext.Page); Assert.Equal(dossierId, nav.ReturnContext.Parameter); } [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.Dossiers, _navigation.CurrentPage); } [Fact] public async Task Load_SansIntervention_EtatVide() { // Arrange Guid dossierId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A")); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert : dossier valide sans intervention. Assert.NotNull(_viewModel.Dossier); Assert.False(_viewModel.HasInterventions); Assert.Empty(_viewModel.Interventions); } [Fact] public async Task Load_AvecClient_HasClientVrai() { // Arrange Guid clientId = Guid.NewGuid(); Guid dossierId = Guid.NewGuid(); await ClientRepository.UpsertAsync(CreateClient(clientId, "Client Alpha")); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A", clientId)); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert Assert.True(_viewModel.HasClient); Assert.Equal("Client Alpha", _viewModel.Dossier!.ClientNom); } [Fact] public async Task Load_SansClient_HasClientFaux() { // Arrange Guid dossierId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A")); // Act await _viewModel.LoadCommand.ExecuteAsync(dossierId); // Assert Assert.False(_viewModel.HasClient); Assert.Null(_viewModel.Dossier!.ClientGuid); } [Fact] public async Task Load_NeReintroduitPasSite() { // La règle métier du lot 37B : le détail Dossier n'expose pas de Site. Guid dossierId = Guid.NewGuid(); await DossierRepository.UpsertAsync(CreateDossier(dossierId, "DOS-001", "A")); await _viewModel.LoadCommand.ExecuteAsync(dossierId); Assert.NotNull(_viewModel.Dossier); Assert.Null(_viewModel.Dossier!.SiteNom); } }