fix(data): restore per-entity write round-trip against the real broker
The per-document isolation refactor (one doc per entity) broke every @data
round-trip against the real broker (0 events readable) — fake-ng unit tests
missed it. Root causes + fixes:
- ngSet.add cannot write to an empty subscription scope ("Set is readonly
because scope is empty") → write each entity DIRECTLY into its own document via
SPARQL (new data/entityWrites.ts: writeEntity/updateEntityField), typing each
field with the correct RDF term per the SHEX shape (else the ORM drops the
entity on read). Reactive set stays read-only; the doc NURI is registered into
useShape({graphs}) for reactive reads.
- Current principal made STABLE and username-derived (urn:festipod:user:<name>),
available immediately at login and invariant — so a Participation's mandatory
fp:user is never empty and identity/cap-owner/connections all key on the same
value.
- Discovery deposits AS the current identity (harness sets current user first).
- Idempotence/deregistration checks made authoritative against the broker;
participantCount persisted via SPARQL. rule_document-per-entity enriched with
these write/read + stable-principal lessons.
Round-trip restored (seed readable, inscription+notif, persistent deregistration,
public discovery all pass in isolation). NOT yet stably green as a full suite:
@data oscillates 15–20/21 — residual failures are environmental (participation-
read fan-out lag on an accumulating persistent test wallet), same class as the
Chromium saturation; not a logic bug. Durable fix (follow-up): non-fan-out
materialized read + per-scenario test-wallet isolation. app build+tsc + lib 89
tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,5 +33,26 @@ Cucumber → Playwright (Chromium, profil persistant)
|
||||
- **Flags Chromium** (`--disable-web-security`, `--allow-insecure-localhost`, désactivation de Private Network Access) : nécessaires car le broker public charge un harness `http://127.0.0.1` en iframe.
|
||||
- **Profil persistant** `.playwright-profile/` (gitignored, wallet en localStorage) — exige le vrai binaire Chrome, pas `chrome-headless-shell`.
|
||||
- **Serveur HTTP** lancé en `BeforeAll` (port auto), sert le HTML + `/harness.js` (fichiers séparés — le script inline casse à cause de caractères spéciaux du bundle).
|
||||
- **Subscriptions ORM** : les shapes des entités partageables sont souscrites sur le scope **protected** (`harness-ng.tsx` utilise `protectedNuri`), cohérent avec le placement des entités domaine côté app (concept `data-layer`).
|
||||
- **Bridge `window.__testData`** : `events`/`users`/`participations` (sets live), `currentUserId`, lookups (`getEvent`, `getEventByTitle`), mutations (`joinEvent`, `leaveEvent`, `updateEvent` — `joinEvent`/`leaveEvent` réels depuis T02.b/c : persistance Participation + inbox + Notification / DELETE-WHERE), requêtes (`isParticipating`, `getEventParticipants`).
|
||||
- **Bridge = le vrai chemin app (per-entité).** Depuis le passage à *un document par entité*
|
||||
(concept `data-layer`, [[rule_document-per-entity]]), le bridge `window.__testData`
|
||||
(`events`/`users`/`participations`, `joinEvent`/`leaveEvent`/`isParticipating`/
|
||||
`getEventParticipants`, `loadTestData`) **délègue au contexte de données de l'app**
|
||||
(`appData` via `FestipodDataProvider`) — c'est le chemin per-entité réel des écrans, pas une
|
||||
lecture au niveau du store-racine. Le harness monte donc l'**`AccountProvider`** et se logge
|
||||
par défaut (`@mariedupont`) pour établir l'identité courante (sans quoi le filtre ReadCap ne
|
||||
laisserait passer que le public). Il lit `appData` via une **ref vivante** (un snapshot capturé
|
||||
devient périmé après un re-rendu de seed).
|
||||
- Chemins probes de bas niveau conservés (scope store-racine `protectedNuri`) pour les
|
||||
scénarios ReadCap/isolation qui *gouvernent* ce document : `rawJoin`/`rawParticipations`,
|
||||
`governDocument`/`governProtected`/`documentNuri`, `FilterProbe`/`FanoutProbe`.
|
||||
- **Identité avant écriture.** Une `Participation` a un `fp:user` obligatoire ; comme la lecture
|
||||
du profil peut retarder derrière les events publics, les steps attendent
|
||||
`ensureCurrentUser()` avant `joinEvent` (sinon participation écrite sans user → jetée en
|
||||
lecture, ne fait jamais l'aller-retour) et attendent (`waitForFunction`) que la participation
|
||||
soit relue.
|
||||
- **Caveat wallet persistant** : le wallet partagé **accumule** les docs per-entité à chaque run
|
||||
(seed + inscriptions). Le fan-out de lecture (`listEntityDocs`) parcourt tous les docs de tous
|
||||
les comptes → ralentit et fait *timeouter* les steps quand le wallet est pollué. Pour une suite
|
||||
fiable, repartir d'un wallet **frais** (supprimer `.playwright-profile/` → recréation
|
||||
automatique) ; le seed connecté est volontairement **allégé** (peu de docs) car chaque
|
||||
`docCreate` est un aller-retour broker sériel ~2s.
|
||||
|
||||
Reference in New Issue
Block a user