Exécution — Tests E2E profil PSYCHOLOGUE (puis ASSISTANT_SOCIAL)¶
Note mise à jour en temps réel au fur et à mesure de la progression. Compte utilisé :
test.agent@projetesm.test/ commune KANDI, profil PSYCHOLOGUE.
Origine¶
Demande de Bernard : simuler une vraie session utilisateur (clics dans l'UI, pas de raccourcis
API) pour couvrir toutes les sous-activités/actions du profil PSYCHOLOGUE hors indicateurs,
avant de refaire le même exercice pour ASSISTANT_SOCIAL avec les tâches propres à son cahier des
charges (référencement vers le psychologue notamment). Scripts conservés (pas de nettoyage) dans
Test_E2E/.
Écart RBAC détecté et corrigé avant de commencer¶
Le profil PSYCHOLOGUE (et ASSISTANT_SOCIAL, permissions identiques) a en base les permissions
SUPPORT_GERER, COMITE_GERER, CONCERTATION_GERER — mais les routes frontend
/sensibilisation/* et /mobilisation/* étaient réservées à ROLES_COORD_ET_ADMIN, both dans
Router.tsx (garde de route) et Sidebar.tsx (menu invisible). Un vrai agent de terrain ne
pouvait donc ni voir ni atteindre ces écrans, alors que son cahier des charges les couvre
explicitement (groupes de parole, cadres de concertation, plaidoyer local — cf.
Descriptif_poste_de_psychologue.md / Descriptif_poste_Assistante_Sociale.md).
Corrigé : nouvelle constante ROLES_TERRAIN_ET_COORD (PSYCHOLOGUE, ASSISTANT_SOCIAL +
coordination/admin) appliquée aux groupes de routes concernés dans Router.tsx et aux sections
sensibilisation/mobilisation/referencements-recus de Sidebar.tsx. /pilotage/* reste
réservé coordination/admin (indicateurs, hors périmètre). Les boutons de validation MEAL
(ValidationActions, déjà présents dans ces pages) restent inchangés — seuls
création/lecture/modification s'ouvrent, conformément aux permissions backend déjà détenues.
Checklist — PSYCHOLOGUE (test.agent@projetesm.test)¶
Tâches génériques CRUD + tâches spécifiques au cahier des charges (identifiées entre parenthèses).
- [x] Enfants : identification/création (tâche métier centrale), modification de dossier, changement de statut, liste/filtre par statut — ✅ testé via UI réelle, tout fonctionne
- [x] Suivi générique (
SUIVI_CREER) : renseigner la base de données de suivi d'un enfant — ✅ testé via UI, bug trouvé et corrigé (voir plus bas) - [x] Volet Soutien psychologique (
SOUTIEN_PSYCHOLOGIQUE_GERER) — ✅ testé via UI, bug trouvé et corrigé (voir plus bas) - [x] Volet Séance alphabétisation (
SEANCE_ALPHABETISATION_GERER) — ✅ testé via UI après fix - [x] Volet Appui alimentaire (
APPUI_ALIMENTAIRE_GERER) — ✅ testé via UI après fix - [x] Volet Prise en charge sanitaire (
PRISE_EN_CHARGE_SANITAIRE_GERER) — ✅ testé via UI après fix - [x] Cas orienté / Référencements reçus (
CAS_ORIENTE_CREER) — ✅ création testée via UI (référencements reçus, avec l'assistant social comme émetteur, à re-tester à la session ASSISTANT_SOCIAL) - [x] Formations (
/formations,FORMATION_CREER) — ✅ testé via UI, 2 bugs trouvés et corrigés (voir plus bas) - [x] Sensibilisation (
SUPPORT_GERER, fraîchement ouvert) : ✅ 6 pages testées + création validée (Supports) — 5 bugs de scope idUtilisateur/commune corrigés (voir plus bas) - [x] Mobilisation (
COMITE_GERER/CONCERTATION_GERER, fraîchement ouvert) : ✅ 5 pages testées + création validée (Comité villageois, Parole des jeunes) — 1 bug de scope (SessionConcertation) + 1 bug de formulaire (Type en texte libre au lieu d'enum) corrigés - [x] Insertion socio-économique (
INSERTION_CREER/MODIFIER) : ✅ 4 pages testées + parcours créé (cross-agent, enfant "A A") + micro-subvention ajoutée — même bug de scope corrigé (ParcoursInsertion.lister+verifierParcoursAppartientAUtilisateur, bloquait les 4 types d'événements : inscriptions formation, accompagnement plan d'affaires, micro-subventions, installations CQM) - [x] Acteurs de terrain (
ACTEUR_CREER/MODIFIER) : ✅ 5 pages testées + création tuteur validée — 2 bugs trouvés et corrigés (voir plus bas), aucun bug de scope idUtilisateur dans ce module (déjà vérifié correct) - [x] Mon profil : ✅ 3 onglets testés, modification profil validée — 4 bugs trouvés et corrigés (voir plus bas), dont un bug racine touchant potentiellement toute donnée utilisateur affichée dans l'app
Explicitement exclu (indicateurs) : /rapports/*, /pilotage/*, export de rapports.
Bug majeur trouvé et corrigé — module Accompagnements (Suivi + 4 volets + Cas orienté)¶
En testant la fiche enfant (/enfants/:id) avec test.agent sur l'enfant "A A" (créé par un
autre agent, flow.test.backup), toutes les sections échouaient en 404 "Enfant introuvable" /
"Cible du suivi introuvable" : AppuiAlimentaire, SoutienPsychologique,
PriseEnChargeSanitaire, SeanceAlphabetisation, CasOriente, Suivi.
Cause : les 5 repositories Prisma du module Accompagnements portaient un commentaire
explicite mais erroné — "RÈGLES MULTI-TENANT : TOUJOURS filtrer par idUtilisateur" — copié d'un
pattern SaaS multi-tenant mal appliqué ici. Résultat : verifierEnfant/verifierCible et
lister filtraient par idUtilisateur (l'utilisateur courant) au lieu du périmètre de
commune (communeCode), contrairement à tout le reste du code (Enfant, MaitreCoranique...).
Concrètement : seul l'agent ayant personnellement enregistré l'enfant pouvait consulter ou
ajouter un soutien psychologique/appui alimentaire/etc. pour cet enfant — bloquant tout travail
d'équipe (ex: le psychologue ne peut aider un enfant enregistré par l'assistant social).
Corrigé (6 modules : AppuiAlimentaire, SoutienPsychologique, PriseEnChargeSanitaire,
SeanceAlphabetisation, CasOriente, Suivi) : verifierEnfant/verifierCible et lister scopés
par communeCodeScope (comme EnfantRepositoryPrisma.obtenirParId, null = accès toutes
communes pour coordination/admin), au lieu de idUtilisateur. modifier/supprimer non
touchés — ils passent déjà par ServiceAutorisationDonnees.peutModifier (auteur seul tant que
non validé), un mécanisme distinct et correct. ~48 fichiers modifiés (interface repo + repo Prisma
+ use case Lister/Creer + interface Donnees + handler, ×6 modules).
Bug additionnel découvert en testant la création via l'UI réelle : CreerSuiviUseCase
appelait executerLogique(validation.donnees!) — l'objet validé par Zod, qui ne contient pas
idUtilisateur (absent du schéma) → Prisma.suivi.create() échouait avec Argument idUtilisateur
is missing (500). Corrigé en passant donnees.idUtilisateur/donnees.communeCodeScope
séparément à executerLogique, comme le font déjà les 5 autres use cases Creer du module.
Validé : redéploiement backend (deploy.sh --target backend) + tests croisés (test.agent,
PSYCHOLOGUE, sur un enfant créé par flow.test.backup) via curl ET via l'UI réelle (Playwright) :
création + lecture réussies pour les 5 volets + Suivi + Cas orienté, confirmé en base de données.
⚠️ Changements non commités — modifications présentes sur le disque et déployées (le conteneur
Docker backend tourne dessus), mais pas encore git commit dans ProjetESM (règle : jamais de
commit sans demande explicite). À faire à la demande de Bernard.
Bugs trouvés et corrigés — module Formations¶
En testant la création d'une formation via l'UI réelle (/formations), 2 bugs distincts trouvés :
- Recherche cassée (500) :
FormationRepositoryPrisma.lister()filtrait le champanimateuravec{ contains: ... }comme si c'était unString, alors que le schéma a été refactoré (commentaireFIX: animateurId avec @relation vers PersonneRessource) —animateurest désormais une relation versPersonneRessource, pas un scalaire. Toute recherche texte plantait avecUnknown argument 'contains'. Corrigé : recherche suranimateur.nom/animateur.prenom(relation). - Même bug systémique que le module Accompagnements :
lister()scopait paridUtilisateur(créateur) au lieu de la commune — un agent ne voyait que ses propres formations, pas celles de ses collègues de la même commune. Corrigé selon le même principe (communeCodeScope, priorité sur le filtre commune libre optionnel).modifier/obtenirParIdnon touchés (author-only intentionnel, cohérent avec le workflow de validation MEAL déjà en place surFormationSensibilisation).
Vérifié via UI réelle (création réussie, commune KANDI) + curl (recherche fonctionnelle, 200 au lieu de 500).
Périmètre plus large possible : le commentaire erroné "TOUJOURS filtrer par idUtilisateur"
apparaît aussi dans EnfantRepositoryPrisma et MaitreCoraniqueRepositoryPrisma, mais vérification
faite — ces deux-là ont déjà le bon comportement (scope commune) malgré le commentaire resté stale.
Les modules Mobilisation/Sensibilisation n'ont pas encore été audités pour ce pattern — à surveiller
lors de leurs tests respectifs (tâches #5/#6).
Bugs trouvés et corrigés — Sensibilisation + Mobilisation¶
Le même pattern (commentaire erroné "TOUJOURS filtrer par idUtilisateur") s'est révélé présent dans 6 repositories supplémentaires, tous corrigés :
- SupportCommunication / TraductionSupport (Formations) — pas de champ
communeCodeen base (production organisation-wide) : le filtreidUtilisateurétait simplement retiré (pas de remplacement par une commune, cohérent avec le commentaire déjà présent surobtenirPourValidation: "pas de scope commune, communeCode toujours null"). - PoseAffiche / EmissionRadio (Formations) — ont un
communeCode: scope corrigé comme les modules précédents (communeCodeScopeprioritaire sur le filtre libre optionnel). - SessionConcertation (Mobilisation) — scope simplement retiré (comme ses voisins
ReunionComite/MembreComite/MembreCadreConcertation, déjà corrects sans idUtilisateur) : le périmètre pertinent est déjà lecadreConcertationIdparent, pas besoin d'un second filtre.
Vérifié sans bug (mêmes fichiers, même commentaire stale mais code déjà correct) :
EnfantRepositoryPrisma, MaitreCoraniqueRepositoryPrisma, ComiteVillageoisRepositoryPrisma,
CadreConcertationRepositoryPrisma, ActionPlaidoyerRepositoryPrisma,
ActiviteCohesionSocialeRepositoryPrisma — tous corrects malgré le commentaire trompeur.
Bug de formulaire trouvé en testant "Parole des jeunes" via l'UI réelle : le champ "Type *"
du formulaire de création était un champ texte libre (z.string().min(1)) alors que le backend
exige un enum strict (GROUPE_PAROLE | PARLEMENT_JEUNES) — tout utilisateur réel tapant une
description au lieu de la valeur technique exacte recevait une 400 systématique. Corrigé :
transformé en <select> avec les 2 options, TypeSessionParole ajouté au DTO frontend (même
pattern que AnimateurParole déjà en place pour le champ "Animé par" voisin).
Validé : redémarrage (rate limit atteint après ~40 connexions de test dans la même session, reset immédiat car stockage en mémoire) + redéploiement backend + tests UI réels : création Comité villageois (KANDI) et session Parole des jeunes (GROUPE_PAROLE, KANDI) réussies, confirmées en base.
Bugs trouvés et corrigés — Acteurs de terrain¶
?limit=500rejeté (400) : la page "Maîtres coraniques" (section Actions communautaires) etFormationDetailPagedemandaientlimit=500pour charger tous les maîtres dans un select, mais le schéma Zod backend plafonnelimità 100 (schemaFiltresMaitreCoranique) — 400 systématique, page cassée. Corrigé :limit: 100(le plafond réel) dans les deux fichiers.- Enums de relation confondus (Tuteurs) : le formulaire "Ajouter un tuteur" proposait 7
valeurs pour "Type de lien" (PERE, MERE, GRAND_PARENT, ONCLE_TANTE, TUTEUR_LEGAL,
TUTEUR_COMMUNAUTAIRE, AUTRE), mais l'enum Prisma
TypeLienTuteurn'en accepte que 3 (TUTEUR_LEGAL/TUTEUR_COMMUNAUTAIRE/AUTRE — catégorie légale/institutionnelle). Les 4 valeurs en trop appartiennent en réalité à un autre enum,TypeLienParent(PERE/MERE/GRAND_PARENT/ ONCLE_TANTE), utilisé par le module Parents — probablement copié-collé par erreur d'un formulaire à l'autre. Résultat : sélectionner "Père", "Mère", "Grand-parent" ou "Oncle/Tante" (les cas les plus courants en pratique) provoquait un 400 systématique, alors que "Lien avec l'enfant" (champ libre déjà présent dans le formulaire) est l'endroit prévu pour cette précision. Corrigé :TypeLienTuteur/TYPES_LIEN_OPTIONSrestreints aux 3 valeurs réelles. Vérifié : le module Parents utilise déjà correctement son propre enum à 4 valeurs, aucune correction nécessaire là.
Validé via UI réelle : création tuteur (KANDI, TUTEUR_LEGAL) réussie, confirmée en base.
Bugs trouvés et corrigés — Mon profil (le plus significatif de la session)¶
En testant la modification du profil (nom/prénom/téléphone), 4 bugs liés découverts en cascade :
- Colonne
telephoneinexistante en base :Utilisateurn'avait jamais eu de colonnetelephonedansschema.prisma, alors que use case, repository, DTOs et formulaire frontend référençaient tous ce champ (fonctionnalité codée mais jamais branchée en base — 500 à chaque tentative de modification). Corrigé : colonnetelephone String?ajoutée (prisma db push, additif, aucune perte de donnée). - Bug racine —
UtilisateurRepositoryPrisma.convertirVersEntite(): cette méthode construit l'entité domaineUtilisateuravec l'appel positionnelnew Utilisateur(...), et les 3 paramètres correspondant àtelephone/organisation/posteétaient codés en dur ànullau lieu de lireutilisateurPrisma.telephoneetc. Résultat : même une fois la colonne ajoutée en base, aucune route de l'application (login, obtenir profil, modifier profil...) ne pouvait jamais renvoyer le téléphone réel d'un utilisateur — silencieusement remplacé parnullà chaque construction de l'entité. Corrigé :utilisateurPrisma.telephone ?? nullà la place du premiernull(organisation/poste restentnull, colonnes absentes de la base et non utilisées par le frontend — code mort, hors périmètre). ProfilModifie(réponse de modification) sanscommuneCode: contrairement àObtenirProfilUseCasequi l'inclut,ModifierProfilUseCase.construireResultat()omettaitcommuneCode. Comme le frontend remplace tout l'objet utilisateur en store avec la réponse de modification (storageService.setUser(response.data)), toute modification de profil faisait disparaître la commune de l'utilisateur affichée (repli sur le libellé "Accès national", trompeur pour un agent de terrain qui a bien une commune). Corrigé :communeCodeajouté au DTO et à la construction du résultat.- Champ
phoneNumber(API, package auth partagé) vstelephone(frontendUserProfile, aligné sur la colonne Prisma) : mismatch de nom de champ jamais remarqué faute de données réelles (bug #1). Corrigé par une normalisation à la frontière API (authApi.ts,normalizeUserProfile()) plutôt que de renommer le contrat du package partagé (risque cross-projets) ou le champ frontend (utilisé nulle part ailleurs sous ce nom). - Bonus, même investigation :
Utilisateur.updatedAtn'avait pas la directive@updatedAt(présente sur les autres modèles) — ne se mettait jamais à jour après modification. Corrigé.
Portée du bug racine (#2) : ce bug affectait potentiellement l'affichage du téléphone utilisateur partout dans l'application (pas seulement "Mon profil"), depuis l'introduction du champ dans le code — resté invisible jusqu'ici car la colonne elle-même n'existait pas encore (aucune valeur réelle à perdre). C'est la correction la plus structurellement importante de cette session de tests, avec le bug idUtilisateur/commune du module Accompagnements.
Validé via UI réelle + curl : Commune (KANDI) et Téléphone (22900000099) s'affichent et se sauvegardent correctement après modification, confirmé en base de données.
Journal d'exécution¶
(rempli au fur et à mesure)