Files
Omika/tests/Omika.Tests/Contracts/TempsObligatoireRulesTests.cs
T
2026-10-23 18:46:50 +02:00

329 lines
13 KiB
C#
Raw 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 Omika.Core.Contracts.Configuration;
namespace Omika.Tests.Contracts;
/// <summary>
/// Tests de la règle centrale d'obligation de saisie d'un type de temps sur un
/// rendez-vous (<see cref="TempsObligatoireRules"/>).
///
/// <para>
/// Les cas de référence sont ceux du métier : pause déjeuner autorisée dans les
/// rendez-vous de type intervention, plage horaire 12:00 → 15:00, saisie
/// obligatoire dès que le rendez-vous dépasse 30 minutes.
/// </para>
/// </summary>
public class TempsObligatoireRulesTests
{
private static readonly DateTime Jour = new(2026, 3, 10);
private static DateTime Heure(int heures, int minutes = 0) => Jour.AddHours(heures).AddMinutes(minutes);
/// <summary>Type de temps « pause déjeuner » : 12:00 → 15:00, minimum 10 minutes.</summary>
private static TypeTempsParametresDto PauseDejeuner(
string? tempsMin = "10",
string? obligatoire = "0030",
string? typeRdv = null,
string? heureDebut = "120000",
string? heureFin = "150000")
=> new()
{
Obligatoire = obligatoire,
HeureDebut = heureDebut,
HeureFin = heureFin,
TempsMin = tempsMin,
TypeRdv = typeRdv
};
/// <summary>Type de rendez-vous « intervention » exigeant une saisie de temps.</summary>
private static TypeRdvParametresDto TypeRdvIntervention(bool saisirTempsObligatoire = true)
=> new() { SaisirTempsObligatoire = saisirTempsObligatoire };
// ===== Déclenchement par le seuil et la plage horaire =====
[Fact]
public void EstObligatoire_RendezVous8h17h_QuiEnglobeLaPlage_EstObligatoire()
{
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(8), Heure(17));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous11h15h_QuiEnglobeLaPlage_EstObligatoire()
{
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(11), Heure(15));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous13h15h_CreneauLibreAvant_NAPasEteObligatoire()
{
// 12:00 → 13:00 reste libre : la pause a pu être prise avant le rendez-vous.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(13), Heure(15));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous11h14h_CreneauLibreApres_NAPasEteObligatoire()
{
// 14:00 → 15:00 reste libre : la pause peut venir après le rendez-vous.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(11), Heure(14));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous12h30_15h_CreneauLibre30Min_AvecMinimum10Min_NAPasEteObligatoire()
{
// 12:00 → 12:30 reste libre, soit 30 minutes : de quoi prendre une pause
// dont la durée minimale est de 10 minutes.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(tempsMin: "10"), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(12, 30), Heure(15));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous12h30_15h_CreneauLibre30Min_AvecMinimum1Heure_EstObligatoire()
{
// Mêmes horaires, mais une pause d'au moins 1 heure ne tient plus dans les
// 30 minutes libres avant le rendez-vous : le collaborateur doit la déclarer.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(tempsMin: "0100"), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(12, 30), Heure(15));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_RendezVous13h30_15h_CreneauLibre1h30_AvecMinimum1Heure_NAPasEteObligatoire()
{
// 12:00 → 13:30 reste libre (1 h 30) : la pause d'une heure peut y être prise.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(tempsMin: "0100"), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(13, 30), Heure(15));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_CreneauLibreInsuffisantDesDeuxCotes_EstObligatoire()
{
// 30 minutes avant et 30 minutes après, mais une pause d'une heure : aucun
// créneau libre n'est assez long, la saisie est obligatoire.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(tempsMin: "0100"), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(12, 30), Heure(14, 30));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_SansTempsMin_NAExigeQueSiAucunCreneauLibre()
{
TypeTempsParametresDto sansMinimum = PauseDejeuner(tempsMin: null);
// 5 minutes libres suffisent à pouvoir prendre le temps ailleurs.
Assert.False(TempsObligatoireRules.EstObligatoire(
sansMinimum, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(12, 5), Heure(15)));
// Aucun créneau libre : la saisie est obligatoire.
Assert.True(TempsObligatoireRules.EstObligatoire(
sansMinimum, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(11), Heure(16)));
}
// ===== Paramètres absents ou nuls : aucune restriction =====
[Fact]
public void EstObligatoire_SeuilVideOuZero_NAPasEteObligatoire()
{
Assert.False(TempsObligatoireRules.EstObligatoire(
PauseDejeuner(obligatoire: null), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(8), Heure(17)));
Assert.False(TempsObligatoireRules.EstObligatoire(
PauseDejeuner(obligatoire: "0"), TypeRdvIntervention(), "T.RDV.INTER", "Intervention",
Heure(8), Heure(17)));
}
[Fact]
public void EstObligatoire_RendezVousPlusCourtQueLeSeuil_NAPasEteObligatoire()
{
// 20 minutes de rendez-vous pour un seuil de 30 minutes.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(12), Heure(12, 20));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_TypeRdvSansSaisieObligatoire_NAPasEteObligatoire()
{
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(saisirTempsObligatoire: false), "T.RDV.INTER", "Intervention",
Heure(8), Heure(17));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_ParametresDuTypeDeTempsAbsents_NAPasEteObligatoire()
{
bool obligatoire = TempsObligatoireRules.EstObligatoire(
null, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(8), Heure(17));
Assert.False(obligatoire);
}
[Fact]
public void EstObligatoire_SansPlageHoraire_LeSeuilSeulDeclenche()
{
// Aucune borne d'horaire : aucune restriction, le seuil décide.
Assert.True(TempsObligatoireRules.EstObligatoire(
PauseDejeuner(heureDebut: null, heureFin: null), TypeRdvIntervention(),
"T.RDV.INTER", "Intervention", Heure(8), Heure(17)));
Assert.True(TempsObligatoireRules.EstObligatoire(
PauseDejeuner(heureDebut: "000000", heureFin: "000000"), TypeRdvIntervention(),
"T.RDV.INTER", "Intervention", Heure(8), Heure(17)));
}
// ===== Premier paramètre : type de rendez-vous autorisé =====
[Fact]
public void EstObligatoire_TypeDeTempsNonAutorisePourCeTypeDeRdv_NAPasEteObligatoire()
{
TypeTempsParametresDto pause = PauseDejeuner(typeRdv: "T.RDV.INTER");
Assert.True(TempsObligatoireRules.EstObligatoire(
pause, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(8), Heure(17)));
Assert.False(TempsObligatoireRules.EstObligatoire(
pause, TypeRdvIntervention(), "T.RDV.SAV", "SAV", Heure(8), Heure(17)));
}
[Fact]
public void EstObligatoire_TypeDeRdvAutoriseParSonLibelle_EstObligatoire()
{
// Génération Batik : le type de temps liste le libellé, le rendez-vous non
// plus (aucune clé métier disponible).
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(typeRdv: "Intervention"), TypeRdvIntervention(), null, "Intervention",
Heure(8), Heure(17));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_AncienCodeTypeAct_EstPrisEnCompte()
{
TypeTempsParametresDto pause = PauseDejeuner(typeRdv: null);
pause = pause with { TypeAct = "T.RDV.INTER" };
Assert.True(TempsObligatoireRules.EstObligatoire(
pause, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(8), Heure(17)));
}
[Fact]
public void EstAutorise_ListeVide_AutoriseTousLesTypesDeRdv()
{
Assert.True(TempsObligatoireRules.EstAutorise(PauseDejeuner(), "T.RDV.SAV", "SAV"));
Assert.True(TempsObligatoireRules.EstAutorise(null, "T.RDV.SAV", "SAV"));
Assert.True(TempsObligatoireRules.EstAutorise(PauseDejeuner(), null, null));
}
[Fact]
public void EstAutorise_TypeDeRdvIndetermine_NAInterditPasLeTypeDeTemps()
{
// Sans type de rendez-vous connu, aucune restriction ne peut être appliquée :
// le type de temps reste saisissable.
Assert.True(TempsObligatoireRules.EstAutorise(PauseDejeuner(typeRdv: "T.RDV.INTER"), null, null));
}
// ===== Plage horaire : bornes partielles, créneau libre =====
[Fact]
public void EstObligatoire_PlageSansHeureDeFin_SEnTendJusquEnFinDeJournee()
{
// Plage 12:00 → fin de journée : un rendez-vous 12:00 → 24:00 la couvre
// entièrement, la pause ne peut plus être prise ailleurs.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(heureDebut: "120000", heureFin: null), TypeRdvIntervention(),
"T.RDV.INTER", "Intervention", Heure(12), Heure(24));
Assert.True(obligatoire);
}
[Fact]
public void EstObligatoire_PlageQuiChevaucheMinuit_EstLueSurLeLendemain()
{
// Plage 22:00 → 02:00 (nuit) et rendez-vous 22:30 → 02:00 : il reste
// 22:00 → 22:30 (30 minutes) pour prendre le temps ailleurs.
TypeTempsParametresDto tempsNuit = PauseDejeuner(
heureDebut: "220000", heureFin: "020000", tempsMin: "10");
Assert.False(TempsObligatoireRules.EstObligatoire(
tempsNuit, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(22, 30), Heure(26)));
// Même plage, rendez-vous 22:00 → 02:00 : plus aucune place libre.
Assert.True(TempsObligatoireRules.EstObligatoire(
tempsNuit, TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(22), Heure(26)));
}
[Fact]
public void PlusGrandCreneauLibreMinutes_RetourneLePlusGrandCote()
{
Assert.Equal(60, TempsObligatoireRules.PlusGrandCreneauLibreMinutes(
PauseDejeuner(), Heure(13), Heure(15)));
Assert.Equal(120, TempsObligatoireRules.PlusGrandCreneauLibreMinutes(
PauseDejeuner(), Heure(14), Heure(16)));
Assert.Equal(0, TempsObligatoireRules.PlusGrandCreneauLibreMinutes(
PauseDejeuner(), Heure(11), Heure(16)));
}
[Fact]
public void PlusGrandCreneauLibreMinutes_SansPlageHoraire_EstIndetermine()
{
Assert.Null(TempsObligatoireRules.PlusGrandCreneauLibreMinutes(
PauseDejeuner(heureDebut: null, heureFin: null), Heure(8), Heure(17)));
Assert.Null(TempsObligatoireRules.PlusGrandCreneauLibreMinutes(null, Heure(8), Heure(17)));
}
[Fact]
public void PlageTexte_FormateLaPlageAutorisee()
{
Assert.Equal("12:00–15:00", TempsObligatoireRules.PlageTexte(PauseDejeuner()));
Assert.Null(TempsObligatoireRules.PlageTexte(PauseDejeuner(heureDebut: null, heureFin: null)));
Assert.Null(TempsObligatoireRules.PlageTexte(null));
}
[Fact]
public void PlusGrandCreneauLibreMinutes_RendezVousHorsPlage_LaisseTouteLaPlage()
{
// Rendez-vous du matin : toute la plage 12:00 → 15:00 reste libre.
Assert.Equal(180, TempsObligatoireRules.PlusGrandCreneauLibreMinutes(
PauseDejeuner(), Heure(8), Heure(11)));
}
[Fact]
public void EstObligatoire_RendezVousTermineAvantLaPlage_NAPasEteObligatoire()
{
// Matinée 8:00 → 9:00 (plus d'une heure de rendez-vous) : la pause reste
// possible dans la plage horaire, elle n'est donc pas obligatoire.
bool obligatoire = TempsObligatoireRules.EstObligatoire(
PauseDejeuner(), TypeRdvIntervention(), "T.RDV.INTER", "Intervention", Heure(8), Heure(9));
Assert.False(obligatoire);
}
}