using System.Text.Json; using Omika.App.Local.Abstractions; 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; /// /// Tests des bornes de plage du service Agenda : chargement d'une journée ou /// d'une semaine avec une seule opération locale et des bornes robustes. /// public class AgendaServiceRangeTests : LocalTestBase { private readonly AgendaService _service; public AgendaServiceRangeTests() { _service = new AgendaService( RendezVousRepository, ClientRepository, SiteRepository, InterventionRepository, CollaborateurRepository, ApiClient, SessionService); } private static LocalRendezVous CreateRendezVous( Guid id, DateTime debut, DateTime fin, string titre, bool estArchive = false) { var dto = new RendezVousDetailsDto { Id = id.ToString(), Titre = titre, Debut = debut, Fin = fin, Statut = new ReferenceValueDto { Label = "Planifié" }, EstArchive = estArchive }; return new LocalRendezVous { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } [Fact] public async Task Plage_BornesJournee_00h00Inclus_23h59Inclus_LendemainExclu() { // Arrange DateTime jour = new(2026, 8, 12); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), jour.Date, jour.Date.AddHours(1), "00:00")); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), jour.Date.AddHours(23).AddMinutes(59), jour.Date.AddHours(23).AddMinutes(59).AddMinutes(1), "23:59")); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), jour.AddDays(1).Date, jour.AddDays(1).Date.AddHours(1), "lendemain 00:00")); // Act : plage d'un jour, borne fin exclue (lendemain 00:00). IReadOnlyList result = await _service.GetRendezVousForRangeAsync(jour.Date, jour.Date.AddDays(1)); // Assert : 00:00 inclus, 23:59 inclus (via borne < lendemain 00:00), lendemain exclu. Assert.Equal(2, result.Count); Assert.Contains(result, x => x.Titre == "00:00"); Assert.Contains(result, x => x.Titre == "23:59"); Assert.DoesNotContain(result, x => x.Titre == "lendemain 00:00"); } [Fact] public async Task Plage_BornesSemaine_LundiInclus_DimancheInclus_LundiSuivantExclu() { // Arrange : 2026-08-10 est un lundi. DateTime lundi = new(2026, 8, 10); DateTime dimanche = lundi.AddDays(6); DateTime lundiSuivant = lundi.AddDays(7); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), lundi.Date, lundi.Date.AddHours(1), "lundi")); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), dimanche.Date.AddHours(23).AddMinutes(59), dimanche.Date.AddHours(23).AddMinutes(59).AddMinutes(1), "dimanche")); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), lundiSuivant.Date, lundiSuivant.Date.AddHours(1), "lundi suivant")); // Act : lundi inclus → dimanche inclus, lundi suivant exclu. IReadOnlyList result = await _service.GetRendezVousForRangeAsync(lundi, lundiSuivant); // Assert Assert.Equal(2, result.Count); Assert.Contains(result, x => x.Titre == "lundi"); Assert.Contains(result, x => x.Titre == "dimanche"); Assert.DoesNotContain(result, x => x.Titre == "lundi suivant"); } [Fact] public async Task Plage_RendezVousPlusieursJours_VisibleChaqueJourDeSonIntervalle() { // Arrange : un rendez-vous groupé sur plusieurs jours (12 → 14/08). DateTime debut = new(2026, 8, 12, 8, 0, 0); DateTime fin = new(2026, 8, 14, 17, 0, 0); Guid id = Guid.NewGuid(); await RendezVousRepository.UpsertAsync(CreateRendezVous(id, debut, fin, "chantier 3 jours")); // Act / Assert : présent sur CHAQUE journée de l'intervalle… foreach (DateTime jour in new[] { debut.Date, debut.Date.AddDays(1), fin.Date }) { IReadOnlyList result = await _service.GetRendezVousForRangeAsync(jour, jour.AddDays(1)); AgendaItem item = Assert.Single(result); Assert.Equal("chantier 3 jours", item.Titre); Assert.True(item.CouvreLeJour(jour)); } // … et absent des journées hors intervalle. Assert.Empty(await _service.GetRendezVousForRangeAsync(fin.Date.AddDays(1), fin.Date.AddDays(2))); Assert.Empty(await _service.GetRendezVousForRangeAsync(debut.Date.AddDays(-2), debut.Date.AddDays(-1))); } [Fact] public async Task Plage_RendezVousCommenceAvantLaPlageMaisLaCouvre_ResteVisible() { // Arrange : un chantier commencé le 10/08 et fini le 20/08. await RendezVousRepository.UpsertAsync( CreateRendezVous(Guid.NewGuid(), new DateTime(2026, 8, 10, 8, 0, 0), new DateTime(2026, 8, 20, 17, 0, 0), "long")); // Act : plage du 14 au 21/08 — le rendez-vous commence AVANT la plage. IReadOnlyList result = await _service.GetRendezVousForRangeAsync( new DateTime(2026, 8, 14), new DateTime(2026, 8, 21)); // Assert : il est conservé car il couvre encore la plage. AgendaItem item = Assert.Single(result); Assert.Equal("long", item.Titre); } [Fact] public async Task Plage_JourneeUnique_RetourneLesRendezVousDuJour() { // Arrange DateTime jour = new(2026, 8, 12); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), jour.AddHours(9), jour.AddHours(10), "A")); await RendezVousRepository.UpsertAsync(CreateRendezVous(Guid.NewGuid(), jour.AddDays(1).AddHours(9), jour.AddDays(1).AddHours(10), "B")); // Act IReadOnlyList result = await _service.GetRendezVousForDayAsync(jour); // Assert : GetRendezVousForDayAsync délègue à la plage d'un jour. AgendaItem item = Assert.Single(result); Assert.Equal("A", item.Titre); } /// /// Règle métier : un rendez-vous ARCHIVÉ ne monte jamais dans le mobile. Une /// copie locale peut subsister (snapshot antérieur au filtre serveur, données /// déjà synchronisées) : l'agenda ne doit jamais l'afficher. /// [Fact] public async Task Plage_RendezVousArchive_NApparaitPas() { // Arrange : un rendez-vous courant et un rendez-vous archivé, même jour. DateTime jour = new(2026, 8, 12); await RendezVousRepository.UpsertAsync( CreateRendezVous(Guid.NewGuid(), jour.AddHours(9), jour.AddHours(10), "courant")); await RendezVousRepository.UpsertAsync( CreateRendezVous(Guid.NewGuid(), jour.AddHours(14), jour.AddHours(15), "archivé", estArchive: true)); // Act IReadOnlyList result = await _service.GetRendezVousForDayAsync(jour); // Assert : seul le rendez-vous courant subsiste. AgendaItem item = Assert.Single(result); Assert.Equal("courant", item.Titre); } }