From 534d8f91b7e65306bfcf99c1434ca7fb50b1b75e Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Mon, 21 Sep 2026 15:12:09 +0200 Subject: [PATCH] test: les deux cas qui ne doivent pas bouger, et la course que la barriere asynchrone ouvre (p) `graphs: ""` est l'autre orthographe du dataset entier, jamais un nom de document : le filtre reste seul a decider. C'est une garde, pas un test discriminant - `isNuri` ecarte deja la chaine vide -, et elle epingle le comportement plutot que le chemin. (q) se desabonner pendant que la barriere est en train d'etre franchie ne doit laisser aucune requete ouverte. La barriere rend l'ouverture asynchrone, donc il existe desormais une fenetre ou l'observable a des abonnes, pas de requete, et une ouverture en chemin ; verifie en retirant le controle de generation, ou la requete est creee apres le dernier auditeur et plus rien ne la ferme. --- .../polyfill/src/surface/use-shape-query.ts | 4 ++ .../polyfill/test/use-shape-query.test.ts | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/packages/polyfill/src/surface/use-shape-query.ts b/packages/polyfill/src/surface/use-shape-query.ts index 1545b41..0188c45 100644 --- a/packages/polyfill/src/surface/use-shape-query.ts +++ b/packages/polyfill/src/surface/use-shape-query.ts @@ -186,6 +186,10 @@ const WHOLE_DATASET = "did:ng:i"; * none, and so is anything that is not a NextGraph reference - an ordinary IRI names no * repo this library could hold a cap for, and the engine is left to match it or not. * Deduplicated, so a scope repeating a document opens it once. + * + * `""` is named beside the marker although `isNuri` would drop it anyway. It is the SDK's + * own alias for `did:ng:i`, and a reader should be able to see that here rather than have + * to work out that a guard two files away happens to exclude it. */ function namedDocuments(scope: Scope): Nuri[] { const out: Nuri[] = []; diff --git a/packages/polyfill/test/use-shape-query.test.ts b/packages/polyfill/test/use-shape-query.test.ts index 7996775..0d96347 100644 --- a/packages/polyfill/test/use-shape-query.test.ts +++ b/packages/polyfill/test/use-shape-query.test.ts @@ -609,6 +609,54 @@ describe("useShapeQuery", () => { unsub(); }); + it("(p) the empty-string `graphs` is the whole dataset too, and is not guarded either", async () => { + const ng = makeFake(); + const orm = makeOrm(); + inject(ng, orm); + setCurrentUser("omar"); + const mine = await createEntityDoc("omar", "protected"); + seedEvent(orm, mine, `${mine}:q:one`, "Mine"); + // Somebody else's, in the same local dataset: the filter is what keeps it out, and + // must stay the one that decides here - `""` names no document to refuse. + seedEvent(orm, "did:ng:o:not-omars", "did:ng:o:not-omars:q:one", "Theirs"); + + // `normalizeScope` maps `""` onto `did:ng:i` (`@ng-org/orm`'s `types.ts`), so the two + // spellings are one marker. A barrier that treated `""` as a document name would + // refuse the whole dataset for a reference nobody wrote. + const obs = useShapeQuery(EventShape, { graphs: "" }); + const unsub = obs.subscribe(() => {}); + await tick(); + await tick(); + + const snap = obs.getSnapshot(); + expect(snap.isError).toBe(false); + expect(snap.isSuccess).toBe(true); + expect(titles(snap.data)).toEqual(["Mine"]); + unsub(); + }); + + it("(q) unsubscribing while the barrier is still crossing leaves no query open", async () => { + const ng = makeFake(); + const orm = makeOrm(); + inject(ng, orm); + setCurrentUser("pia"); + const mine = await createEntityDoc("pia", "protected"); + seedEvent(orm, mine, `${mine}:q:one`, "Mine"); + + // The barrier is asynchronous now, so there is a window in which the observable has + // subscribers, no query, and an open on its way. A teardown landing in that window + // must not be overtaken by the open it superseded - otherwise the shape query is + // created after the last listener left and nothing is ever going to close it. + const obs = useShapeQuery(EventShape, { graphs: [mine] }); + const unsub = obs.subscribe(() => {}); + unsub(); // synchronously, while `ensureRepoOpen` is still awaiting the first `State` + await tick(); + await tick(); + await tick(); + + expect(orm.openCount()).toBe(0); + }); + // -- teardown ------------------------------------------------------------ it("(k) the last unsubscribe closes the shape query", async () => {