fix: une tentative dépassée n'ouvre plus rien, et libère ce qu'elle avait ouvert

This commit is contained in:
Sylvain Duchesne
2026-08-17 14:01:07 +02:00
parent 7b35300723
commit c7c6cb96ea
4 changed files with 312 additions and 10 deletions
@@ -545,3 +545,88 @@ describe("listing the inboxes when one of the two registers cannot be read", ()
}
});
});
/**
* Bob shares TWO documents with Alice, each held back separately, so a test has two
* independent arrivals to drive the observation with rather than one.
*/
async function bobSharesTwiceWithAlice(): Promise<Quad[][]> {
await signIn("alice");
await storeRegistry.createEntityDoc("protected"); // her first visit — now she exists
await signIn("bob");
const aliceInbox = await userInbox("alice", "protected");
const batches: Quad[][] = [];
for (let round = 0; round < 2; round += 1) {
const doc = await storeRegistry.createEntityDoc("protected");
await inboxSurface.share(doc, "alice");
batches.push(heldByTheBroker(aliceInbox));
}
return batches;
}
describe("a register that stays unreadable", () => {
/**
* The retry that would repair the shortfall is also what re-reads it.
*
* Every signal the observation listens to runs a full cycle, and each cycle re-discovers a
* condition that never stopped holding — so ONE deposit put three copies of the same line
* in the log. A report that repeats on its own is not more information; it is what buries
* the report that means something, and it invites reading a persistent fault as a
* recurring one.
*/
test("is reported ONCE, not once per enumeration", async () => {
const { inTransit } = await bobSharesWithAlice();
const store = (await resolveAccount("alice"))?.docPrivate;
if (store === undefined) throw new Error("the fixture did not give Alice a private store");
fake._failReadsOn.add(store);
const reported = await whileWatchingTheLog(async () => {
try {
await signIn("alice");
} catch {
// The restore rejects on the unreadable store; she is connected regardless.
}
await converge();
fake._deliver(inTransit);
await converge();
await converge();
});
fake._failReadsOn.delete(store);
expect(reported.filter((l) => /could not all be listed/.test(l))).toHaveLength(1);
});
/**
* Once per OCCURRENCE, and a second occurrence is a real one.
*
* Silencing the repeat by remembering "already said" and never forgetting it would trade a
* noisy log for a mute one: the register breaking again, after a spell of working, is news
* — and it is exactly the case the retry exists for.
*/
test("is reported AGAIN once the list has come back WHOLE in between", async () => {
const [first, second] = await bobSharesTwiceWithAlice();
const store = (await resolveAccount("alice"))?.docPrivate;
if (store === undefined) throw new Error("the fixture did not give Alice a private store");
fake._failReadsOn.add(store);
const reported = await whileWatchingTheLog(async () => {
try {
await signIn("alice");
} catch {
// As above: the restore rejects, the connection stands.
}
await converge(); // the shortfall, first occurrence
fake._failReadsOn.delete(store); // the register is readable again…
fake._deliver(first ?? []); // …and an arrival makes the observation re-enumerate
await converge(); // the list comes back WHOLE
fake._failReadsOn.add(store); // and then it breaks a second time
fake._deliver(second ?? []);
await converge(); // the shortfall, second occurrence
});
fake._failReadsOn.delete(store);
expect(reported.filter((l) => /could not all be listed/.test(l))).toHaveLength(2);
});
});