343 lines
11 KiB
C#
343 lines
11 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<bool> 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<string>? 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<InterventionDetailsDto>(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<DetailNavigation>(_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<DetailNavigation>(_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);
|
|
}
|
|
}
|