Aller au contenu

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 :

  1. Recherche cassée (500) : FormationRepositoryPrisma.lister() filtrait le champ animateur avec { contains: ... } comme si c'était un String, alors que le schéma a été refactoré (commentaire FIX: animateurId avec @relation vers PersonneRessource) — animateur est désormais une relation vers PersonneRessource, pas un scalaire. Toute recherche texte plantait avec Unknown argument 'contains'. Corrigé : recherche sur animateur.nom / animateur.prenom (relation).
  2. Même bug systémique que le module Accompagnements : lister() scopait par idUtilisateur (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/obtenirParId non touchés (author-only intentionnel, cohérent avec le workflow de validation MEAL déjà en place sur FormationSensibilisation).

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 communeCode en base (production organisation-wide) : le filtre idUtilisateur était simplement retiré (pas de remplacement par une commune, cohérent avec le commentaire déjà présent sur obtenirPourValidation : "pas de scope commune, communeCode toujours null").
  • PoseAffiche / EmissionRadio (Formations) — ont un communeCode : scope corrigé comme les modules précédents (communeCodeScope prioritaire 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à le cadreConcertationId parent, 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

  1. ?limit=500 rejeté (400) : la page "Maîtres coraniques" (section Actions communautaires) et FormationDetailPage demandaient limit=500 pour charger tous les maîtres dans un select, mais le schéma Zod backend plafonne limit à 100 (schemaFiltresMaitreCoranique) — 400 systématique, page cassée. Corrigé : limit: 100 (le plafond réel) dans les deux fichiers.
  2. 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 TypeLienTuteur n'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_OPTIONS restreints 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 :

  1. Colonne telephone inexistante en base : Utilisateur n'avait jamais eu de colonne telephone dans schema.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é : colonne telephone String? ajoutée (prisma db push, additif, aucune perte de donnée).
  2. Bug racine — UtilisateurRepositoryPrisma.convertirVersEntite() : cette méthode construit l'entité domaine Utilisateur avec l'appel positionnel new Utilisateur(...), et les 3 paramètres correspondant à telephone/organisation/poste étaient codés en dur à null au lieu de lire utilisateurPrisma.telephone etc. 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é par null à chaque construction de l'entité. Corrigé : utilisateurPrisma.telephone ?? null à la place du premier null (organisation/poste restent null, colonnes absentes de la base et non utilisées par le frontend — code mort, hors périmètre).
  3. ProfilModifie (réponse de modification) sans communeCode : contrairement à ObtenirProfilUseCase qui l'inclut, ModifierProfilUseCase.construireResultat() omettait communeCode. 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é : communeCode ajouté au DTO et à la construction du résultat.
  4. Champ phoneNumber (API, package auth partagé) vs telephone (frontend UserProfile, 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).
  5. Bonus, même investigation : Utilisateur.updatedAt n'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)