test(db): add abort-racing-send + rejectAllWaiters coverage; document notifyOneWaiter invariant

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mika Kuns
2026-05-20 16:15:39 +02:00
parent 31584fe623
commit b74e969229
2 changed files with 46 additions and 0 deletions

View File

@@ -107,4 +107,49 @@ describe("MailboxStore.waitForMessage", () => {
expect([r1.message.body, r2.message.body]).toEqual(["m1", "m2"]);
}
});
it("abort racing send: message is either delivered or remains pending, never lost", async () => {
store.upsertMailbox("alice");
store.upsertMailbox("bob");
const ac = new AbortController();
const pending = store.waitForMessage("bob", 5000, ac.signal);
// Fire abort and send in the same tick — either order is valid as long as the message is never lost.
ac.abort();
store.send("alice", "bob", "racy");
const r = await pending;
if (r.kind === "aborted") {
// Message must still be in DB for the next caller.
expect(store.peek("bob").pending).toBe(1);
} else if (r.kind === "message") {
expect(r.message.body).toBe("racy");
expect(store.peek("bob").pending).toBe(0);
} else {
throw new Error(`unexpected kind: ${r.kind}`);
}
});
it("rejectAllWaiters resolves every pending waiter as aborted and empties the bucket map", async () => {
store.upsertMailbox("bob");
store.upsertMailbox("carol");
const ac1 = new AbortController();
const ac2 = new AbortController();
const ac3 = new AbortController();
const w1 = store.waitForMessage("bob", 5000, ac1.signal);
const w2 = store.waitForMessage("bob", 5000, ac2.signal);
const w3 = store.waitForMessage("carol", 5000, ac3.signal);
// Give all three a chance to register their waiters.
await new Promise((r) => setTimeout(r, 10));
store.rejectAllWaiters();
const [r1, r2, r3] = await Promise.all([w1, w2, w3]);
expect(r1.kind).toBe("aborted");
expect(r2.kind).toBe("aborted");
expect(r3.kind).toBe("aborted");
});
});