Files
Password-Manager/android-client/TESTS.md
T
r-zakarya ba4ab18d0f feat: the last three passwords travel, and the phone can show them
The desktop keeps twenty previous versions per entry and never sent any. It
now sends three.

Three, not twenty, because the trade is not symmetrical. On the desktop they
sit encrypted column by column and never move. In the snapshot everything
travels in the clear inside the container, and comes down into a phone cache
sealed only by the device key - not by the master password. An old password
stays sensitive: it is often still valid somewhere else. The real use of the
list is short - "I changed it recently, what was it?" - and three cover it.
The other seventeen would be copies with no need facing them.

The phone reads them ON DEMAND from the raw snapshot, never into TVault:
loading them with the vault would put every entry's old passwords in the clear
in memory from the moment it opens, for something one looks at now and then.

Copying goes by RANK, not by value. The page displays the text but never sends
it back - that is the bridge's rule everywhere else, and an old password has
no reason to be the exception just because it happens to be on screen.

They leave with the plaintext at lock, on both sides.
2026-08-22 16:14:04 +01:00

206 lines
10 KiB
Markdown

# 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.**
---
## Ce que la relecture a trouvé
### 1. La fusion ne transportait pas les dossiers — CORRIGÉ
`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.
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.
### 2. Le témoin d'attente n'existait que sur la liste — CORRIGÉ
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*.
### 3. Trois routines ne libèrent pas l'indicateur d'occupation dans un `finally`
`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.
**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.
### 4. `ParseJSONValue(...) as TJSONObject` fuit sur une entrée qui n'est pas un objet
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.
### 5. Ce qui a été vérifié et va bien
- **É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
`<script>` ne s'exécute pas. C'était le risque réel — le coffre contient du
texte arbitraire et la page construit son HTML par `innerHTML`.
- **La file de commandes du pont** libère `sending` dans un `finally` : une
exception au retrait de l'iframe ne peut plus rendre l'interface muette.
- **Les états du `body`** (`typing`, `selecting`, `searching`, `on-list`,
`on-detail`, `is-busy`) ne portent le nom d'aucun élément — le piège qui
éteignait la page entière ne se répète nulle part.
---
## Scénario d'essai
À dérouler dans l'ordre. Chaque étape dit **ce qu'il faut voir**, pas seulement
ce qu'il faut faire : un test dont on ne sait pas ce qu'il doit produire ne
prouve rien.
### A. Enregistrement hors connexion — le plus important
1. Coffre ouvert, **couper les données** (mode avion).
2. Modifier un mot de passe → message *« saved here, not published yet »*, et
**point orange** sur le cadenas.
3. Verrouiller → le point doit être **sur le bouton Sync** de l'accueil.
4. **Fermer complètement l'application** (glisser depuis les récentes), rouvrir,
ouvrir le coffre → le point doit **être encore là**. C'est l'étape qui
éprouve la persistance du drapeau ; sans elle, le reste ne prouve rien.
5. Rétablir le réseau, *Sync* → *« Offline changes published »*, point éteint.
6. Sur le poste, synchroniser → la modification doit y être.
### B. La fusion, qui est le cœur du sujet
1. Téléphone **hors ligne**, modifier l'entrée **X**.
2. Sur le poste, modifier une **autre** entrée **Y**, synchroniser.
3. Téléphone en ligne, *Sync*.
4. **Les deux modifications doivent survivre.** Si une seule survit, c'est
l'arbitrage sur `updated_at` qu'il faut regarder.
5. Variante avec un **dossier créé hors connexion** sur le téléphone : il doit
apparaître dans la barre de dossiers après la fusion, et sur le poste après
sa synchronisation. C'est le défaut n° 1 ci-dessus.
### C. Notes multilignes
1. **+** → *Note* → zone de texte pleine largeur, *Site* et *Username* disparus.
2. Taper plusieurs lignes — la touche Entrée doit **insérer un saut**, pas
replier le clavier.
3. Enregistrer, rouvrir → les ruptures doivent être là **en lecture**.
4. Basculer *Login* → *Note* après avoir tapé : la saisie doit **survivre**.
5. Contre-épreuve : dans un champ d'une ligne (*Name*, code de déverrouillage),
Entrée doit toujours **replier le clavier**.
### D. Sélection et actions de liste
1. Appui long → barre contextuelle, compteur.
2. Deuxième ligne → compteur à 2.
3. **Dupliquer** → deux copies, suffixées *(copy)*.
4. **Supprimer** → confirmation en deux temps, puis les lignes disparaissent.
5. Touche **retour** pendant une sélection → elle s'annule **sans verrouiller**.
6. Sur le poste, synchroniser → les suppressions doivent y être répercutées.
### E. Le dépôt a bougé — témoin bleu
1. Coffre ouvert sur le téléphone, **le laisser ouvert**.
2. Sur le poste, modifier une entrée et synchroniser.
3. Attendre — la vérification bat toutes les **dix minutes**. Le journal doit
dire `remote has moved - sync to bring it in`, et un **point bleu** paraître
sur *Sync* et sur le cadenas.
4. **Rien ne doit changer à l'écran de soi-même.** Si l'entrée qu'on regarde se
met à jour toute seule, c'est que le sondage adopte au lieu de signaler.
5. *Sync* → le point bleu s'éteint.
6. Les deux ensemble : provoquer A puis E sans synchroniser → **orange et bleu
côte à côte**. Ils demandent deux gestes différents ; les confondre dirait
« quelque chose ne va pas » sans dire quoi.
### F. Réglages
1. Roue dentée dans la liste, ou *Settings* sur l'accueil → l'écran s'ouvre.
2. Les trois champs du dépôt doivent porter **les valeurs en place**, pas du
vide : ils ne sont qu'une vue des champs que Delphi connaît déjà.
3. Modifier l'URL, revenir par la flèche, *Sync* → la nouvelle valeur sert.
C'est la contre-épreuve du report à la fermeture : un réglage modifié puis
refermé **sans quitter le champ** doit compter.
4. Touche **retour** dans les réglages → elle referme **sans** verrouiller ni
revenir à la liste.
5. **Poser un code** depuis les réglages : taper, *Set this code*, puis ouvrir
le coffre → *« Unlock code changed »*. Le sceau se pose toujours à
l'ouverture, et nulle part ailleurs.
6. **Retirer le code** : *Remove the unlock code*, deux appuis → *« Unlock code
removed »*. Fermer l'application, rouvrir → **le code n'est plus demandé**.
7. Contre-épreuve du retrait : verrouiller, puis essayer de retirer le code
sans avoir ouvert le coffre → refus *« Open the vault first »*. C'est ce qui
empêche quelqu'un tenant le téléphone déverrouillé de lever la protection
sans la connaître.
### G. Icônes
1. Ouvrir une entrée dont le *Site* est une adresse — `git.ai-agents4you.com`.
2. Trois points → **Fetch icon** → l'icône doit apparaître dans la liste.
3. Une entrée dont le site n'est **pas** une adresse (*Gitea*, ou vide) →
refus *« no web address to look up »*. Sans point dans le nom, il n'y a
rien à demander au relais.
4. Un domaine à trois niveaux (`chat.qwen.ai`) doit retomber sur le second
niveau : c'est le deuxième essai.
5. **Remove icon** → l'entrée reprend son initiale.
6. Synchroniser depuis le poste : l'icône doit y arriver, et le poste ne doit
**pas** la traiter comme une icône choisie — c'est le rôle du drapeau de
provenance, mis à zéro par une recherche automatique.
### H. Anciens mots de passe
**Le poste doit synchroniser d'abord** : il ne les envoie que depuis ce soir,
et un instantané publié avant ne porte aucun historique.
1. Sur le poste, changer le mot de passe d'une entrée, puis synchroniser.
2. Téléphone : *Sync*, ouvrir l'entrée, trois points → **Previous passwords**.
L'ancienne version doit être là, avec sa date.
3. Copier l'une d'elles → le presse-papiers doit la porter, et le message dire
*« Previous password copied »*.
4. Touche **retour** → le voile se referme sans toucher au coffre.
5. Une entrée jamais modifiée → *« No previous password recorded »*, et non un
écran vide. Un instantané d'avant ce soir donne le même message : c'est
volontaire, et le libellé le dit plutôt que de laisser chercher une panne.
6. Contre-épreuve : verrouiller, rouvrir, **Previous passwords** → la liste
doit être reconstruite, jamais celle d'avant restée à l'écran.
### I. Pièces jointes
1. Ligne *Attachment* → flèche de téléchargement → le fichier apparaît dans
*Téléchargements* sous son nom, accents et arabe compris.
### J. Code de déverrouillage
1. Poser un code, ouvrir le coffre, fermer, rouvrir → il est demandé.
2. Code faux → *« Wrong unlock code »*, et **Open vault reste offert** (le
coffre n'est pas déclaré illisible).
3. Changer le code : ancien dans le premier champ, nouveau dans le second,
*Open vault* → *« Unlock code changed »*.
4. Contre-épreuve : taper n'importe quoi dans le champ du code puis *Sync* →
le code **ne doit pas changer**.
### K. Empaquetage
1. `build-apk.ps1 -Release -Keystore ... -Alias ...` → la sortie doit afficher
`Signer #1 certificate DN: CN=R Zakarya`. Si elle affiche `CN=Android
Debug`, la release ne vaut rien.
2. Elle ne s'installe pas d'elle-même, et c'est voulu.
---
## Ce qui n'est toujours pas éprouvé
- **Le chemin `-Release`** du script n'a jamais tourné.
- **§1 point 4** — l'invalidation de la clé par enrôlement d'une empreinte,
désamorcée par le code de déverrouillage tant qu'un code est posé.
- **`MergeSelfCheck`** tourne au démarrage en débogage et écrit
`merge selfcheck: the four rules hold` dans le journal. Personne ne l'a
encore lu.