329 lines
13 KiB
C#
329 lines
13 KiB
C#
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);
|
||
}
|
||
}
|