From 4c80ada3de08505e5c03f62b2d5e56e80b7b8016 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Thu, 9 Jul 2026 13:20:29 +0200 Subject: [PATCH] =?UTF-8?q?doctrine(bdd-testing):=20remplacer=20le=20cavea?= =?UTF-8?q?t=20poll=20par=20la=20r=C3=A8gle=20=C2=AB=20ne=20jamais=20polle?= =?UTF-8?q?r=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'ancien caveat_poll-broker-reads érigeait à tort le POLLING en pratique de test. Remarque utilisateur : le polling est un anti-pattern dans le contexte NextGraph (par abonnement). Remplacé par rule_no-broker-polling : attendre le push réactif / la barrière du 1er State ; ne JAMAIS re-interroger le broker en boucle. Fallback pragmatique admis : un intervalle court qui OBSERVE l'état réactif déjà mis à jour (pas une re-lecture broker) — au plus près de l'utilisateur qui attend. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../bdd-testing/caveat_poll-broker-reads.md | 36 ------------- .../bdd-testing/rule_no-broker-polling.md | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+), 36 deletions(-) delete mode 100644 .project/concepts/bdd-testing/caveat_poll-broker-reads.md create mode 100644 .project/concepts/bdd-testing/rule_no-broker-polling.md diff --git a/.project/concepts/bdd-testing/caveat_poll-broker-reads.md b/.project/concepts/bdd-testing/caveat_poll-broker-reads.md deleted file mode 100644 index 6fdde1a..0000000 --- a/.project/concepts/bdd-testing/caveat_poll-broker-reads.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -type: caveat -summary: Les lectures @data/@multibrowser contre le broker réel convergent avec un lag de sync (~s) — asserter en POLLING borné, jamais en lecture unique ; une lecture one-shot course la fenêtre de sync/réactivité et flake. Vaut pour les lectures à froid (reconnexion) ET la propagation réactive (compteur cross-navigateur). -last_checked: 2026-07-08 ---- - -# Asserter les lectures broker en polling, pas en one-shot - -Contre le **broker réel** (@data, @multibrowser), une donnée écrite n'est pas -lisible **instantanément** par un lecteur : il y a un **lag de sync** (le CRDT du -doc doit se propager/s'ouvrir avant qu'une lecture ancrée le rende). Mesuré : une -donnée fraîche se lit typiquement en **~1 s**, parfois après la 1ʳᵉ tentative. - -**Piège** : une assertion qui lit **une seule fois**, tout de suite, **course -cette fenêtre** et échoue de façon **flaky** — alors que le comportement applicatif -est correct (l'UI réelle est **réactive** : `doc_subscribe` fait converger l'écran -en quelques secondes). Un test qui lit one-shot mesure le lag, pas le bug. - -**Règle** : asserter en **POLLING borné** (re-lire en boucle jusqu'à ~10-15 s max) -la condition attendue. C'est ce que fait déjà la matérialisation côté steps ; toute -NOUVELLE assertion de lecture doit suivre le même pattern. - -Deux familles concernées, toutes deux vérifiées : -- **Lecture à froid / reconnexion** — une session verifier fraîche (page fraîche, - nouveau login) relit ses propres docs après ouverture de repo ; la 1ʳᵉ lecture - peut rendre 0, la suivante les données. Voir `event/reconnexion-meme-identite` - (les 3 `Then` de la page fraîche pollent) et `event/steps/data/reconnexion.steps.ts`. -- **Propagation réactive cross-navigateur** — un compteur écrit par le propriétaire - se propage au `doc_subscribe` d'un autre navigateur ; asserter « passe à {int} - sans reload » en attendant la convergence. Voir le scénario réactif de - `event/e2e-multibrowser.feature` (POV A **et** B). - -Ce caveat porte sur **comment écrire les assertions** contre le broker — pas sur -les internes NextGraph (lag/sync/ouverture de repo), qui vivent dans le repo -`@ng-eventually/client`. Voir aussi [[caveat_wallet-bloat-hang]] (autre source de -flakiness @data : le profil gonflé qui fait hanger les requêtes ancrées). diff --git a/.project/concepts/bdd-testing/rule_no-broker-polling.md b/.project/concepts/bdd-testing/rule_no-broker-polling.md new file mode 100644 index 0000000..b7d1ffc --- /dev/null +++ b/.project/concepts/bdd-testing/rule_no-broker-polling.md @@ -0,0 +1,52 @@ +--- +type: rule +summary: Ne JAMAIS poller le broker (re-lire en boucle « c'est là ? »). NextGraph est par abonnement — la donnée arrive par PUSH, et le 1er `State` d'un `doc_subscribe` est la barrière de sync déterministe (après lui : présence garantie / absence définitive). Tests ET app attendent le push / l'état réactif settlé, jamais une boucle de re-lecture broker. +last_checked: 2026-07-09 +--- + +# Ne jamais poller le broker — attendre l'abonnement + +NextGraph est **par abonnement (réactif)**. Une lecture n'est PAS « interroge en +boucle jusqu'à ce que ça apparaisse » ; c'est « abonne-toi, réagis au push ». Le +**1er `State`** d'un `doc_subscribe` marque la fin de la synchronisation initiale +(barrière synchrone) : après lui, la **présence** d'une donnée est **garantie** et +l'**absence** est **définitive**. Contrat vérifié empiriquement côté SDK +(`@ng-eventually/client`, test e2e « CONTRAT 3 »). + +## L'anti-pattern à bannir + +``` +for (i = 0; i < N; i++) { if (await authParticipationCount(...) === X) break; sleep(500); } +``` + +Toute boucle qui **re-interroge le broker** (`authParticipationCount`, +`listMyEntityDocs`, `sparql_query` répétés) pour « attendre » une donnée est +proscrite : elle masque le vrai mécanisme, fragilise le test (timeout deviné), et +contredit frontalement le modèle NextGraph. C'est la remarque qui a fait supprimer +l'ancien caveat qui, à tort, érigeait le polling en pratique. + +## Ce qu'il faut faire + +Attendre le **push réactif**. En pratique (app ET test) : l'état réactif +(`AD().*` alimenté par `subscribeDoc` dans le contexte de données) se met à jour +**au push**. On attend que CET état reflète l'attendu — on **observe l'état réactif +settlé**, on ne ré-émet PAS de lecture broker. Le mécanisme de données est +l'abonnement ; l'attente ne fait qu'**observer le résultat réactif**. + +- App : l'écran est déjà réactif (`subscribeDoc` → re-render au push) — pas de poll + applicatif, pas de spinner piloté par timeout deviné (si un état d'attente est + voulu, il vient de la barrière d'abonnement native, pas d'un signal ajouté). +- Test : **un helper qui attend le push/la barrière de façon fiable est bienvenu** + (fiabilise sans fragiliser). Ce qui est banni, c'est la **boucle de re-lecture**, + pas l'attente d'un signal. +- **Fallback pragmatique** : si attendre strictement le push/signal s'avère fragile + d'une manière ou d'une autre, un **intervalle court** (`setInterval` / re-check + rapproché) qui **observe l'état réactif DÉJÀ mis à jour** (l'état local alimenté + par l'abonnement — PAS une re-lecture broker) est acceptable : c'est au plus près + de ce que vit l'utilisateur, qui **attend** simplement que l'écran (réactif) se + mette à jour. La ligne rouge est invariante : **ne jamais re-interroger le broker + en boucle** ; observer l'état réactif settlé, oui. + +Voir aussi [[caveat_wallet-bloat-hang]] (autre source de flakiness @data, +orthogonale). Le mécanisme non-polling côté lib (`open-repo` : subscribe + attendre +le 1er State + lire) vit dans le repo `@ng-eventually/client`, pas ici.