diff --git a/android-client/TESTS.md b/android-client/TESTS.md index 8ed78ed..bae5f70 100644 --- a/android-client/TESTS.md +++ b/android-client/TESTS.md @@ -1,60 +1,76 @@ # Client Android — relecture et scénario d'essai -Relecture du 2026-08-22, avant d'ajouter la vérification de fond, les réglages -et les icônes. Le but n'était pas de tout relire mais de chercher là où cette -séance a montré que les défauts se logent : **les frontières entre fichiers, -les chemins d'erreur, et ce qui traverse les threads.** +Réécrit le **2026-08-28**, après une séance qui a remué les deux secrets du +coffre, l'écran des réglages, le code de déverrouillage et le clavier. La +version précédente décrivait une application qui n'existe plus. + +Ce que cette séance a confirmé, et qui vaut plus que n'importe quelle liste : +**les défauts se tiennent par la main.** Corriger le clavier qui volait le +premier appui a laissé le clavier ouvert ; le clavier ouvert recouvrait le +message ; et le message invisible cachait qu'une action avait réussi. Aucun des +deux derniers n'était observable tant que le premier tenait. D'où la règle qui +suit : **quand un essai échoue, lire le journal AVANT de retenter**, parce que la +synchronisation le vide. --- -## Ce que la relecture a trouvé +## Ce que la séance a corrigé, et pourquoi c'est éprouvable -### 1. La fusion ne transportait pas les dossiers — CORRIGÉ +### 1. Le journal ne perdait plus les causes — CORRIGÉ, et c'est le plus important -`MergeSnapshots` fondait `entries` et `tombstones`, pas `folders`. Un dossier -créé sur le téléphone — hors connexion, ou juste avant qu'un autre appareil -publie — disparaissait donc de la liste, **tandis que les entrées gardaient son -nom**. Elles se retrouvaient rangées dans un dossier que rien n'affiche : -introuvables sans qu'on le sache. +Trois trous, tous du même genre : une faute arrivait, et rien ne la nommait. -C'est exactement la panne contre laquelle `EnsureFolder` avait été écrit, et -elle est revenue par une autre porte. Le report est additif — on ne retire pas -un dossier du distant parce qu'il manque ici. +- **Les gestionnaires d'erreur lisaient l'exception après sa libération.** + `RegisterLocal` et `LoginLocal` mettaient leur handler en file avec + `TThread.Queue`, puis y lisaient `E.Message` — or Delphi libère `E` en sortant + du `except`, bien avant que la file s'exécute. Résultat : un message vide, et + parfois une violation d'accès. **Le vrai motif n'a jamais été affiché de toute + l'histoire de ce chemin.** +- **Le dispatch du pont n'était gardé par rien** : une faute dans un handler + remontait à FMX, qui affichait « Access violation » avec une adresse pendant + que le journal montrait la commande reçue suivie de rien. +- **Le retour sur le thread principal** (`TThread.Queue`) n'était couvert ni par + le `except` du thread, ni par celui du pont : il s'exécute après que les deux + ont rendu la main. -### 2. Le témoin d'attente n'existait que sur la liste — CORRIGÉ +**Ce que ça change pour les essais** : un échec porte désormais un nom. Si un +essai ci-dessous échoue sans laisser de ligne au journal, c'est un quatrième +trou du même genre, et il faut le traiter avant de chercher le défaut. -Le point orange était posé sur le cadenas, qui n'existe que dans la liste. Or -après un verrouillage on atterrit sur l'accueil — précisément là où l'on décide -de synchroniser. Il est maintenant aussi sur *Sync*. +### 2. Deux secrets, longtemps confondus — CORRIGÉ -### 3. Trois routines ne libèrent pas l'indicateur d'occupation dans un `finally` +Le mot de passe de création scellait le conteneur local **et** prétendait +sceller l'instantané du dépôt. Un coffre créé ici publiait donc sous son propre +mot de passe, que le poste ne savait plus ouvrir. -`SaveAttachment`, `RegisterLocal` et `LoginLocal` le font dans le chemin normal -**et** dans leur `except`, ce qui couvre tout ce qui descend d'`Exception`. Une -erreur d'un autre genre laisserait le voile en place. +- `FVaultPwd` ouvre `vault-local.json`, et rien d'autre. +- Le secret du dépôt vit dans les réglages, doit être identique sur tous les + appareils, et lui seul ouvre et rescelle l'instantané. +- **Ils ne se rencontrent qu'à un endroit**, la réconciliation : le conteneur + local s'ouvre sous le sien, puis la clé est jetée avant qu'on touche au dépôt. -**Non corrigé, et assumé** : le minuteur de 25 s le lève de toute façon, et -c'est son rôle. Le signaler ici vaut mieux que d'ajouter trois `finally` qui -donneraient l'illusion d'une garantie que seul le minuteur apporte. +Migration : rien à rechiffrer. Les coffres existants ont simplement les deux +valeurs égales, et cessent d'être forcés de le rester. -### 4. `ParseJSONValue(...) as TJSONObject` fuit sur une entrée qui n'est pas un objet +### 3. « Coffre ouvert » se lisait sur le nombre d'entrées — CORRIGÉ -Le transtypage lève avant l'affectation, donc la valeur analysée n'est jamais -libérée. Dix-sept occurrences. **Non corrigé** : n'arrive que sur un conteneur -corrompu, où l'on a de plus gros ennuis, et le processus est de toute façon en -train d'échouer bruyamment. À traiter le jour où l'on touche à ces fonctions. +Un coffre **vide** en compte zéro, comme un coffre fermé. Le garde de `DoSync` +restait donc vrai après l'ouverture, réarmait la reprise, et l'on tournait en +rond. Le même idiome coupait `OnIdleTimer` : un coffre vide ne s'auto-verrouillait +jamais. Les trois lisent `FPlain`, le clair en main. -### 5. Ce qui a été vérifié et va bien +### 4. Ce qui reste assumé, sans correction -- **Échappement HTML** : `esc()` couvre nom, sous-titre, initiale, URI d'icône, - libellés, valeurs, noms de dossiers, options de liste. Une entrée nommée - `