Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions functions/scripts/scenario-many-users.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
* cloud.
*
* Production is refused by name. On the test project, what it has not got
* is skipped and said so: check-ins (no media account to vouch for a photo),
* reviews, and the trigger that counts someone leaving a meetup.
* is skipped and said so: check-ins (no media account to vouch for a photo)
* and reviews.
*
* Exit code 0 only if every check passed.
*/
Expand Down Expand Up @@ -636,10 +636,7 @@ async function scenarios() {
check("meetup: the ones left out are told it is full, not given an error",
failed.refused === 0,
`${toldFull.length} told "${toldFull[0]?.result?.error ?? "-"}"; ${failed.refused} errors${failed.codes ? ` (${failed.codes})` : ""}`);
if (CLOUD) {
skip("meetup: five leave at once, the count follows", "the test project has no onParticipantDeleted");
skip("meetup: the freed places go to the next ones, and no more", "the test project has no onParticipantDeleted");
} else {
{
const leavers = roster.filter((r) => r.id !== users[0].uid).slice(0, 5);
const byUid = new Map(users.map((u) => [u.uid, u]));
await Promise.all(leavers.map((r) => clientDelete(byUid.get(r.id), `meetups/${meetupId}/participants/${r.id}`)));
Expand All @@ -655,10 +652,13 @@ async function scenarios() {
`${rejoin.filter((j) => j.ok && j.result?.success !== false).length} got in of ${outside.length}; participantCount ${meetup?.participantCount}, room for ${CAP}`);
}

// --- A second meetup, with room for everyone, cancelled while they are
// joining: whoever got in before the cancel is on the roster and counted,
// and nobody gets in after it.
// --- A second meetup, with room for everyone: half of them join, and it is
// cancelled while the other half are joining. Whoever got in before the
// cancel is on the roster and counted, nobody gets in after it, and
// everyone on the roster is told.
const crowd = users.slice(1, Math.max(2, N - 1));
const firstHalf = crowd.slice(0, Math.ceil(crowd.length / 2));
const secondHalf = crowd.slice(firstHalf.length);
const late = users[N - 1];
const roomy = await call(users[0], "createMeetupCallable", {
title: `TEST CONTENT ${TAG} cancelled`, description: "TEST CONTENT cancelled while people join",
Expand All @@ -671,22 +671,22 @@ async function scenarios() {
const roomyId = roomy.result?.meetupId ?? roomy.result?.id;
if (!roomyId) throw new Error(`the meetup to cancel came back without an id: ${JSON.stringify(roomy).slice(0, 200)}`);
made.meetups.push(roomyId);
const joining = Promise.all(crowd.map((u) => call(u, "joinMeetupCallable", { meetupId: roomyId, petId: petIds[users.indexOf(u)] })));
const joinRoomy = (u) => call(u, "joinMeetupCallable", { meetupId: roomyId, petId: petIds[users.indexOf(u)] });
const firstIn = (await Promise.all(firstHalf.map(joinRoomy))).filter((j) => j.ok && j.result?.success !== false).length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require the seeded half to join successfully

If any of these pre-cancellation joins are rejected or fail, firstIn is only reported in diagnostic text and the later notification assertion derives its recipients from the reduced roster. In the extreme case where every join fails, toTell is empty and toTell.every(...) passes vacuously, so all cancellation checks can pass without testing the large-roster fan-out this change is intended to cover. Assert that firstIn === firstHalf.length before starting the cancellation race.

Useful? React with 馃憤聽/ 馃憥.

const joining = Promise.all(secondHalf.map(joinRoomy));
await sleep(CLOUD ? 400 : 100);
const cancel = await call(users[0], "cancelMeetupCallable", { meetupId: roomyId });
const joinedBefore = (await joining).filter((j) => j.ok && j.result?.success !== false).length;
const joinedBefore = firstIn + (await joining).filter((j) => j.ok && j.result?.success !== false).length;
const after = await call(late, "joinMeetupCallable", { meetupId: roomyId, petId: petIds[N - 1] });
const [cancelled, cancelledRoster] = await Promise.all([adminGet(`meetups/${roomyId}`), adminQuery(`meetups/${roomyId}`, "participants")]);
check("meetup: cancelled while everyone joins, nobody gets in after",
cancel.ok && cancelled?.status === "cancelled" && after.ok && after.result?.success === false,
`cancel ${cancel.ok ? "ok" : cancel.code}; status ${cancelled?.status}; ${joinedBefore} of ${crowd.length} got in first; `
`cancel ${cancel.ok ? "ok" : cancel.code}; status ${cancelled?.status}; ${joinedBefore} of ${crowd.length} got in first (${firstIn} before it began); `
+ `a join after the cancel: ${after.ok ? `"${after.result?.error ?? "joined"}"` : after.code}`);
check("meetup: and its roster and count agree",
cancelled?.participantCount === cancelledRoster.length && !cancelledRoster.some((r) => r.id === late.uid),
`participantCount ${cancelled?.participantCount}, ${cancelledRoster.length} on the roster`);
if (CLOUD) {
skip("meetup: everyone on the roster is told it is cancelled", "the test project has no onMeetupUpdated");
} else {
{
const toTell = cancelledRoster.filter((r) => r.id !== users[0].uid).map((r) => r.id);
const told = await settle(async () => new Set((await adminQuery(null, "notifications", { where: ["fromUserId", users[0].uid] }))
.filter((n) => n.type === "meetup_cancelled").map((n) => n.userId)), (set) => toTell.every((id) => set.has(id)));
Expand Down
Loading