Files
Omika/tests/Omika.Tests/Mobile/Local/AgendaServiceAffectationTests.cs
2026-10-23 18:46:50 +02:00

251 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.Json;
using Omika.App.Local.Models;
using Omika.App.Local.Services;
using Omika.Core.Contracts.Common;
using Omika.Core.Contracts.Planning;
namespace Omika.Tests.Mobile.Local;
/// <summary>
/// Rendez-vous de groupe : l'unité affichée est l'AFFECTATION
/// (<c>RDV_Collaborateur</c>), jamais le noyau du rendez-vous. Une carte par
/// collaborateur et par jour, avec la date, les heures et le collaborateur de la
/// ligne réelle ; la fiche conserve l'affectation ouverte et expose, à titre
/// informatif, les jours et les collaborateurs du rendez-vous.
/// </summary>
public class AgendaServiceAffectationTests : LocalTestBase
{
private static readonly DateTime Jour1 = new(2026, 8, 12);
private static readonly DateTime Jour2 = new(2026, 8, 13);
private static readonly Guid CollaborateurA = Guid.Parse("11111111-1111-1111-1111-111111111111");
private static readonly Guid CollaborateurB = Guid.Parse("22222222-2222-2222-2222-222222222222");
private readonly AgendaService _service;
public AgendaServiceAffectationTests()
{
_service = new AgendaService(
RendezVousRepository,
ClientRepository,
SiteRepository,
InterventionRepository,
CollaborateurRepository,
ApiClient,
SessionService);
}
private static AffectationCollaborateurDto Affectation(
Guid affectationId,
Guid collaborateurId,
string nom,
DateTime debut,
DateTime fin)
=> new()
{
Id = affectationId.ToString("D"),
CollaborateurId = collaborateurId.ToString("D"),
NomAffiche = nom,
PeriodeDebut = debut,
PeriodeFin = fin
};
/// <summary>
/// Rendez-vous groupé sur DEUX jours avec DEUX collaborateurs : quatre
/// affectations, chacune avec son créneau.
/// </summary>
private async Task<(Guid RendezVousId, Guid AffectationAJour2, Guid AffectationBJour2)> PreparerGroupeAsync()
{
Guid rendezVousId = Guid.NewGuid();
Guid aJour1 = Guid.NewGuid();
Guid aJour2 = Guid.NewGuid();
Guid bJour1 = Guid.NewGuid();
Guid bJour2 = Guid.NewGuid();
var dto = new RendezVousDetailsDto
{
Id = rendezVousId.ToString(),
Titre = "Chantier groupé",
Debut = Jour1.AddHours(8),
Fin = Jour2.AddHours(17),
Statut = new ReferenceValueDto { Label = "Planifié" },
EstGroupe = true,
Collaborateurs =
[
Affectation(aJour1, CollaborateurA, "Alice Martin", Jour1.AddHours(8), Jour1.AddHours(12)),
Affectation(aJour2, CollaborateurA, "Alice Martin", Jour2.AddHours(8), Jour2.AddHours(12)),
Affectation(bJour1, CollaborateurB, "Bruno Petit", Jour1.AddHours(13), Jour1.AddHours(17)),
Affectation(bJour2, CollaborateurB, "Bruno Petit", Jour2.AddHours(13), Jour2.AddHours(17))
]
};
await RendezVousRepository.UpsertAsync(new LocalRendezVous
{
Id = rendezVousId,
PayloadJson = JsonSerializer.Serialize(dto),
UpdatedAtUtc = DateTimeOffset.UtcNow
});
return (rendezVousId, aJour2, bJour2);
}
[Fact]
public async Task Agenda_UneCarteParAffectation_AvecLeBonCollaborateurEtLeBonCreneau()
{
// Arrange : aucune portée connue → toutes les affectations sont présentées.
(Guid rendezVousId, _, _) = await PreparerGroupeAsync();
// Act
IReadOnlyList<AgendaItem> result = await _service.GetRendezVousForRangeAsync(Jour1, Jour2.AddDays(1));
// Assert : une carte par affectation (2 collaborateurs × 2 jours).
Assert.Equal(4, result.Count);
// La carte d'Alice au 2e jour porte BIEN le créneau du 2e jour (et non
// celui du premier rendez-vous du groupe).
AgendaItem aliceJour2 = Assert.Single(result, i =>
i.CollaborateurNom == "Alice Martin" && i.Debut.Date == Jour2);
Assert.Equal(Jour2.AddHours(8), aliceJour2.Debut);
Assert.Equal(Jour2.AddHours(12), aliceJour2.Fin);
AgendaItem brunoJour2 = Assert.Single(result, i =>
i.CollaborateurNom == "Bruno Petit" && i.Debut.Date == Jour2);
Assert.Equal(Jour2.AddHours(13), brunoJour2.Debut);
Assert.Equal(Jour2.AddHours(17), brunoJour2.Fin);
// Toutes les cartes se rattachent au même rendez-vous (noyau).
Assert.All(result, item => Assert.Equal(rendezVousId, item.RendezVousGuid));
}
[Fact]
public async Task Agenda_PorteeCollaborateur_NePresenteQueSesPropresCreneaux()
{
// Arrange : agenda personnel d'Alice (bootstrap local).
await PreparerGroupeAsync();
SessionService.Bootstrap = new LocalBootstrapState
{
Collaborateur = new Omika.Core.Contracts.Organisation.CollaborateurDetailsDto
{
Id = CollaborateurA.ToString("D"),
Nom = "Martin",
Prenom = "Alice"
}
};
// Act
IReadOnlyList<AgendaItem> result = await _service.GetRendezVousForRangeAsync(Jour1, Jour2.AddDays(1));
// Assert : uniquement les deux créneaux d'Alice (un par jour).
Assert.Equal(2, result.Count);
Assert.All(result, item => Assert.Equal("Alice Martin", item.CollaborateurNom));
Assert.Equal([Jour1.AddHours(8), Jour2.AddHours(8)], result.Select(i => i.Debut).OrderBy(d => d).ToList());
}
[Fact]
public async Task Fiche_AffectationPrecise_GardeLeCreneauEtLeCollaborateurDeLaCarte()
{
// Arrange : la fiche est ouverte depuis la carte « Bruno – 2e jour ».
(Guid rendezVousId, _, Guid affectationBJour2) = await PreparerGroupeAsync();
// Act
AgendaItem? item = await _service.GetRendezVousByIdAsync(
rendezVousId,
default,
affectationBJour2.ToString("D"));
// Assert : créneau et collaborateur de l'affectation ouverte.
Assert.NotNull(item);
Assert.Equal(Jour2.AddHours(13), item!.Debut);
Assert.Equal(Jour2.AddHours(17), item.Fin);
Assert.Equal("Bruno Petit", item.CollaborateurNom);
Assert.Equal(affectationBJour2.ToString("D"), item.AffectationId);
}
[Fact]
public async Task Fiche_Groupe_ExposeLesJoursEtTousLesCollaborateurs()
{
// Arrange
(Guid rendezVousId, Guid affectationAJour2, _) = await PreparerGroupeAsync();
// Act
AgendaItem? item = await _service.GetRendezVousByIdAsync(
rendezVousId,
default,
affectationAJour2.ToString("D"));
// Assert : les deux jours du rendez-vous et les deux collaborateurs sont
// exposés pour le bloc informatif de la fiche, avec pour chaque journée le
// créneau réellement occupé (du premier démarrage à la dernière fin).
Assert.NotNull(item);
Assert.Equal([Jour1, Jour2], item!.JoursDuGroupe);
Assert.Equal(2, item.JoursAvecHoraires.Count);
Assert.Equal(Jour1.AddHours(8), item.JoursAvecHoraires[0].Debut);
Assert.Equal(Jour1.AddHours(17), item.JoursAvecHoraires[0].Fin);
Assert.Equal(Jour2.AddHours(8), item.JoursAvecHoraires[1].Debut);
Assert.Equal(Jour2.AddHours(17), item.JoursAvecHoraires[1].Fin);
Assert.Equal(2, item.Collaborateurs.Count);
Assert.Contains("Alice Martin", item.Collaborateurs);
Assert.Contains("Bruno Petit", item.Collaborateurs);
Assert.True(item.AfficheBlocPeriode);
// Le collaborateur de l'affectation OUVERTE est identifié : c'est lui qui
// sera exclu du bloc (sa propre fiche) et dont les journées sont présentées.
Assert.Equal(CollaborateurA, item.CollaborateurOccurrenceGuid);
// Journées PAR collaborateur : Alice n'a que son créneau du matin, Bruno
// celui de l'après-midi — aucun des deux n'hérite des heures de l'autre.
Assert.Equal(2, item.JoursParCollaborateur.Count);
JoursParCollaborateur joursAlice = Assert.Single(
item.JoursParCollaborateur, j => j.CollaborateurGuid == CollaborateurA);
Assert.Equal([Jour1, Jour2], joursAlice.Jours.Select(j => j.Jour).ToList());
Assert.All(joursAlice.Jours, j =>
{
Assert.Equal(j.Jour.AddHours(8), j.Debut);
Assert.Equal(j.Jour.AddHours(12), j.Fin);
});
JoursParCollaborateur joursBruno = Assert.Single(
item.JoursParCollaborateur, j => j.CollaborateurGuid == CollaborateurB);
Assert.Equal([Jour1, Jour2], joursBruno.Jours.Select(j => j.Jour).ToList());
Assert.All(joursBruno.Jours, j =>
{
Assert.Equal(j.Jour.AddHours(13), j.Debut);
Assert.Equal(j.Jour.AddHours(17), j.Fin);
});
}
[Fact]
public async Task Fiche_RendezVousSimple_NAffichePasLeBlocGroupe()
{
// Arrange : rendez-vous d'une journée, un seul collaborateur.
Guid rendezVousId = Guid.NewGuid();
Guid affectationId = Guid.NewGuid();
var dto = new RendezVousDetailsDto
{
Id = rendezVousId.ToString(),
Titre = "Visite",
Debut = Jour1.AddHours(9),
Fin = Jour1.AddHours(10),
Collaborateurs =
[
Affectation(affectationId, CollaborateurA, "Alice Martin", Jour1.AddHours(9), Jour1.AddHours(10))
]
};
await RendezVousRepository.UpsertAsync(new LocalRendezVous
{
Id = rendezVousId,
PayloadJson = JsonSerializer.Serialize(dto),
UpdatedAtUtc = DateTimeOffset.UtcNow
});
// Act
AgendaItem? item = await _service.GetRendezVousByIdAsync(rendezVousId, default, affectationId.ToString("D"));
// Assert : bloc informatif masqué (un jour, un collaborateur).
Assert.NotNull(item);
Assert.False(item!.AfficheBlocPeriode);
Assert.Single(item.JoursDuGroupe);
}
}