323 lines
10 KiB
C#
323 lines
10 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<SiteDto>? sites = null,
|
|
IReadOnlyList<ContactDto>? 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<InterventionDetailsDto>(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<DetailNavigation>(_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);
|
|
}
|
|
}
|