Aller au contenu

Vue filtrée pour Administrateur Planificateur Gestionnaire de patrimoine Enseignant Étudiant Tout afficher

Les chemins qui échouent

Vérifié contre le produit le .

Un refus réussi est un succès de test. Cette page vérifie que le produit bloque ce qu’il doit bloquer — et c’est ce qu’un client demandera de voir avant les fonctionnalités.

Domaine transverse — les huit familles de refus du produit
Durée ~25 min pour les huit familles
Niveau complet
Étiquette manuel — trois familles exigent deux sessions simultanées, et la famille « permissions » suppose de comparer deux écrans du même produit vus par deux rôles

Trois choses, et il faut les trois. Un refus qui ne satisfait que les deux premières est un défaut.

  1. L’action est bloquée — rien ne s’est écrit.
  2. Le message est compréhensible — il nomme la cause, il ne dit pas « interdit ». Le texte exact est cité dans chaque cas.
  3. L’état n’a pas changé — c’est la vérification qu’on oublie, et la seule qui prouve qu’un refus n’a pas laissé de dégât derrière lui.

Formulez les résultats positivement. « Le produit refuse et affiche “Quota atteint : 4 h réservées cette semaine sur 4 h autorisées.” » n’est pas un échec : c’est la garantie qui tient.

Socle B pour les familles 1 à 5 et 7, socle A pour la famille 3, le montage étant sur la préparation commune. La famille 6 est autonome, la famille 8 exige deux navigateurs.


Les quotas Une limite chiffrée qu'un profil ne peut pas dépasser — heures par semaine, réservations simultanées, durée maximale d'une demande. se posent sur le profil « Demandeur », depuis Administration → Règles de réservation. Les tests se jouent avec bob.

# Ce qu’on tente Le produit refuse et affiche L’état après
1.1 Réserver 1 h de plus après avoir consommé un quota de 2 h « Quota atteint : 2 h réservées cette semaine sur 2 h autorisées. » aucune réservation créée ; les 2 h précédentes sont intactes
1.2 Réserver au-delà de l’horizon autorisé « Réservation trop lointaine : J+30 demandé, maximum J+7. » rien créé
1.3 Réserver plus long que la durée maximale « Durée trop longue : 120 min demandées, maximum 60 min. » rien créé
1.4 Réserver hors de la plage horaire autorisée « Réservations autorisées uniquement entre 08:00 et 12:00. » rien créé

À vérifier en plus : la même semaine, la réservation initiale est toujours « Confirmée ». Un refus de quota ne consomme rien et n’annule rien.

Étiquette : automatisable. Couvert par apps/api/src/booking-rules/booking-rules.e2e.spec.ts. Traçabilité : STORY-012, V61.


# Ce qu’on tente Le produit refuse et affiche L’état après
2.1 Réserver une salle déjà réservée sur le même créneau « Cette salle vient d’être prise. Alternatives sur le même créneau : » suivi de salles cliquables la réservation existante est intacte ; aucune seconde réservation
2.2 Poser une séance sur une salle déjà occupée « Salle occupée : B-201 · 09:45 – 11:15 » la grille n’a pas bougé — il n’y a pas de mise à jour optimiste
2.3 Poser une séance là où l’enseignant a déjà cours « Enseignant déjà en cours : Hugo Martin · 09:45 – 11:15 » idem
2.4 Poser une séance là où le groupe a déjà cours « Groupe déjà en cours : L1 A · 09:45 – 11:15 » idem
2.5 Réserver une salle couverte par une indisponibilité la salle n’apparaît pas dans les résultats ; avec la case gestionnaire, elle est grisée avec « Indisponible — Réfection du sol » et son bouton « Réserver » est inactif rien créé
2.6 Réserver pendant une période gelée « Période gelée “Vacances de Noël” : aucune réservation possible sur cette période. » rien créé

Le point de fond : séances, réservations et indisponibilités partagent une seule vérité d’occupation. Un brouillon occupe la salle exactement comme une réservation confirmée — le cas 2.2 le prouve même sur une séance non publiée.

Étiquette : manuel pour 2.1 (deux navigateurs), automatisable pour les autres. Couvert par apps/api/src/occupancy/occupancy.e2e.spec.ts et course-placement.e2e.spec.ts. Traçabilité : STORY-013, STORY-016, STORY-018.


Socle A. Ces refus se lisent sur la grille avant même d’être tentés : une cellule rendue impossible par une contrainte dure Une règle qui ne se négocie pas : tant qu'elle est violée, le placement est impossible — une salle déjà prise, un enseignant absent. est hachurée et ne porte aucun chiffre.

# Ce qu’on tente Le produit refuse et affiche L’état après
3.1 Placer un cours le mercredi, quand son enseignant est indisponible la cellule est hachurée ; le survol donne la raison, et le placement n’est pas proposé rien créé
3.2 Forcer une salle sans l’équipement requis, depuis le détail du créneau la salle est non sélectionnable, avec son motif : « A-101 — Salle TP — exclue : Équipement requis manquant » rien créé
3.3 Placer un cours dont la fenêtre déborde la plage d’ouverture « Dépasse les heures d’ouverture (08:00 – 20:00) » rien créé
3.4 Ouvrir le détail d’une cellule entièrement exclue toutes les raisons sont listées, chacune avec « Bloque 14 salle(s) candidate(s) », plus « Créneau exclu : les contraintes souples ne sont pas évaluées. »

La différence à faire constater : une contrainte souple ne refuse rien. Elle fait baisser le score, et le placement reste possible. Un testeur qui cherche un refus sur une contrainte souple cherchera longtemps.

Étiquette : manuel — la lecture d’une cellule hachurée est visuelle. Couvert partiellement par apps/api/src/scoring/scoring.e2e.spec.ts. Traçabilité : STORY-021, STORY-023, STORY-026.


Le protocole de cette famille est différent des autres, et c’est le point à comprendre.

Le produit filtre la navigation ; il ne la désactive pas. Il n’y a ni entrée grisée, ni cadenas : ce qui n’est pas permis n’existe pas à l’écran.

On ne cherche donc pas un élément désactivé : on constate une absence.

Ce qui suppose de savoir à quoi ressemble l’écran quand on y a droit. Comparez deux sessions, ou relevez la liste des entrées avec un administrateur avant de vous connecter avec le rôle restreint.

# Ce qu’on tente Ce qu’on doit constater L’état après
4.1 Se connecter avec un compte sans planning.edit, chercher « Planification » l’entrée est absente du menu — pas grisée
4.2 Ouvrir /planning par l’adresse directe redirection vers l’accueil, sans message de refus rien
4.3 Compte sans planning.solve sur la grille le bouton « Placement optimisé » est absent de la barre d’actions rien
4.4 Compte sans planning.publish avec des changements en attente le bandeau « … changement(s) non publié(s) sur cette semaine. » est là, sans bouton à sa droite rien
4.5 Compte sans audit.read ouvrant /admin/audit redirection vers l’accueil rien
4.6 Comparer « Vos accès » sur l’accueil entre deux rôles les cartes diffèrent — c’est la liste exhaustive des droits de chacun

Étiquette : manuel — la comparaison entre deux rôles ne s’automatise pas honnêtement côté interface. Couvert côté serveur par apps/api/src/rbac/rbac.e2e.spec.ts. Traçabilité : STORY-004, V60.


C’est le refus le plus surprenant du produit, et il mérite d’être joué lentement.

Un rôle limité au campus A ne donne aucun droit sur une ressource du campus B — cela, tout le monde l’attend. Ce qu’on n’attend pas :

Un rôle limité au campus A ne donne aucun droit sur une ressource dont le campus n’est pas renseigné.

Dans le doute, la réponse est non. Une donnée manquante n’est pas un doute favorable.

# Ce qu’on tente Ce qu’on doit constater L’état après
5.1 dave, « Valideur A » limité au campus A, ouvre Validations avec une demande sur le campus A il la voit
5.2 Une demande est déposée sur une salle du campus B dave ne la voit pas ; alice, sans périmètre, la voit
5.3 Comparer le compteur de dave et celui d’alice ils diffèrent — chacun compte ce qui le concerne
5.4 Une ressource dont le campus n’est pas renseigné dave ne la voit pas non plus — c’est le cas fail-closed
5.5 Ajouter à dave un second rôle sans périmètre portant la même permission il voit désormais tout : le cumul est permissif, le périmètre du premier rôle est contourné

Étiquette : manuel. Couvert côté serveur par apps/api/src/rbac/rbac.e2e.spec.ts et apps/api/src/approvals/approvals.e2e.spec.ts. Traçabilité : STORY-004.


Ce cas a son scénario dédié, autonome et présentable devant un client : Isolation multi-établissements.

Il n’est pas recopié ici. Retenez seulement les deux formes de refus qu’il éprouve :

  • la ressource est absente — une recherche ne renvoie rien de l’autre établissement ;
  • la ressource est introuvable — ouvrir l’adresse directe d’une ressource de l’autre établissement donne « Salle introuvable. », et non un refus d’accès. Le produit ne confirme pas l’existence de ce qu’il protège.

Étiquette : manuel. Traçabilité : STORY-002, STORY-013.


Les refus de formulaire, qui bloquent avant tout envoi.

# Ce qu’on tente Le produit refuse et affiche L’état après
7.1 Une heure de fin antérieure à l’heure de début, en réservation « L’heure de fin doit être après l’heure de début. », et aucune recherche n’est lancée rien
7.2 Une année académique dont la fin précède le début « La date de fin doit être postérieure à la date de début. » rien créé
7.3 Une période hors des dates de l’année « La période doit être comprise dans les dates de l’année académique. » rien créé
7.4 Une ouverture postérieure à la fermeture « L’heure d’ouverture doit précéder l’heure de fermeture. » rien enregistré
7.5 Une durée de séance de 5 minutes « La durée doit être comprise entre 15 et 480 minutes. » rien créé
7.6 Un JSON invalide dans les caractéristiques d’une salle « JSON invalide : corrigez les caractéristiques avant d’enregistrer. » la fiche n’est pas enregistrée
7.7 Un rôle personnalisé sans aucune permission « Sélectionnez au moins une permission. » rien créé
7.8 Deux mots de passe différents « Les deux mots de passe ne correspondent pas. » mot de passe inchangé
7.9 Un seuil de contrainte hors bornes « Valeur hors bornes : corrigez avant d’enregistrer. » profil inchangé
7.10 Une capacité non numérique dans un import « « Capacité » doit être un nombre entier valide. » ligne rejetée, rien d’écrit

Les jetons expirés, qui sont une validation d’un autre ordre :

# Ce qu’on tente Le produit refuse et affiche
7.11 Rouvrir un lien de réinitialisation déjà utilisé « Ce lien de réinitialisation est invalide ou a expiré. »
7.12 Rouvrir une invitation déjà acceptée, annulée, ou vieille de plus de sept jours « Cette invitation est invalide, expirée ou a déjà été utilisée. »le même message dans les trois cas, et c’est délibéré

Étiquette : automatisable. Traçabilité : STORY-008, STORY-011, STORY-021.


Deux navigateurs. Le comportement de chaque cas a été vérifié, il n’y a rien d’indéterminé ici.

# Ce qu’on tente Ce qui se passe L’état après
8.1 alice et bob réservent la même salle sur le même créneau, presque en même temps un seul gagne ; le perdant lit « Cette salle vient d’être prise. Alternatives sur le même créneau : » avec des salles cliquables une seule réservation existe
8.2 Le perdant clique une alternative le formulaire se re-pré-remplit sur cette salle
8.3 Un administrateur ouvre le récapitulatif de publication, un planificateur pose une séance de plus sur le même périmètre, l’administrateur confirme « Le planning a changé depuis l’aperçu — récapitulatif actualisé, merci de revalider. » rien n’a été publié
8.4 Un calcul d’optimisation tourne ; quelqu’un déplace une séance sur les mêmes salles ; on tente d’appliquer la suggestion « Le planning a bougé pendant le calcul : la suggestion est périmée. Relancez le calcul. » aucune séance créée
8.5 Deux personnes lancent un calcul sur le même établissement la seconde lit « Un calcul est déjà en cours pour l’établissement. » un seul calcul tourne
8.6 Deux approbateurs traitent la même demande la seconde action donne « L’action a échoué. Réessayez. » ; la demande a déjà quitté la file une seule décision enregistrée

Le refus différé, qui n’a pas d’acteur humain :

# Ce qu’on tente Ce qui se passe Le délai
8.7 Laisser une demande de réservation non traitée jusqu’au début de son créneau elle passe à « Expirée », sans motif, et la salle est libérée le contrôle passe toutes les 60 secondes, valeur écrite en dur : comptez jusqu’à une minute après l’heure du créneau

Étiquette : manuel. Traçabilité : STORY-014, STORY-016, STORY-017, STORY-030, STORY-032.


  • Quota dépassé → « Quota atteint : 2 h réservées cette semaine sur 2 h autorisées. », et la réservation initiale est intacte
  • Horizon dépassé → « Réservation trop lointaine : J+30 demandé, maximum J+7. »
  • Durée dépassée → « Durée trop longue : 120 min demandées, maximum 60 min. »
  • Salle déjà occupée → « Salle occupée : B-201 · 09:45 – 11:15 » et la grille n’a pas bougé
  • Enseignant occupé → « Enseignant déjà en cours : … » ; groupe occupé → « Groupe déjà en cours : … »
  • Salle indisponible → absente des résultats, ou grisée avec « Réserver » inactif
  • Période gelée → « Période gelée “…” : aucune réservation possible sur cette période. »
  • Contrainte dure → cellule hachurée, sans chiffre, raison nommée au survol
  • Salle sans équipement requis → non sélectionnable, avec son motif
  • Fenêtre hors ouverture → « Dépasse les heures d’ouverture (08:00 – 20:00) »
  • Sans permission → l’entrée du menu est absente, pas grisée
  • Adresse directe → redirection vers l’accueil (sauf les trois exceptions notées)
  • Sans planning.publish → le bandeau de changements est là, sans bouton
  • Rôle limité au campus A → ne voit pas le campus B, ni une ressource sans campus
  • Second rôle sans périmètre → il voit tout : le cumul est permissif
  • Adresse d’une ressource d’un autre établissement → « Salle introuvable. »
  • Dates incohérentes, durée hors bornes, JSON invalide, rôle sans permission → chaque message cité s’affiche, rien n’est enregistré
  • Lien de réinitialisation réutilisé → « Ce lien de réinitialisation est invalide ou a expiré. »
  • Invitation morte → « Cette invitation est invalide, expirée ou a déjà été utilisée. »
  • Course à deux sur une salle → « Cette salle vient d’être prise. … », une seule réservation
  • Publication concurrente → « Le planning a changé depuis l’aperçu … », rien n’est publié
  • Suggestion périmée → « Le planning a bougé pendant le calcul : la suggestion est périmée. … »
  • Second calcul → « Un calcul est déjà en cours pour l’établissement. »
  • Demande non traitée → « Expirée » dans la minute qui suit l’heure du créneau, salle libérée
  • Ce n’est pas la liste des messages d’erreur du produit. Les états particuliers de chaque écran sont décrits dans la référence, écran par écran ; ici on les vérifie.
  • Ce n’est pas la page des pièges. Un refus attendu est une garantie ; un piège est une perte de temps. Les seconds sont sur Les pièges connus.
Documentation technique source (4)