fix!: la reprise cesse de dependre d-un front, et une ouverture abandonnee se termine
Quatrieme tour. Ce n'est pas un drapeau de plus : c'est la forme qui change. Les tours 2 et 3 construisaient tous deux la reprise ainsi - noter qu'on a refuse, puis reagir a un evenement ulterieur - et c'est la note qui etait fautive les deux fois : une fois conditionnee a quelque chose qui ne pouvait pas devenir vrai, une fois ecrite apres la fenetre ou arrive l'evenement decisif. Un front manque n'est pas rattrapable par construction. Donc : - LA DECISION EST UN NIVEAU. Les documents nommes sont reinterroges a chaque publication, jamais reportes depuis la barriere. Une capacite qui arrive change `isError` en document par le canal qui existait deja, et rien n'etant memorise, il n'y a aucun evenement a manquer. C'est aussi ce qui rend sans objet la question du porteur : une requete ouverte pendant qu'un autre etait connecte ne peut pas revenir a cet abonne en succes auquel il n'a pas droit. - UNE TENTATIVE QUI A COURU CONTRE UN CHANGEMENT S'EN APERCOIT au lieu d'attendre. `open()` echantillonne `CapRegistry.version()` avant la barriere et compare apres son echec ; un ecart signifie qu'elle a decide sur un terrain perime, et elle retente d'elle-meme. Cela termine : une tentative ne se repete que parce que le trousseau a bouge, les mouvements sont finis, et ceux qu'une tentative provoque elle-meme sont les capacites qu'elle depose - au plus une par document nomme. La comparaison de porteur disparait : `holderKey()` vaut "" tant que personne n'est connecte, donc un refus pris avant que l'identite se fixe - la ou les composants se montent - ne correspondait plus jamais ensuite. Un instantane d'echec inchange n'est plus republie : la surface documente sa stabilite, et huit arrivees sans rapport produisaient huit objets porteurs de huit `Error` distinctes pour un etat identique. Enfin, un defaut a moi, trouve en mesurant : le changement rendait la suite intermittente. `resetOpenedRepos()` abandonnait les ouvertures en vol sans les terminer, donc un appelant attendait les 8s du repli sur un abonnement demonte, puis repartait avec son etat de synchro efface - et « unknown » est la lecture LAXE, celle qui accepte qu'une lecture a zero ligne soit une absence averee. Une absence-qui-cache-un-echec latente, de la famille meme de cette tache. Une reinitialisation termine desormais ce qu'elle abandonne, en « timed-out ». Deux fixtures qui se couraient apres sont corrigees, raison ecrite a cote.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
|
||||
import { ensureRepoOpen, resetOpenedRepos } from "../src/emulated-verifier/open-repo";
|
||||
import { ensureRepoOpen, getSyncState, resetOpenedRepos } from "../src/emulated-verifier/open-repo";
|
||||
import { readDocumentSubjects } from "../src/surface/read-document";
|
||||
import { configure } from "../src/index";
|
||||
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
|
||||
@@ -55,7 +55,11 @@ const FP = "http://festipod.org/";
|
||||
|
||||
function makeFakeNgWithSubscribe(
|
||||
triplesByDoc: Record<string, Array<[string, string]>>,
|
||||
options: { pushState?: boolean } = {},
|
||||
) {
|
||||
// A broker that accepts the subscription and never pushes is the state the bounded
|
||||
// fallback exists for, and the only way to hold an open genuinely IN FLIGHT.
|
||||
const pushState = options.pushState !== false;
|
||||
const subscribed = new Set<string>();
|
||||
const subscribeCallOrder: string[] = [];
|
||||
|
||||
@@ -65,7 +69,7 @@ function makeFakeNgWithSubscribe(
|
||||
subscribeCallOrder.push(nuri);
|
||||
// Simulate initial State push (synchronously deferred so the subscription
|
||||
// setup promise path in ensureRepoOpen can resolve it).
|
||||
setTimeout(() => cb({ V0: { State: {} } }), 0);
|
||||
if (pushState) setTimeout(() => cb({ V0: { State: {} } }), 0);
|
||||
return () => {}; // unsubscribe fn
|
||||
});
|
||||
|
||||
@@ -168,6 +172,38 @@ describe("ensureRepoOpen", () => {
|
||||
await expect(ensureRepoOpen("did:ng:o:c")).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// An open ENDS, whatever happens to the registry underneath it.
|
||||
//
|
||||
// A reset - which is what a session change does, `syncSession` - tears down the
|
||||
// subscription an in-flight open is waiting on. Until 2026-09-21 it then left that open
|
||||
// hanging: its `State` could no longer arrive, so the caller waited out the whole
|
||||
// 8-second fallback and only then proceeded, with its sync state CLEARED. Cleared reads
|
||||
// as `"unknown"`, and `unknown` is the LAX answer - `emulated-verifier/index-deposit.ts`
|
||||
// accepts it as "a zero-row read means the document is empty" - so a read taken right
|
||||
// after a session change could have an absence read into it. Both halves are the point:
|
||||
// it must END, and it must end as `"timed-out"`.
|
||||
it("a reset ENDS the opens it abandons, and marks them timed-out rather than unknown", async () => {
|
||||
const ng = makeFakeNgWithSubscribe({}, { pushState: false });
|
||||
inject(ng);
|
||||
|
||||
const inFlight = ensureRepoOpen("did:ng:o:abandoned");
|
||||
await new Promise((r) => setTimeout(r, 5)); // let the subscribe land
|
||||
expect(ng.doc_subscribe).toHaveBeenCalledTimes(1);
|
||||
|
||||
resetOpenedRepos();
|
||||
|
||||
// Ends at once. Without the fix it resolves only when the 8s fallback fires, which is
|
||||
// longer than any test's patience and longer than any page's.
|
||||
await expect(
|
||||
Promise.race([
|
||||
inFlight.then(() => "ended"),
|
||||
new Promise((r) => setTimeout(() => r("still waiting"), 200)),
|
||||
]),
|
||||
).resolves.toBe("ended");
|
||||
// ...and NOT as a barrier that was reached.
|
||||
expect(getSyncState("did:ng:o:abandoned")).toBe("timed-out");
|
||||
});
|
||||
|
||||
it("the anchored document read triggers doc_subscribe then returns data (integration path)", async () => {
|
||||
const ng = makeFakeNgWithSubscribe({
|
||||
"did:ng:o:p": [[TYPE, `${FP}Participation`], [`${FP}event`, "did:ng:o:e"]],
|
||||
|
||||
Reference in New Issue
Block a user