Write the failing tests for direct sign-up to an event

This commit is contained in:
Sylvain Duchesne
2026-11-06 22:57:46 +01:00
parent f45fc564ec
commit 4abab5a55a
11 changed files with 909 additions and 11 deletions
+96 -5
View File
@@ -185,11 +185,6 @@ describe('registration — refusals write nothing and record nothing', () => {
await refused(setup({ acts: [j], stored: { [MP1]: { ...meetingPoint(MP1), type: 'Note' } } }), j, { contains: 'is not a Event' });
});
test('an Event with no as:context (no event to anchor the URN)', async () => {
const j = join(MP1);
await refused(setup({ acts: [j], stored: { [MP1]: meetingPoint(MP1, { context: undefined }) } }), j, { contains: 'as:context' });
});
test('a meeting point not in the pod of its dc:creator, or naming no creator', async () => {
const j = join(MP1);
await refused(setup({ acts: [j], stored: { [MP1]: meetingPoint(MP1, { 'dc:creator': MALLORY }) } }), j, { contains: 'not in the pod of its creator' });
@@ -847,3 +842,99 @@ describe('attendance-ledger — the order of acts', () => {
expect(() => lastAttendanceQuery(MP1, BOB)).not.toThrow();
});
});
describe('registration — a direct sign-up: a Join naming an event itself', () => {
const DAVE = ALICE.replace('/alice', '/dave');
const PLAIN = `${ALICE}/data/events/plain`;
const plainEvent = (uri, extra = {}) => meetingPoint(uri, { context: undefined, name: `Evénement ${uri.split('/').pop()}`, ...extra });
const plainWorld = (acts, options = {}) => setup({ acts, stored: { [PLAIN]: plainEvent(PLAIN) }, ...options });
const plainUrn = (who = BOB, event = PLAIN) => memberUrn({ participantUri: who, eventUri: event, secret: SECRETS[who] });
test("the member URN, computed with the event as anchor, joins the event's own list and the event joins the participant's collection", async () => {
const j = join(PLAIN);
const w = plainWorld([j]);
await emitJoin(w, j);
expect(attendees(w, PLAIN)).toEqual([plainUrn()]);
expect(attendees(w, PLAIN)).not.toContain(BOB);
const add = w.calls.find(c => c.action === 'attendees.add');
expect(add.params).toEqual({ collectionUri: attendeesOf(PLAIN), itemUri: plainUrn(), actorUri: ALICE });
expect(participations(w)).toEqual([PLAIN]);
const added = w.calls.find(c => c.action === 'pod-collections.add');
expect(added.params).toEqual({ collectionUri: participationsUri(w), itemUri: PLAIN, actorUri: BOB });
expect(ledgerOf(w, PLAIN)).toEqual({ activityUri: j.id, published: T0, change: 'join', member: plainUrn() });
expect(w.logs.error).toEqual([]);
});
test("a direct sign-up notifies nobody: the declarer of an event is not its host", async () => {
const j = join(PLAIN);
const w = plainWorld([j]);
await emitJoin(w, j);
expect(attendees(w, PLAIN)).toEqual([plainUrn()]);
expect(w.calls.some(c => c.action === 'pod-notifications.send')).toBe(false);
expect(w.state.notifications).toEqual([]);
});
test("the participant carries the SAME URN in the event's own list and in its meeting points' lists (R5, R6)", async () => {
const festival = plainEvent(EVENT1, { 'dc:creator': DAVE });
const acts = [join(EVENT1), join(MP1), join(MP2)];
const w = setup({ acts, stored: { [EVENT1]: festival } });
for (const a of acts) await emitJoin(w, a);
expect(attendees(w, EVENT1)).toEqual([urnOf(BOB, MP1)]);
expect(attendees(w, MP1)).toEqual(attendees(w, EVENT1));
expect(attendees(w, MP2)).toEqual(attendees(w, EVENT1));
expect(participations(w).sort()).toEqual([EVENT1, MP1, MP2].sort());
});
test('signing up twice leaves one entry in the list and one in the collection (R1)', async () => {
const first = join(PLAIN, T0, BOB, 1);
const again = join(PLAIN, T1, BOB, 2);
const w = plainWorld([first, again]);
await emitJoin(w, first);
await emitJoin(w, again);
expect(attendees(w, PLAIN)).toEqual([plainUrn()]);
expect(participations(w)).toEqual([PLAIN]);
expect(w.logs.error).toEqual([]);
});
test("a Leave naming the event removes the URN from its list and the event from the participant's collection (R8)", async () => {
const j = join(PLAIN);
const l = leave(PLAIN);
const w = plainWorld([j, l]);
await emitJoin(w, j);
await emitLeave(w, l);
expect(attendees(w, PLAIN)).toEqual([]);
expect(participations(w)).toEqual([]);
expect(ledgerOf(w, PLAIN).change).toBe('leave');
});
test("leaving the event leaves the participant's meeting point memberships alone, and the other way round", async () => {
const acts = [join(PLAIN), join(MP1), leave(PLAIN, T1)];
const w = setup({ acts, stored: { [PLAIN]: plainEvent(PLAIN) } });
await emitJoin(w, acts[0]);
await emitJoin(w, acts[1]);
expect(participations(w).sort()).toEqual([PLAIN, MP1].sort());
await emitLeave(w, acts[2]);
expect(attendees(w, MP1)).toEqual([urnOf(BOB, MP1)]);
expect(participations(w)).toEqual([MP1]);
});
test('a stored object that is not an Event stays refused', async () => {
const j = join(PLAIN);
const w = plainWorld([j], { stored: { [PLAIN]: { ...plainEvent(PLAIN), type: 'Note' } } });
await emitJoin(w, j);
expect(writes(w.calls)).toEqual([]);
expect(w.logs.warn.length).toBeGreaterThan(0);
});
test("an event not in the pod of its dc:creator, or naming no creator, stays refused", async () => {
const j = join(PLAIN);
for (const extra of [{ 'dc:creator': MALLORY }, { 'dc:creator': undefined }]) {
const w = plainWorld([j], { stored: { [PLAIN]: plainEvent(PLAIN, extra) } });
await emitJoin(w, j);
expect(writes(w.calls)).toEqual([]);
expect(attendees(w, PLAIN)).toEqual([]);
expect(w.logs.warn.length).toBeGreaterThan(0);
}
});
});