diff --git a/.ideai/tickets/84/carnet.md b/.ideai/tickets/84/carnet.md index d204181..79bc812 100644 --- a/.ideai/tickets/84/carnet.md +++ b/.ideai/tickets/84/carnet.md @@ -796,4 +796,60 @@ B1/B2/B3 backend sont complets et testés. DevFrontend peut enchaîner F1 (déj **Lot frontend F1/F2 : GO.** -Étape suivante : QA pour validation finale. \ No newline at end of file +Étape suivante : QA pour validation finale. +# QA — Validation finale #84 + +## Méthode + +Relecture indépendante du cadrage UX et Architect vs implémentation (`lib/application/use_cases.dart`, `lib/presentation/profile_screen.dart`), relecture de la couverture de test (`test/application/use_cases_test.dart`, `test/infrastructure/drift_repositories_test.dart`, `test/presentation/profile_screen_test.dart`), puis ré-exécution réelle des commandes. + +## Conformité cadrage + +- **Format `.gametime` v1** : envelope, `kind`, `formatVersion`/`minSupportedFormatVersion`, `counts`, `data`, `mediaFiles` — conforme au cadrage Architect (`LocalBackupCodec.encode/decode`, `lib/application/use_cases.dart:53-145`). +- **Validation de compatibilité** : `invalidFile` (kind/racine), `incompatibleFormat` (version absente/<1), `newerVersion` (version future), `corrupted` (data malformé) — tous les cas du cadrage sont bien distingués et testés. +- **Blocage séance active** : vérifié avant toute mutation (`importFrom`, ligne 197), pour `merge` et `replaceAll` indifféremment — conforme. +- **Transaction tout-ou-rien** : erreur générique pendant `applyImportSnapshot` → `importFailedNoMutation`, rollback confirmé par test dédié (round-trip avec ressource cassée → aucune insertion partielle, `drift_repositories_test.dart` ~ligne 673-732). +- **Fusionner (LWW)** : nouvelle ressource insérée, ressource plus récente dans le fichier remplace le local, local plus récent ignoré — couvert par test round-trip dédié. +- **Remplacer tout** : purge logique (soft-delete + change_log tombstone) + import intégral, compte/tokens/sync préservés — couvert par test dédié (`local backup replaceAll soft deletes absent data and imports file`). +- **UX Profil** : tous les libellés exacts du cadrage sont présents mot pour mot dans `profile_screen.dart` (`Sauvegarde locale`, `Exporter mes données`, `Préparation de l'export...`, `Lecture de la sauvegarde...`, `Importer cette sauvegarde ?`, `Fusionner`, `Remplacer tout`, doubles confirmations, tous les messages d'erreur typés, message compte connecté). Cas app locale vide correctement simplifié (`_confirmSimpleImport` avec bouton unique `Importer`). +- **Aucun libellé à éviter** (`Dump`, `Backup DB`, etc.) n'apparaît dans le code. + +## Couverture de test — vérifiée par lecture directe + +- Round-trip export→import Fusionner et Remplacer tout via le vrai codec : présents et couvrent insertion, préservation, tags, historique complet. +- Détection fichier invalide / corrompu / version incompatible / version future : couverte côté `use_cases_test.dart` (codec) et côté widget (`profile_screen_test.dart` : message dédié affiché). +- Blocage séance active : couvert côté repository ET côté widget. +- Comptage médias manquants/corrompus : couvert (`missingMediaCount` reflète un échec de checksum, régression testée explicitement suite au fix DevBackend). +- Double confirmation destructive Remplacer tout : couverte côté widget, y compris annulation de la seconde confirmation. +- Annulation sélection fichier silencieuse : couverte. + +## Ré-exécution des commandes (QA, indépendante) + +``` +$ flutter test --no-pub +00:08 +238 -1: Some tests failed. +Failing tests: + test/infrastructure/drift_repositories_test.dart: exercise repository round-trips exercise option combinations stopwatch score with stopwatch scored steps +``` +238 tests exécutés, 1 seul échec — identique à celui documenté par DevBackend/Main (contrainte FOREIGN KEY sur media assets de fixture, préexistant, sans rapport avec #84). Aucune régression détectée. + +``` +$ dart analyze +24 issues found. (uniquement des `info`, deprecations Flutter préexistantes + 2 `use_build_context_synchronously` déjà présents ailleurs dans profile_screen.dart, hors code #84) +``` +Propre : aucun `warning`/`error`. + +## Écarts non bloquants (confirmés par QA) + +1. **Pas de message dédié « Espace insuffisant »** : le cadrage UX rend ce message conditionnel (« Si l'erreur vient d'un manque d'espace disque et que l'app peut le savoir »). L'implémentation retombe sur le message générique `Export impossible pour le moment.` dans tous les cas d'erreur d'export. Acceptable au MVP : le cadrage n'impose pas cette distinction, seulement l'autorise. +2. **État intermédiaire d'import non distingué en deux phases** : `_importBusy` réutilise le même indicateur/libellé (`Lecture de la sauvegarde...`) pour la phase de lecture/preview ET pour la phase d'application (merge/replace). Le cadrage ne demande explicitement ce libellé que pour la validation initiale ; il n'y a pas d'exigence UX d'un libellé distinct pendant l'application. Acceptable au MVP, cohérence UX conservée (bouton désactivé dans les deux cas, pas d'action concurrente possible). + +Aucun autre écart trouvé entre cadrage UX/Architect et implémentation. + +## Verdict + +**GO.** + +Implémentation conforme au cadrage UX et Architect, couverture de test adéquate sur les invariants critiques (LWW, transaction tout-ou-rien, blocage séance active, validation de version, comptage médias), suite complète verte à l'exception de l'échec préexistant hors périmètre, `dart analyze` propre. Les 2 écarts relevés par DevFrontend sont non bloquants et n'appellent pas de correction avant clôture. + +Étape suivante : Git peut clôturer le ticket #84.