From 8904e6127a116bc09a7689944cbe8495e04ab66a Mon Sep 17 00:00:00 2001 From: Azmog <80716388+AzmogEx@users.noreply.github.com> Date: Thu, 18 Jun 2026 22:09:48 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20ajoute=20la=20colonne=20Responsable=20e?= =?UTF-8?q?t=20un=20mod=C3=A8le=20de=20PV=20de=20recette=20au=20cahier=20d?= =?UTF-8?q?e=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - colonne « Responsable » sur les 6 tables de tests (fonctionnels + non-fonctionnels) - nouvelle section 6.4 : modèle de procès-verbal de recette (identification, synthèse, réserves, décision, blocs de signature MOA/MOE) Répond aux exigences de testing du sujet (responsable par test, PV de recette). Co-Authored-By: Claude Opus 4.8 (1M context) --- CESIZen_Cahier_Tests_Etude_Comparative.pdf | Bin 37599 -> 40115 bytes generate_pdf.py | 202 ++++++++++++++------- 2 files changed, 136 insertions(+), 66 deletions(-) diff --git a/CESIZen_Cahier_Tests_Etude_Comparative.pdf b/CESIZen_Cahier_Tests_Etude_Comparative.pdf index 1213905624d1389bd7d84d69738238d898f10c55..08d1e89abc95ffbd52742f8bc0c85b84626aac39 100644 GIT binary patch delta 25323 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 QFbo8MP+qyBtgS-%U)IdN;Q#;t diff --git a/generate_pdf.py b/generate_pdf.py index 00ee761..752f182 100644 --- a/generate_pdf.py +++ b/generate_pdf.py @@ -250,109 +250,109 @@ def generate_pdf(): pdf.ln(5) pdf.section_title('1.2 Tests fonctionnels - Authentification') pdf.colored_table( - ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut'], + ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut', 'Responsable'], [ - ['T-AUTH-01', 'Inscription valide', 'Nom, prénom, email, mdp', 'Compte créé, token JWT', 'OK'], - ['T-AUTH-02', 'Inscription email existant', 'Email déjà utilisé', 'Erreur 422, msg clair', 'OK'], - ['T-AUTH-03', 'Connexion valide', 'Email + mdp corrects', 'Token JWT retourné', 'OK'], - ['T-AUTH-04', 'Connexion mdp erroné', 'Email + mdp incorrect', 'Erreur 401', 'OK'], - ['T-AUTH-05', 'Connexion compte inactif', 'Compte désactivé', 'Erreur 403', 'OK'], - ['T-AUTH-06', 'Rate limiting login', '6 tentatives en 1 min', 'Erreur 429 throttle', 'OK'], - ['T-AUTH-07', 'Déconnexion', 'Token valide', 'Token invalidé', 'OK'], - ['T-AUTH-08', 'Refresh token', 'Token expirant', 'Nouveau token retourné', 'OK'], - ['T-AUTH-09', 'Accès route protégée sans token', 'Aucun token', 'Erreur 401', 'OK'], - ['T-AUTH-10', 'Accès route admin sans rôle', 'Token membre', 'Erreur 403', 'OK'], + ['T-AUTH-01', 'Inscription valide', 'Nom, prénom, email, mdp', 'Compte créé, token JWT', 'OK', 'Dév. backend'], + ['T-AUTH-02', 'Inscription email existant', 'Email déjà utilisé', 'Erreur 422, msg clair', 'OK', 'Dév. backend'], + ['T-AUTH-03', 'Connexion valide', 'Email + mdp corrects', 'Token JWT retourné', 'OK', 'Dév. backend'], + ['T-AUTH-04', 'Connexion mdp erroné', 'Email + mdp incorrect', 'Erreur 401', 'OK', 'Dév. backend'], + ['T-AUTH-05', 'Connexion compte inactif', 'Compte désactivé', 'Erreur 403', 'OK', 'Dév. backend'], + ['T-AUTH-06', 'Rate limiting login', '6 tentatives en 1 min', 'Erreur 429 throttle', 'OK', 'Dév. backend'], + ['T-AUTH-07', 'Déconnexion', 'Token valide', 'Token invalidé', 'OK', 'Dév. backend'], + ['T-AUTH-08', 'Refresh token', 'Token expirant', 'Nouveau token retourné', 'OK', 'Dév. backend'], + ['T-AUTH-09', 'Accès route protégée sans token', 'Aucun token', 'Erreur 401', 'OK', 'Dév. backend'], + ['T-AUTH-10', 'Accès route admin sans rôle', 'Token membre', 'Erreur 403', 'OK', 'Dév. backend'], ], - [20, 42, 40, 55, 33] + [20, 42, 40, 49, 15, 24] ) pdf.ln(3) pdf.section_title('1.3 Tests fonctionnels - Tracker émotionnel') pdf.colored_table( - ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut'], + ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut', 'Responsable'], [ - ['T-TRK-01', 'Créer saisie', 'Émotion, intensité 1-10, note', 'Saisie enregistrée', 'OK'], - ['T-TRK-02', 'Créer saisie sans tracker', 'Premier usage', 'Tracker auto-créé', 'OK'], - ['T-TRK-03', 'Lister saisies', 'Token valide', 'Liste paginée (20/page)', 'OK'], - ['T-TRK-04', 'Filtrer par date', 'date_debut, date_fin', 'Saisies dans la période', 'OK'], - ['T-TRK-05', 'Filtrer par émotion', 'emotion_id', 'Saisies correspondantes', 'OK'], - ['T-TRK-06', 'Modifier saisie', 'Nouvelle intensité/note', 'Saisie mise à jour', 'OK'], - ['T-TRK-07', 'Supprimer saisie', 'ID saisie existante', 'Saisie supprimée', 'OK'], - ['T-TRK-08', 'Rapport semaine', 'period=week', 'Stats hebdomadaires', 'OK'], - ['T-TRK-09', 'Rapport mois', 'period=month', 'Stats mensuelles', 'OK'], - ['T-TRK-10', 'Rapport trimestre', 'period=quarter', 'Stats trimestrielles', 'OK'], - ['T-TRK-11', 'Rapport année', 'period=year', 'Stats annuelles', 'OK'], - ['T-TRK-12', 'Saisie intensité hors bornes', 'intensité = 15', 'Erreur validation 422', 'OK'], + ['T-TRK-01', 'Créer saisie', 'Émotion, intensité 1-10, note', 'Saisie enregistrée', 'OK', 'Dév. backend'], + ['T-TRK-02', 'Créer saisie sans tracker', 'Premier usage', 'Tracker auto-créé', 'OK', 'Dév. backend'], + ['T-TRK-03', 'Lister saisies', 'Token valide', 'Liste paginée (20/page)', 'OK', 'Dév. backend'], + ['T-TRK-04', 'Filtrer par date', 'date_debut, date_fin', 'Saisies dans la période', 'OK', 'Dév. backend'], + ['T-TRK-05', 'Filtrer par émotion', 'emotion_id', 'Saisies correspondantes', 'OK', 'Dév. backend'], + ['T-TRK-06', 'Modifier saisie', 'Nouvelle intensité/note', 'Saisie mise à jour', 'OK', 'Dév. backend'], + ['T-TRK-07', 'Supprimer saisie', 'ID saisie existante', 'Saisie supprimée', 'OK', 'Dév. backend'], + ['T-TRK-08', 'Rapport semaine', 'period=week', 'Stats hebdomadaires', 'OK', 'Dév. backend'], + ['T-TRK-09', 'Rapport mois', 'period=month', 'Stats mensuelles', 'OK', 'Dév. backend'], + ['T-TRK-10', 'Rapport trimestre', 'period=quarter', 'Stats trimestrielles', 'OK', 'Dév. backend'], + ['T-TRK-11', 'Rapport année', 'period=year', 'Stats annuelles', 'OK', 'Dév. backend'], + ['T-TRK-12', 'Saisie intensité hors bornes', 'intensité = 15', 'Erreur validation 422', 'OK', 'Dév. backend'], ], - [20, 42, 42, 53, 33] + [20, 42, 42, 47, 15, 24] ) pdf.add_page() pdf.section_title('1.4 Tests fonctionnels - Feeds / Ressources') pdf.colored_table( - ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut'], + ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut', 'Responsable'], [ - ['T-FEED-01', 'Lister feeds publiés', 'Aucun (public)', 'Liste ordonnée des articles', 'OK'], - ['T-FEED-02', 'Détail feed par slug', 'Slug existant', 'Contenu complet + auteur', 'OK'], - ['T-FEED-03', 'Feed slug inexistant', 'Slug invalide', 'Erreur 404', 'OK'], - ['T-FEED-04', 'Feed non publié inaccessible', 'Feed draft', 'Non listé / 404', 'OK'], + ['T-FEED-01', 'Lister feeds publiés', 'Aucun (public)', 'Liste ordonnée des articles', 'OK', 'Dév. backend'], + ['T-FEED-02', 'Détail feed par slug', 'Slug existant', 'Contenu complet + auteur', 'OK', 'Dév. backend'], + ['T-FEED-03', 'Feed slug inexistant', 'Slug invalide', 'Erreur 404', 'OK', 'Dév. backend'], + ['T-FEED-04', 'Feed non publié inaccessible', 'Feed draft', 'Non listé / 404', 'OK', 'Dév. backend'], ], - [22, 45, 38, 52, 33] + [22, 45, 38, 46, 15, 24] ) pdf.ln(5) pdf.section_title('1.5 Tests fonctionnels - Administration') pdf.colored_table( - ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut'], + ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut', 'Responsable'], [ - ['T-ADM-01', 'Lister utilisateurs', 'Token admin', 'Liste paginée (15/p)', 'OK'], - ['T-ADM-02', 'Rechercher utilisateur', 'search=nom', 'Résultats ilike', 'OK'], - ['T-ADM-03', 'Créer utilisateur', 'Données complètes', 'Utilisateur + Tracker', 'OK'], - ['T-ADM-04', 'Modifier utilisateur', 'Nouvelles données', 'Utilisateur mis à jour', 'OK'], - ['T-ADM-05', 'Activer/Désactiver', 'ID user', 'Toggle est_actif', 'OK'], - ['T-ADM-06', 'Supprimer utilisateur', 'ID user', 'Soft delete', 'OK'], - ['T-ADM-07', 'CRUD feeds (admin)', 'Données feed', 'Création/modif/suppr.', 'OK'], - ['T-ADM-08', 'CRUD émotions', 'Données émotion', 'Création/modif/désact.', 'OK'], - ['T-ADM-09', 'Désactiver émotion parent', 'ID parent', 'Parent + enfants désact.', 'OK'], - ['T-ADM-10', 'Audit trail', 'Actions admin', 'Logs en base (audits)', 'OK'], + ['T-ADM-01', 'Lister utilisateurs', 'Token admin', 'Liste paginée (15/p)', 'OK', 'Dév. backend'], + ['T-ADM-02', 'Rechercher utilisateur', 'search=nom', 'Résultats ilike', 'OK', 'Dév. backend'], + ['T-ADM-03', 'Créer utilisateur', 'Données complètes', 'Utilisateur + Tracker', 'OK', 'Dév. backend'], + ['T-ADM-04', 'Modifier utilisateur', 'Nouvelles données', 'Utilisateur mis à jour', 'OK', 'Dév. backend'], + ['T-ADM-05', 'Activer/Désactiver', 'ID user', 'Toggle est_actif', 'OK', 'Dév. backend'], + ['T-ADM-06', 'Supprimer utilisateur', 'ID user', 'Soft delete', 'OK', 'Dév. backend'], + ['T-ADM-07', 'CRUD feeds (admin)', 'Données feed', 'Création/modif/suppr.', 'OK', 'Dév. backend'], + ['T-ADM-08', 'CRUD émotions', 'Données émotion', 'Création/modif/désact.', 'OK', 'Dév. backend'], + ['T-ADM-09', 'Désactiver émotion parent', 'ID parent', 'Parent + enfants désact.', 'OK', 'Dév. backend'], + ['T-ADM-10', 'Audit trail', 'Actions admin', 'Logs en base (audits)', 'OK', 'Dév. backend'], ], - [20, 42, 38, 52, 38] + [20, 42, 38, 46, 15, 29] ) pdf.ln(5) pdf.section_title('1.6 Tests fonctionnels - Profil utilisateur') pdf.colored_table( - ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut'], + ['ID', 'Cas de test', 'Entrée', 'Résultat attendu', 'Statut', 'Responsable'], [ - ['T-PRO-01', 'Voir profil', 'Token valide', 'Infos profil complètes', 'OK'], - ['T-PRO-02', 'Modifier nom/prénom', 'Nouvelles valeurs', 'Profil mis à jour', 'OK'], - ['T-PRO-03', 'Changer mot de passe', 'Ancien + nouveau mdp', 'Mot de passe changé', 'OK'], - ['T-PRO-04', 'Ancien mdp incorrect', 'Mauvais ancien mdp', 'Erreur 422', 'OK'], - ['T-PRO-05', 'Supprimer compte (RGPD)', 'Confirmation', 'Soft delete du compte', 'OK'], + ['T-PRO-01', 'Voir profil', 'Token valide', 'Infos profil complètes', 'OK', 'Dév. backend'], + ['T-PRO-02', 'Modifier nom/prénom', 'Nouvelles valeurs', 'Profil mis à jour', 'OK', 'Dév. backend'], + ['T-PRO-03', 'Changer mot de passe', 'Ancien + nouveau mdp', 'Mot de passe changé', 'OK', 'Dév. backend'], + ['T-PRO-04', 'Ancien mdp incorrect', 'Mauvais ancien mdp', 'Erreur 422', 'OK', 'Dév. backend'], + ['T-PRO-05', 'Supprimer compte (RGPD)', 'Confirmation', 'Soft delete du compte', 'OK', 'DPO / Dév.'], ], - [20, 42, 42, 53, 33] + [20, 42, 42, 47, 15, 24] ) pdf.ln(5) pdf.section_title('1.7 Tests non fonctionnels') pdf.colored_table( - ['ID', 'Catégorie', 'Test', 'Critère', 'Statut'], + ['ID', 'Catégorie', 'Test', 'Critère', 'Statut', 'Responsable'], [ - ['T-NF-01', 'Performance', 'Temps de réponse API', '< 200ms (95e centile)', 'OK'], - ['T-NF-02', 'Performance', 'Chargement page front', '< 2s (First Contentful Paint)', 'OK'], - ['T-NF-03', 'Sécurité', 'Injection SQL', 'Eloquent ORM paramétré', 'OK'], - ['T-NF-04', 'Sécurité', 'XSS', 'Échappement React natif', 'OK'], - ['T-NF-05', 'Sécurité', 'CORS', 'Origines restreintes', 'OK'], - ['T-NF-06', 'Sécurité', 'JWT expiration', 'Tokens à durée limitée', 'OK'], - ['T-NF-07', 'Sécurité', 'Rate limiting', 'Throttle 5 tentatives/min', 'OK'], - ['T-NF-08', 'Accessibilité', 'Responsive design', 'Mobile, tablette, desktop', 'OK'], - ['T-NF-09', 'Accessibilité', 'Mode sombre', 'Toggle dark/light mode', 'OK'], - ['T-NF-10', 'RGPD', 'Droit à l\'effacement', 'Soft delete + anonymisation', 'OK'], - ['T-NF-11', 'RGPD', 'Consentement', 'Champ consentement_rgpd', 'OK'], - ['T-NF-12', 'Fiabilité', 'Gestion erreurs API', 'Réponses JSON structurées', 'OK'], - ['T-NF-13', 'Fiabilité', 'Audit trail admin', 'Table audits traçabilité', 'OK'], + ['T-NF-01', 'Performance', 'Temps de réponse API', '< 200ms (95e centile)', 'OK', 'DevOps'], + ['T-NF-02', 'Performance', 'Chargement page front', '< 2s (First Contentful Paint)', 'OK', 'DevOps'], + ['T-NF-03', 'Sécurité', 'Injection SQL', 'Eloquent ORM paramétré', 'OK', 'Dév. backend'], + ['T-NF-04', 'Sécurité', 'XSS', 'Échappement React natif', 'OK', 'Dév. front'], + ['T-NF-05', 'Sécurité', 'CORS', 'Origines restreintes', 'OK', 'Dév. backend'], + ['T-NF-06', 'Sécurité', 'JWT expiration', 'Tokens à durée limitée', 'OK', 'Dév. backend'], + ['T-NF-07', 'Sécurité', 'Rate limiting', 'Throttle 5 tentatives/min', 'OK', 'Dév. backend'], + ['T-NF-08', 'Accessibilité', 'Responsive design', 'Mobile, tablette, desktop', 'OK', 'Dév. front'], + ['T-NF-09', 'Accessibilité', 'Mode sombre', 'Toggle dark/light mode', 'OK', 'Dév. front'], + ['T-NF-10', 'RGPD', 'Droit à l\'effacement', 'Soft delete + anonymisation', 'OK', 'DPO'], + ['T-NF-11', 'RGPD', 'Consentement', 'Champ consentement_rgpd', 'OK', 'DPO'], + ['T-NF-12', 'Fiabilité', 'Gestion erreurs API', 'Réponses JSON structurées', 'OK', 'DevOps'], + ['T-NF-13', 'Fiabilité', 'Audit trail admin', 'Table audits traçabilité', 'OK', 'DevOps'], ], - [18, 28, 42, 60, 42] + [16, 26, 38, 52, 14, 34] ) # ============================ @@ -875,6 +875,76 @@ def generate_pdf(): pdf.set_fill_color(230, 250, 230) pdf.cell(0, 10, ' RÉSULTAT : 14/14 critères d\'acceptation validés - Projet conforme aux exigences.', fill=True, new_x="LMARGIN", new_y="NEXT") + # ============================ + # 6.4 Modèle de PV de recette + # ============================ + pdf.add_page() + pdf.section_title('6.4 Modèle de procès-verbal de recette') + pdf.body_text( + 'Le procès-verbal (PV) de recette formalise l\'acceptation de la solution par le commanditaire ' + 'à l\'issue de la recette utilisateur (UAT). Modèle type à compléter et signer par les deux parties :' + ) + + pdf.subsection_title('Identification') + pdf.colored_table( + ['Rubrique', 'Renseignement'], + [ + ['Projet', 'CESIZen - Plateforme de santé mentale'], + ['Version recettée', 'v1.0'], + ['Date de recette', '...... / ...... / ............'], + ['Maîtrise d\'ouvrage (commanditaire)', 'Ministère des Solidarités et de la Santé'], + ['Maîtrise d\'oeuvre (réalisation)', 'Adam Marzuk - CESI École d\'Ingénieurs'], + ['Périmètre recetté', 'Comptes utilisateurs, Informations, Tracker d\'émotions'], + ], + [70, 120] + ) + + pdf.ln(2) + pdf.subsection_title('Synthèse des résultats') + pdf.colored_table( + ['Indicateur', 'Valeur'], + [ + ['Cas de test exécutés', '............ / 54'], + ['Cas conformes (OK)', '............'], + ['Anomalies bloquantes', '............'], + ['Anomalies mineures', '............'], + ['Taux de conformité', '............ %'], + ], + [95, 95] + ) + + pdf.ln(2) + pdf.subsection_title('Réserves éventuelles') + pdf.body_text('1. ................................................................................................................') + pdf.body_text('2. ................................................................................................................') + + pdf.ln(1) + pdf.subsection_title('Décision de recette') + pdf.body_text( + '[ ] Recette prononcée sans réserve ' + '[ ] Recette prononcée avec réserves ' + '[ ] Recette refusée' + ) + + pdf.ln(4) + pdf.set_font('Helvetica', 'B', 9) + pdf.set_text_color(30, 30, 30) + pdf.cell(95, 7, 'Pour la maîtrise d\'ouvrage', border=0) + pdf.cell(95, 7, 'Pour la maîtrise d\'oeuvre', border=0, new_x="LMARGIN", new_y="NEXT") + pdf.set_font('Helvetica', '', 8) + pdf.cell(95, 6, 'Nom : .....................................', border=0) + pdf.cell(95, 6, 'Nom : .....................................', border=0, new_x="LMARGIN", new_y="NEXT") + pdf.cell(95, 6, 'Fonction : Product Owner', border=0) + pdf.cell(95, 6, 'Fonction : Développeur / Apprenant', border=0, new_x="LMARGIN", new_y="NEXT") + pdf.cell(95, 6, 'Date et signature :', border=0) + pdf.cell(95, 6, 'Date et signature :', border=0, new_x="LMARGIN", new_y="NEXT") + pdf.ln(2) + sig_y = pdf.get_y() + pdf.set_draw_color(120, 120, 120) + pdf.rect(pdf.l_margin, sig_y, 90, 26) + pdf.rect(pdf.l_margin + 100, sig_y, 90, 26) + pdf.ln(30) + # Sauvegarde output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'CESIZen_Cahier_Tests_Etude_Comparative.pdf') pdf.output(output_path)