@webex/plugin-memberships 2.59.2 → 2.59.3-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,854 +1,854 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- import '@webex/plugin-logger';
6
- import '@webex/plugin-messages';
7
- import '@webex/plugin-memberships';
8
- import '@webex/plugin-people';
9
- import '@webex/plugin-rooms';
10
- import WebexCore from '@webex/webex-core';
11
- import {SDK_EVENT, hydraTypes, constructHydraId} from '@webex/common';
12
- import {assert} from '@webex/test-helper-chai';
13
- import sinon from 'sinon';
14
- import testUsers from '@webex/test-helper-test-users';
15
-
16
- const debug = require('debug')('memberships');
17
-
18
- describe('plugin-memberships', function () {
19
- this.timeout(60000);
20
-
21
- let webex, user, actor;
22
-
23
- before(() =>
24
- testUsers.create({count: 1}).then(([u]) => {
25
- user = u;
26
- webex = new WebexCore({credentials: user.token});
27
- webex.people.get('me').then((person) => {
28
- actor = person;
29
- debug('SDK User (Actor) for tests:');
30
- debug(`- name: ${actor.displayName}`);
31
- debug(`- id: ${actor.id}`);
32
- });
33
- })
34
- );
35
-
36
- describe('#memberships', () => {
37
- let user1;
38
-
39
- before(() =>
40
- testUsers.create({count: 1}).then((users) => {
41
- user1 = users[0];
42
- debug('User that memberships are created for:');
43
- debug(`- name: ${user1.displayName}`);
44
- debug(`- id: ${constructHydraId(hydraTypes.PEOPLE, user1.id)}`);
45
- })
46
- );
47
-
48
- let room;
49
-
50
- beforeEach(() =>
51
- webex.rooms.create({title: 'Webex Test Room'}).then((r) => {
52
- debug('Created Cisco Test Room');
53
- room = r;
54
- })
55
- );
56
-
57
- afterEach(() => {
58
- webex.memberships.stopListening();
59
- webex.memberships.off('created');
60
- webex.memberships.off('updated');
61
- webex.memberships.off('deleted');
62
- webex.memberships.off('seen');
63
- webex.rooms
64
- .remove(room)
65
- .catch((e) => webex.logger.error(`Failed to clean up after unit test: ${e}`));
66
- });
67
-
68
- describe('#create()', () => {
69
- it('creates a membership by user id', () =>
70
- webex.memberships
71
- .create({
72
- roomId: room.id,
73
- personId: user1.id,
74
- })
75
- .then((membership) => {
76
- assert.isMembership(membership);
77
- }));
78
-
79
- it('creates a membership by user email', () => {
80
- const created = new Promise((resolve) => {
81
- webex.memberships.on('created', (event) => {
82
- debug('membership:created event handler for membership by email test called');
83
- resolve(event);
84
- });
85
- });
86
-
87
- return webex.memberships.listen().then(() =>
88
- webex.memberships
89
- .create({
90
- roomId: room.id,
91
- personEmail: user1.email,
92
- })
93
- .then(async (membership) => {
94
- validateMembership(membership);
95
- const event = await created;
96
-
97
- validateMembershipEvent(event, membership, actor);
98
- })
99
- );
100
- });
101
-
102
- it('creates a membership and sets moderator status', () => {
103
- // "Creating" a member as a moderator actually generates
104
- // two events, first a created event (with non-moderator status)
105
- // followed by an updated event with isModerator set to true
106
- const created = new Promise((resolve) => {
107
- webex.memberships.on('created', (event) => {
108
- debug('membership:created event handler for moderator test called');
109
- resolve(event);
110
- });
111
- });
112
- const updated = new Promise((resolve) => {
113
- webex.memberships.on('updated', (event) => {
114
- debug('membership:updated event handler for moderator test called');
115
-
116
- if (event.data.personId === actor.id) {
117
- debug(
118
- 'Setting a member to moderator implicitly sets the ' +
119
- 'caller of the API to moderator as well. In this test we ' +
120
- 'will ignore this event'
121
- );
122
-
123
- return;
124
- }
125
- resolve(event);
126
- });
127
- });
128
-
129
- return webex.memberships.listen().then(() =>
130
- webex.memberships
131
- .create({
132
- roomId: room.id,
133
- personId: user1.id,
134
- isModerator: true,
135
- })
136
- .then(async (m) => {
137
- const membership = m;
138
-
139
- validateMembership(membership, true);
140
- const event = await created;
141
-
142
- // We expect the isModerator status to be false on create
143
- membership.isModerator = false;
144
- validateMembershipEvent(event, membership, actor);
145
-
146
- const event2 = await updated;
147
-
148
- // We expect the isModerator status to be false on create
149
- membership.isModerator = true;
150
- validateMembershipEvent(event2, membership, actor);
151
- })
152
- );
153
- });
154
-
155
- it('creates a space and memberships simultaniously', () => {
156
- // "Creating" a space with users generates a unique event
157
- // activity object shape via the client that contains
158
- // multiple "participants". There isn't a documented
159
- // way to perform this action via the API with more than
160
- // one user at the time of edit.
161
- const created = new Promise((resolve) => {
162
- webex.memberships.on('created', (event) => {
163
- debug('membership:created event handler for new space test called');
164
- resolve(event);
165
- });
166
- });
167
-
168
- return (
169
- webex.memberships
170
- .listen()
171
- // Cleanup current room, we need to catch the event when it's created.
172
- .then(() => webex.rooms.remove(room))
173
- // Create a room to trigger created event.
174
- .then(() => webex.rooms.create({title: 'Webex Test Room'}))
175
- // Store new room object and get the memberships of the room.
176
- .then((r) => {
177
- room = r;
178
-
179
- return webex.memberships.list({roomId: room.id});
180
- })
181
- // validate data
182
- .then(async ({items: [membership]}) => {
183
- const event = await created;
184
-
185
- validateMembership(membership, false);
186
- validateMembershipEvent(event, membership, actor);
187
- })
188
- );
189
- });
190
- });
191
-
192
- describe('#get()', () => {
193
- let membership;
194
-
195
- before(() =>
196
- // this could be in parallel once KMS always sends new keys
197
- webex.rooms
198
- .create({title: 'Membership A'})
199
- .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
200
- .then((rooms) => {
201
- const room = rooms[0];
202
-
203
- return webex.memberships.create({
204
- roomId: room.id,
205
- personId: user1.id,
206
- });
207
- })
208
- .then((m) => {
209
- membership = m;
210
- })
211
- );
212
-
213
- it('retrieves a single membership', () =>
214
- webex.memberships.get(membership).then((m) => {
215
- assert.deepEqual(m, membership);
216
- }));
217
- });
218
-
219
- describe('#list()', () => {
220
- let room;
221
-
222
- before(() =>
223
- // this could be in parallel once KMS always sends new keys
224
- webex.rooms
225
- .create({title: 'Membership A'})
226
- .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
227
- .then((rooms) => {
228
- room = rooms[0];
229
-
230
- return webex.memberships.create({
231
- roomId: room.id,
232
- personId: user1.id,
233
- });
234
- })
235
- );
236
-
237
- it('retrieves all memberships for a room', () =>
238
- webex.memberships.list({roomId: room.id}).then((memberships) => {
239
- assert.isDefined(memberships);
240
- assert.isAbove(memberships.length, 0);
241
- for (const membership of memberships) {
242
- assert.isMembership(membership);
243
- assert.equal(membership.roomId, room.id);
244
- }
245
- }));
246
-
247
- it('retrieves a bounded set of memberships for a room', () => {
248
- const spy = sinon.spy();
249
-
250
- return webex.memberships
251
- .list({roomId: room.id, max: 1})
252
- .then((memberships) => {
253
- assert.lengthOf(memberships, 1);
254
-
255
- return (function f(page) {
256
- for (const membership of page) {
257
- spy(membership.id);
258
- }
259
-
260
- if (page.hasNext()) {
261
- return page.next().then(f);
262
- }
263
-
264
- return Promise.resolve();
265
- })(memberships);
266
- })
267
- .then(() => {
268
- assert.calledTwice(spy);
269
- });
270
- });
271
-
272
- it('retrieves all room memberships for a user', () =>
273
- webex.memberships
274
- .list({
275
- personId: user.id,
276
- roomId: room.id,
277
- })
278
- .then((memberships) => {
279
- const membership = memberships.items[0];
280
-
281
- return webex.memberships
282
- .list({
283
- personEmail: user.email,
284
- })
285
- .then((memberships) => {
286
- assert.isDefined(memberships);
287
- assert.isAbove(memberships.length, 0);
288
- for (const membership of memberships) {
289
- assert.isMembership(membership);
290
- assert.equal(membership.personEmail, user.email);
291
- }
292
- assert.deepInclude(memberships.items, membership);
293
- });
294
- }));
295
-
296
- it('retrieves a bounded set of memberships for a user', () => {
297
- const spy = sinon.spy();
298
-
299
- return webex.memberships
300
- .list({personId: user.id, max: 1})
301
- .then((memberships) => {
302
- assert.lengthOf(memberships, 1);
303
-
304
- return (function f(page) {
305
- for (const membership of page) {
306
- assert.equal(membership.personEmail, user.email);
307
- spy(membership.id);
308
- }
309
-
310
- if (page.hasNext()) {
311
- return page.next().then(f);
312
- }
313
-
314
- return Promise.resolve();
315
- })(memberships);
316
- })
317
- .then(() => {
318
- assert.isAbove(spy.callCount, 0);
319
- });
320
- });
321
- });
322
-
323
- describe('#listWithReadStatus()', () => {
324
- let room;
325
-
326
- before(() => {
327
- // Create a new SDK instance so the 2nd user can post a message
328
- user1.webex = new WebexCore({credentials: user1.token});
329
-
330
- // Setup a space with both users
331
- return webex.rooms
332
- .create({title: 'Membership A'})
333
- .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
334
- .then((rooms) => {
335
- room = rooms[0];
336
-
337
- return webex.memberships.create({
338
- roomId: room.id,
339
- personId: user1.id,
340
- });
341
- });
342
- });
343
-
344
- it('retrieves memberships with read status for a room that has not been visited', () =>
345
- webex.memberships.listWithReadStatus({roomId: room.id}).then((memberships) => {
346
- assert.isDefined(memberships);
347
- assert.equal(memberships.items.length, 2);
348
- for (const membership of memberships.items) {
349
- if (membership.personId !== actor.id) {
350
- assert.notExists(
351
- membership.lastSeenId,
352
- 'no lastSeenId for a room that has never been visited'
353
- );
354
- assert.notExists(
355
- membership.lastSeenDate,
356
- 'no lastSeenDate for a room that has never been visited'
357
- );
358
- }
359
- assert.equal(membership.roomId, room.id);
360
- }
361
- }));
362
-
363
- it('validates read status activity after other user posts a message', () =>
364
- user1.webex.messages
365
- .create({
366
- roomId: room.id,
367
- text: 'Message to create activity for other member',
368
- })
369
- .then(() =>
370
- webex.memberships.listWithReadStatus({roomId: room.id}).then((memberships) => {
371
- assert.isDefined(memberships);
372
- assert.equal(memberships.items.length, 2);
373
- for (const membership of memberships.items) {
374
- assert.exists(
375
- membership.lastSeenId,
376
- 'lastSeenId exists in a room that has been visited'
377
- );
378
- assert.exists(
379
- membership.lastSeenDate,
380
- 'lastSeenDate exists in a room that has been visited'
381
- );
382
- assert.equal(membership.roomId, room.id);
383
- // listWithReadStatus does not include created
384
- // fudge it here so we can validate all the other fields
385
- membership.created = 'foo';
386
- assert.isMembership(membership);
387
- }
388
- })
389
- ));
390
- });
391
-
392
- describe('#update()', () => {
393
- let membership, sdkMember, room;
394
-
395
- before(() => {
396
- // Before setting another user to moderator
397
- // We will set the test user to moderator
398
- // and wait for the expected events to arive
399
- const updated = new Promise((resolve) => {
400
- webex.memberships.on('updated', (event) => {
401
- debug('membership:updated event handler for update test called');
402
- webex.memberships.stopListening(); // disable this callback after test
403
- webex.memberships.off('updated');
404
- resolve(event);
405
- });
406
- });
407
-
408
- const roomUpdated = new Promise((resolve) => {
409
- webex.rooms.on('updated', (event) => {
410
- debug('rooms:updated event handler for update test called');
411
- webex.rooms.stopListening(); // disable this callback after test
412
- webex.rooms.off('updated');
413
- resolve(event);
414
- });
415
- });
416
-
417
- return webex.memberships
418
- .listen()
419
- .then(() => webex.rooms.listen())
420
- .then(() =>
421
- webex.rooms
422
- .create({title: 'Membership E'})
423
- .then((r) => {
424
- room = r;
425
- debug(`Room under test ID: ${room.id}`);
426
-
427
- // First get the SDK users membership
428
- return webex.memberships.list({
429
- roomId: room.id,
430
- personId: actor.id,
431
- });
432
- })
433
- .then((membershipList) => {
434
- assert.isArray(
435
- membershipList.items,
436
- 'membership list not returned after room creation'
437
- );
438
- assert.equal(
439
- membershipList.items.length,
440
- 1,
441
- 'SDK Test user not a member of room just created'
442
- );
443
- sdkMember = membershipList.items[0];
444
-
445
- validateMembership(sdkMember);
446
- sdkMember.isModerator = true;
447
-
448
- // Then update the SDK user to a moderator
449
- return webex.memberships.update(sdkMember);
450
- })
451
- .then(async (m) => {
452
- debug('SDK User is now moderator. Wait for events');
453
- validateMembership(m, true);
454
- const event = await updated;
455
-
456
- validateMembershipEvent(event, m, actor);
457
- const roomUpdatedEvent = await roomUpdated;
458
-
459
- return Promise.resolve(roomUpdatedEvent);
460
- })
461
- .then((roomUpdatedEvent) => {
462
- // Check that the expected rooms:updated event matches
463
- // what we expect when a room is first moderated
464
- validateRoomsUpdatedEvent(
465
- roomUpdatedEvent,
466
- room,
467
- /* expected value of isLocked */ true
468
- );
469
-
470
- // Finally, create the user for our test
471
- return webex.memberships.create({
472
- roomId: room.id,
473
- personId: user1.id,
474
- });
475
- })
476
- .then((m) => {
477
- debug('User 1 Membership created in Membership E');
478
- membership = m;
479
- validateMembership(membership);
480
- })
481
- .catch((e) => debug(`membership failed: ${e}`))
482
- );
483
- });
484
-
485
- after(() => {
486
- // Before deleting the test space
487
- // We will unset the test user to moderator
488
- // and wait for the expected events to arive
489
- const updated = new Promise((resolve) => {
490
- webex.memberships.on('updated', (event) => {
491
- debug('membership:updated event handler for update test called');
492
- webex.memberships.stopListening(); // disable this callback after test
493
- webex.memberships.off('updated');
494
- resolve(event);
495
- });
496
- });
497
-
498
- const roomUpdated = new Promise((resolve) => {
499
- webex.rooms.on('updated', (event) => {
500
- debug('rooms:updated event handler for update test called');
501
- webex.rooms.stopListening(); // disable this callback after test
502
- webex.rooms.off('updated');
503
- resolve(event);
504
- });
505
- });
506
-
507
- sdkMember.isModerator = false;
508
-
509
- return webex.rooms
510
- .listen()
511
- .then(() => webex.memberships.listen())
512
- .then(() => webex.memberships.update(sdkMember))
513
- .then(async (m) => {
514
- debug('SDK User is no longer a moderator. Wait for events');
515
- validateMembership(m, /* expected isModerator status */ false);
516
- const event = await updated;
517
-
518
- debug('Validating memberships:updated event...');
519
- validateMembershipEvent(event, m, actor);
520
-
521
- const roomUpdatedEvent = await roomUpdated;
522
-
523
- return Promise.resolve(roomUpdatedEvent);
524
- })
525
- .then((roomUpdatedEvent) => {
526
- // Check that the expected rooms:updated event matches
527
- // what we expect when a room moves into the unmoderated state
528
- debug('Validating rooms:updated event...');
529
- debug(`roomId: ${room.id}, event.data.id: ${roomUpdatedEvent.data.id}`);
530
-
531
- return Promise.resolve(
532
- validateRoomsUpdatedEvent(roomUpdatedEvent, room, /* expected isLocked value */ false)
533
- );
534
- })
535
- .catch((e) => debug(`after logic for #update()' failed: ${e}`));
536
- });
537
-
538
- it('assigns a membership to moderator status', () => {
539
- assert.isFalse(membership.isModerator);
540
- membership.isModerator = true;
541
-
542
- const updated = new Promise((resolve) => {
543
- webex.memberships.on('updated', (event) => {
544
- debug('membership:updated event handler for assign moderator test called');
545
- resolve(event);
546
- });
547
- });
548
-
549
- return webex.memberships.listen().then(() =>
550
- webex.memberships.update(membership).then(async (m) => {
551
- debug('membership updated');
552
- assert.deepEqual(m, membership);
553
- validateMembership(membership, true);
554
- const event = await updated;
555
-
556
- validateMembershipEvent(event, membership, actor);
557
- })
558
- );
559
- });
560
-
561
- it("revokes a member's moderator status", () => {
562
- assert.isTrue(membership.isModerator);
563
- membership.isModerator = false;
564
-
565
- const updated = new Promise((resolve) => {
566
- webex.memberships.on('updated', (event) => {
567
- debug('membership:updated event handler for revoke moderator test called');
568
- resolve(event);
569
- });
570
- });
571
-
572
- return webex.memberships.listen().then(() =>
573
- webex.memberships.update(membership).then(async (m) => {
574
- assert.deepEqual(m, membership);
575
- validateMembership(membership, false);
576
- const event = await updated;
577
-
578
- validateMembershipEvent(event, membership, actor);
579
- })
580
- );
581
- });
582
- });
583
-
584
- describe('#updateLastSeen()', () => {
585
- let actor1, message;
586
-
587
- before(() => {
588
- // Get another SDK instance for the second user
589
- // so that one user can send the message
590
- // and another can mark it as read
591
- user1.webex = new WebexCore({credentials: user1.token});
592
-
593
- return user1.webex.people
594
- .get('me')
595
- .then((person) => {
596
- actor1 = person;
597
-
598
- return webex.rooms.create({title: 'Read Receipt Test'});
599
- })
600
- .then((r) => {
601
- room = r;
602
-
603
- return webex.memberships.create({
604
- roomId: room.id,
605
- personId: actor1.id,
606
- });
607
- })
608
- .then(() =>
609
- webex.messages
610
- .create({
611
- roomId: room.id,
612
- text: 'This is a test message',
613
- })
614
- .then((m) => {
615
- message = m;
616
- assert.isMessage(message);
617
- })
618
- );
619
- });
620
-
621
- it('marks a message as read and verifies membership:updated event', () => {
622
- const seenPromise = new Promise((resolve) => {
623
- webex.memberships.on('seen', (event) => {
624
- debug('membership:seen event handler for updateLastSeen test called');
625
- resolve(event);
626
- });
627
- });
628
-
629
- debug(`${user1.displayName} marked message as read...`);
630
-
631
- return webex.memberships.listen().then(() =>
632
- user1.webex.memberships.updateLastSeen(message).then(async (m) => {
633
- debug('membership seen');
634
- validateMembership(m);
635
- const event = await seenPromise;
636
-
637
- debug(`...${user.displayName} got the membership:seen event.`);
638
- assert.equal(
639
- event.data.lastSeenId,
640
- message.id,
641
- 'message:seen lastSeenID matches id of message that was acknlowledged'
642
- );
643
- validateMembershipEvent(event, m, actor1, actor);
644
- })
645
- );
646
- });
647
- });
648
-
649
- describe('#hide()', () => {
650
- let membership, roomId;
651
-
652
- // We need a one-on-on space for this test
653
- // We create it by sending a message to the test user
654
- before(() =>
655
- // Ensure that room is a one-on-one object
656
- webex.messages
657
- .create({
658
- toPersonId: user1.id,
659
- text: 'This message will create a 1-1 space',
660
- })
661
- .then((message) => {
662
- roomId = message.roomId;
663
-
664
- // Get the test users membership
665
- return webex.memberships.list({
666
- roomId,
667
- personId: user.id,
668
- });
669
- })
670
- .then((memberships) => {
671
- membership = memberships.items[0];
672
- })
673
- );
674
-
675
- it('hides a space and validates the membership is updated', () => {
676
- const updatedEventPromise = new Promise((resolve) => {
677
- webex.memberships.on('updated', (event) => {
678
- debug('membership:updated event handler called');
679
- resolve(event);
680
- });
681
- });
682
-
683
- return webex.memberships.listen().then(() => {
684
- debug(`Hiding my membership in 1-1 space with ID: ${roomId}...`);
685
- membership.isRoomHidden = true;
686
-
687
- return webex.memberships
688
- .update(membership)
689
- .then(async (m) => {
690
- debug(
691
- 'memberships.update() request returned OK. Waiting for memberships:updated event'
692
- );
693
- assert(
694
- m.isRoomHidden,
695
- 'membership returned from meberships.update() did not have isRoomHidden set to true'
696
- );
697
- const event = await updatedEventPromise;
698
-
699
- assert(
700
- event.data.isRoomHidden,
701
- 'memberships:updated event did not have isRoomHidden set to true'
702
- );
703
- validateMembershipEvent(event, m, actor);
704
- })
705
- .catch((e) => assert.fail(`Updating room to hidden failed: ${e.message}`));
706
- });
707
- });
708
- });
709
-
710
- describe('#remove()', () => {
711
- let membership;
712
-
713
- before(() =>
714
- webex.rooms
715
- .create({title: 'Membership F'})
716
- .then((r) => {
717
- room = r;
718
-
719
- return webex.memberships.create({
720
- roomId: room.id,
721
- personId: user1.id,
722
- });
723
- })
724
- .then((m) => {
725
- membership = m;
726
- })
727
- );
728
-
729
- it('deletes a single membership', () => {
730
- const deletedEventPromise = new Promise((resolve) => {
731
- webex.memberships.on('deleted', (event) => {
732
- debug('membership:deleted event handler for delete single membership test called');
733
- resolve(event);
734
- });
735
- });
736
-
737
- return webex.memberships
738
- .listen()
739
- .then(() =>
740
- webex.memberships.remove(membership).catch((reason) => {
741
- webex.logger.error('Failed to delete membership', reason);
742
- })
743
- )
744
- .then(async (body) => {
745
- debug('member deleted');
746
- assert.notOk(body);
747
-
748
- return webex.memberships.list(room);
749
- })
750
- .then(async (memberships) => {
751
- assert.notInclude(memberships, membership);
752
- const event = await deletedEventPromise;
753
-
754
- validateMembershipEvent(event, membership, actor);
755
- });
756
- });
757
- });
758
- });
759
- });
760
-
761
- /**
762
- * Validate a Membership object.
763
- * @param {Object} membership
764
- * @param {Boolean} isModerator -- expected moderator status of member
765
- * @returns {void}
766
- */
767
- function validateMembership(membership, isModerator = false) {
768
- assert.isDefined(membership);
769
- if ('isModerator' in membership) {
770
- assert.equal(membership.isModerator, isModerator, 'unexpected isModerator status');
771
- } else {
772
- // moderator status not returned for membership:seen events
773
- assert.exists(membership.lastSeenId);
774
- // fudge the moderator field so we can do the general check
775
- // eslint-disable-next-line no-param-reassign
776
- membership.isModerator = 'manually added';
777
- }
778
- assert.isMembership(membership);
779
- debug('membership validated');
780
- }
781
-
782
- /**
783
- * Validate a membership event.
784
- * @param {Object} event - membership event
785
- * @param {Object} membership -- return from the API that generate this event
786
- * @param {Object} actor - person object for user who performed action
787
- * @param {Object} creator - person object for user who called listen()
788
- * @returns {void}
789
- */
790
- function validateMembershipEvent(event, membership, actor, creator = actor) {
791
- assert.isTrue(
792
- event.resource === SDK_EVENT.EXTERNAL.RESOURCE.MEMBERSHIPS,
793
- 'not a membership event'
794
- );
795
- assert.isDefined(event.event, 'membership event type not set');
796
- assert.isDefined(event.created, 'event listener created date not set');
797
- assert.equal(event.createdBy, creator.id, 'event listener createdBy not set to our actor');
798
- assert.equal(event.orgId, actor.orgId, "event listener orgId not === to our actor's");
799
- assert.equal(event.ownedBy, 'creator', 'event listener not owned by creator');
800
- assert.equal(event.status, 'active', 'event listener status not active');
801
- assert.equal(event.actorId, actor.id, "event actorId not equal to our actor's id");
802
-
803
- // Ensure event data matches data returned from function call
804
- // Skip this until we figure out how conversations converts the internal test user UUID
805
- assert.equal(event.data.id, membership.id, 'event/membership.id not equal');
806
- assert.equal(event.data.roomId, membership.roomId, 'event/membership.roomId not equal');
807
- assert.equal(event.data.personId, membership.personId, 'event/membership.personId not equal');
808
- assert.equal(
809
- event.data.personOrgId,
810
- membership.personOrgId,
811
- 'event/membership.personId not equal'
812
- );
813
- assert.equal(
814
- event.data.personEmail,
815
- membership.personEmail,
816
- 'event/membership.personEmail not equal'
817
- );
818
- assert.equal(
819
- event.data.personDisplayName,
820
- membership.personDisplayName,
821
- 'event/membership.personDisplayName not equal'
822
- );
823
- assert.equal(event.data.roomType, membership.roomType, 'event/membership.roomType not equal');
824
- assert.equal(event.data.isHidden, membership.isHidden, 'event/membership.isHidden not equal');
825
- if (event.event !== 'seen') {
826
- // moderator status not returned on membership:seen events
827
- assert.equal(
828
- event.data.isModerator,
829
- membership.isModerator,
830
- 'event/membership.isModerator not equal'
831
- );
832
- }
833
- debug(`membership:${event.event} event validated`);
834
- }
835
-
836
- /**
837
- * Validate a rooms:updated event for moderated space change
838
- *
839
- * We check this in membership tests (instead of the rooms tests)
840
- * because a change in membership is the activity that triggers this event.
841
- * @param {Object} event - rooms event
842
- * @param {Object} room -- return from the API that created the room
843
- * @param {Boolean} isLocked - expected value for isLocked
844
- * @returns {void}
845
- */
846
- function validateRoomsUpdatedEvent(event, room, isLocked) {
847
- const {data} = event;
848
-
849
- assert(room.id === data.id, 'rooms:updated event did not have the expected roomId');
850
- assert(room.type === data.type, 'rooms:updated event did not have the expected room type');
851
- assert(data.isLocked === isLocked, 'rooms:updated did not have expected isLocked value');
852
-
853
- debug(`rooms:${event.event} event validated`);
854
- }
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import '@webex/plugin-logger';
6
+ import '@webex/plugin-messages';
7
+ import '@webex/plugin-memberships';
8
+ import '@webex/plugin-people';
9
+ import '@webex/plugin-rooms';
10
+ import WebexCore from '@webex/webex-core';
11
+ import {SDK_EVENT, hydraTypes, constructHydraId} from '@webex/common';
12
+ import {assert} from '@webex/test-helper-chai';
13
+ import sinon from 'sinon';
14
+ import testUsers from '@webex/test-helper-test-users';
15
+
16
+ const debug = require('debug')('memberships');
17
+
18
+ describe('plugin-memberships', function () {
19
+ this.timeout(60000);
20
+
21
+ let webex, user, actor;
22
+
23
+ before(() =>
24
+ testUsers.create({count: 1}).then(([u]) => {
25
+ user = u;
26
+ webex = new WebexCore({credentials: user.token});
27
+ webex.people.get('me').then((person) => {
28
+ actor = person;
29
+ debug('SDK User (Actor) for tests:');
30
+ debug(`- name: ${actor.displayName}`);
31
+ debug(`- id: ${actor.id}`);
32
+ });
33
+ })
34
+ );
35
+
36
+ describe('#memberships', () => {
37
+ let user1;
38
+
39
+ before(() =>
40
+ testUsers.create({count: 1}).then((users) => {
41
+ user1 = users[0];
42
+ debug('User that memberships are created for:');
43
+ debug(`- name: ${user1.displayName}`);
44
+ debug(`- id: ${constructHydraId(hydraTypes.PEOPLE, user1.id)}`);
45
+ })
46
+ );
47
+
48
+ let room;
49
+
50
+ beforeEach(() =>
51
+ webex.rooms.create({title: 'Webex Test Room'}).then((r) => {
52
+ debug('Created Cisco Test Room');
53
+ room = r;
54
+ })
55
+ );
56
+
57
+ afterEach(() => {
58
+ webex.memberships.stopListening();
59
+ webex.memberships.off('created');
60
+ webex.memberships.off('updated');
61
+ webex.memberships.off('deleted');
62
+ webex.memberships.off('seen');
63
+ webex.rooms
64
+ .remove(room)
65
+ .catch((e) => webex.logger.error(`Failed to clean up after unit test: ${e}`));
66
+ });
67
+
68
+ describe('#create()', () => {
69
+ it('creates a membership by user id', () =>
70
+ webex.memberships
71
+ .create({
72
+ roomId: room.id,
73
+ personId: user1.id,
74
+ })
75
+ .then((membership) => {
76
+ assert.isMembership(membership);
77
+ }));
78
+
79
+ it('creates a membership by user email', () => {
80
+ const created = new Promise((resolve) => {
81
+ webex.memberships.on('created', (event) => {
82
+ debug('membership:created event handler for membership by email test called');
83
+ resolve(event);
84
+ });
85
+ });
86
+
87
+ return webex.memberships.listen().then(() =>
88
+ webex.memberships
89
+ .create({
90
+ roomId: room.id,
91
+ personEmail: user1.email,
92
+ })
93
+ .then(async (membership) => {
94
+ validateMembership(membership);
95
+ const event = await created;
96
+
97
+ validateMembershipEvent(event, membership, actor);
98
+ })
99
+ );
100
+ });
101
+
102
+ it('creates a membership and sets moderator status', () => {
103
+ // "Creating" a member as a moderator actually generates
104
+ // two events, first a created event (with non-moderator status)
105
+ // followed by an updated event with isModerator set to true
106
+ const created = new Promise((resolve) => {
107
+ webex.memberships.on('created', (event) => {
108
+ debug('membership:created event handler for moderator test called');
109
+ resolve(event);
110
+ });
111
+ });
112
+ const updated = new Promise((resolve) => {
113
+ webex.memberships.on('updated', (event) => {
114
+ debug('membership:updated event handler for moderator test called');
115
+
116
+ if (event.data.personId === actor.id) {
117
+ debug(
118
+ 'Setting a member to moderator implicitly sets the ' +
119
+ 'caller of the API to moderator as well. In this test we ' +
120
+ 'will ignore this event'
121
+ );
122
+
123
+ return;
124
+ }
125
+ resolve(event);
126
+ });
127
+ });
128
+
129
+ return webex.memberships.listen().then(() =>
130
+ webex.memberships
131
+ .create({
132
+ roomId: room.id,
133
+ personId: user1.id,
134
+ isModerator: true,
135
+ })
136
+ .then(async (m) => {
137
+ const membership = m;
138
+
139
+ validateMembership(membership, true);
140
+ const event = await created;
141
+
142
+ // We expect the isModerator status to be false on create
143
+ membership.isModerator = false;
144
+ validateMembershipEvent(event, membership, actor);
145
+
146
+ const event2 = await updated;
147
+
148
+ // We expect the isModerator status to be false on create
149
+ membership.isModerator = true;
150
+ validateMembershipEvent(event2, membership, actor);
151
+ })
152
+ );
153
+ });
154
+
155
+ it('creates a space and memberships simultaniously', () => {
156
+ // "Creating" a space with users generates a unique event
157
+ // activity object shape via the client that contains
158
+ // multiple "participants". There isn't a documented
159
+ // way to perform this action via the API with more than
160
+ // one user at the time of edit.
161
+ const created = new Promise((resolve) => {
162
+ webex.memberships.on('created', (event) => {
163
+ debug('membership:created event handler for new space test called');
164
+ resolve(event);
165
+ });
166
+ });
167
+
168
+ return (
169
+ webex.memberships
170
+ .listen()
171
+ // Cleanup current room, we need to catch the event when it's created.
172
+ .then(() => webex.rooms.remove(room))
173
+ // Create a room to trigger created event.
174
+ .then(() => webex.rooms.create({title: 'Webex Test Room'}))
175
+ // Store new room object and get the memberships of the room.
176
+ .then((r) => {
177
+ room = r;
178
+
179
+ return webex.memberships.list({roomId: room.id});
180
+ })
181
+ // validate data
182
+ .then(async ({items: [membership]}) => {
183
+ const event = await created;
184
+
185
+ validateMembership(membership, false);
186
+ validateMembershipEvent(event, membership, actor);
187
+ })
188
+ );
189
+ });
190
+ });
191
+
192
+ describe('#get()', () => {
193
+ let membership;
194
+
195
+ before(() =>
196
+ // this could be in parallel once KMS always sends new keys
197
+ webex.rooms
198
+ .create({title: 'Membership A'})
199
+ .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
200
+ .then((rooms) => {
201
+ const room = rooms[0];
202
+
203
+ return webex.memberships.create({
204
+ roomId: room.id,
205
+ personId: user1.id,
206
+ });
207
+ })
208
+ .then((m) => {
209
+ membership = m;
210
+ })
211
+ );
212
+
213
+ it('retrieves a single membership', () =>
214
+ webex.memberships.get(membership).then((m) => {
215
+ assert.deepEqual(m, membership);
216
+ }));
217
+ });
218
+
219
+ describe('#list()', () => {
220
+ let room;
221
+
222
+ before(() =>
223
+ // this could be in parallel once KMS always sends new keys
224
+ webex.rooms
225
+ .create({title: 'Membership A'})
226
+ .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
227
+ .then((rooms) => {
228
+ room = rooms[0];
229
+
230
+ return webex.memberships.create({
231
+ roomId: room.id,
232
+ personId: user1.id,
233
+ });
234
+ })
235
+ );
236
+
237
+ it('retrieves all memberships for a room', () =>
238
+ webex.memberships.list({roomId: room.id}).then((memberships) => {
239
+ assert.isDefined(memberships);
240
+ assert.isAbove(memberships.length, 0);
241
+ for (const membership of memberships) {
242
+ assert.isMembership(membership);
243
+ assert.equal(membership.roomId, room.id);
244
+ }
245
+ }));
246
+
247
+ it('retrieves a bounded set of memberships for a room', () => {
248
+ const spy = sinon.spy();
249
+
250
+ return webex.memberships
251
+ .list({roomId: room.id, max: 1})
252
+ .then((memberships) => {
253
+ assert.lengthOf(memberships, 1);
254
+
255
+ return (function f(page) {
256
+ for (const membership of page) {
257
+ spy(membership.id);
258
+ }
259
+
260
+ if (page.hasNext()) {
261
+ return page.next().then(f);
262
+ }
263
+
264
+ return Promise.resolve();
265
+ })(memberships);
266
+ })
267
+ .then(() => {
268
+ assert.calledTwice(spy);
269
+ });
270
+ });
271
+
272
+ it('retrieves all room memberships for a user', () =>
273
+ webex.memberships
274
+ .list({
275
+ personId: user.id,
276
+ roomId: room.id,
277
+ })
278
+ .then((memberships) => {
279
+ const membership = memberships.items[0];
280
+
281
+ return webex.memberships
282
+ .list({
283
+ personEmail: user.email,
284
+ })
285
+ .then((memberships) => {
286
+ assert.isDefined(memberships);
287
+ assert.isAbove(memberships.length, 0);
288
+ for (const membership of memberships) {
289
+ assert.isMembership(membership);
290
+ assert.equal(membership.personEmail, user.email);
291
+ }
292
+ assert.deepInclude(memberships.items, membership);
293
+ });
294
+ }));
295
+
296
+ it('retrieves a bounded set of memberships for a user', () => {
297
+ const spy = sinon.spy();
298
+
299
+ return webex.memberships
300
+ .list({personId: user.id, max: 1})
301
+ .then((memberships) => {
302
+ assert.lengthOf(memberships, 1);
303
+
304
+ return (function f(page) {
305
+ for (const membership of page) {
306
+ assert.equal(membership.personEmail, user.email);
307
+ spy(membership.id);
308
+ }
309
+
310
+ if (page.hasNext()) {
311
+ return page.next().then(f);
312
+ }
313
+
314
+ return Promise.resolve();
315
+ })(memberships);
316
+ })
317
+ .then(() => {
318
+ assert.isAbove(spy.callCount, 0);
319
+ });
320
+ });
321
+ });
322
+
323
+ describe('#listWithReadStatus()', () => {
324
+ let room;
325
+
326
+ before(() => {
327
+ // Create a new SDK instance so the 2nd user can post a message
328
+ user1.webex = new WebexCore({credentials: user1.token});
329
+
330
+ // Setup a space with both users
331
+ return webex.rooms
332
+ .create({title: 'Membership A'})
333
+ .then((room) => Promise.all([room, webex.rooms.create({title: 'Membership B'})]))
334
+ .then((rooms) => {
335
+ room = rooms[0];
336
+
337
+ return webex.memberships.create({
338
+ roomId: room.id,
339
+ personId: user1.id,
340
+ });
341
+ });
342
+ });
343
+
344
+ it('retrieves memberships with read status for a room that has not been visited', () =>
345
+ webex.memberships.listWithReadStatus({roomId: room.id}).then((memberships) => {
346
+ assert.isDefined(memberships);
347
+ assert.equal(memberships.items.length, 2);
348
+ for (const membership of memberships.items) {
349
+ if (membership.personId !== actor.id) {
350
+ assert.notExists(
351
+ membership.lastSeenId,
352
+ 'no lastSeenId for a room that has never been visited'
353
+ );
354
+ assert.notExists(
355
+ membership.lastSeenDate,
356
+ 'no lastSeenDate for a room that has never been visited'
357
+ );
358
+ }
359
+ assert.equal(membership.roomId, room.id);
360
+ }
361
+ }));
362
+
363
+ it('validates read status activity after other user posts a message', () =>
364
+ user1.webex.messages
365
+ .create({
366
+ roomId: room.id,
367
+ text: 'Message to create activity for other member',
368
+ })
369
+ .then(() =>
370
+ webex.memberships.listWithReadStatus({roomId: room.id}).then((memberships) => {
371
+ assert.isDefined(memberships);
372
+ assert.equal(memberships.items.length, 2);
373
+ for (const membership of memberships.items) {
374
+ assert.exists(
375
+ membership.lastSeenId,
376
+ 'lastSeenId exists in a room that has been visited'
377
+ );
378
+ assert.exists(
379
+ membership.lastSeenDate,
380
+ 'lastSeenDate exists in a room that has been visited'
381
+ );
382
+ assert.equal(membership.roomId, room.id);
383
+ // listWithReadStatus does not include created
384
+ // fudge it here so we can validate all the other fields
385
+ membership.created = 'foo';
386
+ assert.isMembership(membership);
387
+ }
388
+ })
389
+ ));
390
+ });
391
+
392
+ describe('#update()', () => {
393
+ let membership, sdkMember, room;
394
+
395
+ before(() => {
396
+ // Before setting another user to moderator
397
+ // We will set the test user to moderator
398
+ // and wait for the expected events to arive
399
+ const updated = new Promise((resolve) => {
400
+ webex.memberships.on('updated', (event) => {
401
+ debug('membership:updated event handler for update test called');
402
+ webex.memberships.stopListening(); // disable this callback after test
403
+ webex.memberships.off('updated');
404
+ resolve(event);
405
+ });
406
+ });
407
+
408
+ const roomUpdated = new Promise((resolve) => {
409
+ webex.rooms.on('updated', (event) => {
410
+ debug('rooms:updated event handler for update test called');
411
+ webex.rooms.stopListening(); // disable this callback after test
412
+ webex.rooms.off('updated');
413
+ resolve(event);
414
+ });
415
+ });
416
+
417
+ return webex.memberships
418
+ .listen()
419
+ .then(() => webex.rooms.listen())
420
+ .then(() =>
421
+ webex.rooms
422
+ .create({title: 'Membership E'})
423
+ .then((r) => {
424
+ room = r;
425
+ debug(`Room under test ID: ${room.id}`);
426
+
427
+ // First get the SDK users membership
428
+ return webex.memberships.list({
429
+ roomId: room.id,
430
+ personId: actor.id,
431
+ });
432
+ })
433
+ .then((membershipList) => {
434
+ assert.isArray(
435
+ membershipList.items,
436
+ 'membership list not returned after room creation'
437
+ );
438
+ assert.equal(
439
+ membershipList.items.length,
440
+ 1,
441
+ 'SDK Test user not a member of room just created'
442
+ );
443
+ sdkMember = membershipList.items[0];
444
+
445
+ validateMembership(sdkMember);
446
+ sdkMember.isModerator = true;
447
+
448
+ // Then update the SDK user to a moderator
449
+ return webex.memberships.update(sdkMember);
450
+ })
451
+ .then(async (m) => {
452
+ debug('SDK User is now moderator. Wait for events');
453
+ validateMembership(m, true);
454
+ const event = await updated;
455
+
456
+ validateMembershipEvent(event, m, actor);
457
+ const roomUpdatedEvent = await roomUpdated;
458
+
459
+ return Promise.resolve(roomUpdatedEvent);
460
+ })
461
+ .then((roomUpdatedEvent) => {
462
+ // Check that the expected rooms:updated event matches
463
+ // what we expect when a room is first moderated
464
+ validateRoomsUpdatedEvent(
465
+ roomUpdatedEvent,
466
+ room,
467
+ /* expected value of isLocked */ true
468
+ );
469
+
470
+ // Finally, create the user for our test
471
+ return webex.memberships.create({
472
+ roomId: room.id,
473
+ personId: user1.id,
474
+ });
475
+ })
476
+ .then((m) => {
477
+ debug('User 1 Membership created in Membership E');
478
+ membership = m;
479
+ validateMembership(membership);
480
+ })
481
+ .catch((e) => debug(`membership failed: ${e}`))
482
+ );
483
+ });
484
+
485
+ after(() => {
486
+ // Before deleting the test space
487
+ // We will unset the test user to moderator
488
+ // and wait for the expected events to arive
489
+ const updated = new Promise((resolve) => {
490
+ webex.memberships.on('updated', (event) => {
491
+ debug('membership:updated event handler for update test called');
492
+ webex.memberships.stopListening(); // disable this callback after test
493
+ webex.memberships.off('updated');
494
+ resolve(event);
495
+ });
496
+ });
497
+
498
+ const roomUpdated = new Promise((resolve) => {
499
+ webex.rooms.on('updated', (event) => {
500
+ debug('rooms:updated event handler for update test called');
501
+ webex.rooms.stopListening(); // disable this callback after test
502
+ webex.rooms.off('updated');
503
+ resolve(event);
504
+ });
505
+ });
506
+
507
+ sdkMember.isModerator = false;
508
+
509
+ return webex.rooms
510
+ .listen()
511
+ .then(() => webex.memberships.listen())
512
+ .then(() => webex.memberships.update(sdkMember))
513
+ .then(async (m) => {
514
+ debug('SDK User is no longer a moderator. Wait for events');
515
+ validateMembership(m, /* expected isModerator status */ false);
516
+ const event = await updated;
517
+
518
+ debug('Validating memberships:updated event...');
519
+ validateMembershipEvent(event, m, actor);
520
+
521
+ const roomUpdatedEvent = await roomUpdated;
522
+
523
+ return Promise.resolve(roomUpdatedEvent);
524
+ })
525
+ .then((roomUpdatedEvent) => {
526
+ // Check that the expected rooms:updated event matches
527
+ // what we expect when a room moves into the unmoderated state
528
+ debug('Validating rooms:updated event...');
529
+ debug(`roomId: ${room.id}, event.data.id: ${roomUpdatedEvent.data.id}`);
530
+
531
+ return Promise.resolve(
532
+ validateRoomsUpdatedEvent(roomUpdatedEvent, room, /* expected isLocked value */ false)
533
+ );
534
+ })
535
+ .catch((e) => debug(`after logic for #update()' failed: ${e}`));
536
+ });
537
+
538
+ it('assigns a membership to moderator status', () => {
539
+ assert.isFalse(membership.isModerator);
540
+ membership.isModerator = true;
541
+
542
+ const updated = new Promise((resolve) => {
543
+ webex.memberships.on('updated', (event) => {
544
+ debug('membership:updated event handler for assign moderator test called');
545
+ resolve(event);
546
+ });
547
+ });
548
+
549
+ return webex.memberships.listen().then(() =>
550
+ webex.memberships.update(membership).then(async (m) => {
551
+ debug('membership updated');
552
+ assert.deepEqual(m, membership);
553
+ validateMembership(membership, true);
554
+ const event = await updated;
555
+
556
+ validateMembershipEvent(event, membership, actor);
557
+ })
558
+ );
559
+ });
560
+
561
+ it("revokes a member's moderator status", () => {
562
+ assert.isTrue(membership.isModerator);
563
+ membership.isModerator = false;
564
+
565
+ const updated = new Promise((resolve) => {
566
+ webex.memberships.on('updated', (event) => {
567
+ debug('membership:updated event handler for revoke moderator test called');
568
+ resolve(event);
569
+ });
570
+ });
571
+
572
+ return webex.memberships.listen().then(() =>
573
+ webex.memberships.update(membership).then(async (m) => {
574
+ assert.deepEqual(m, membership);
575
+ validateMembership(membership, false);
576
+ const event = await updated;
577
+
578
+ validateMembershipEvent(event, membership, actor);
579
+ })
580
+ );
581
+ });
582
+ });
583
+
584
+ describe('#updateLastSeen()', () => {
585
+ let actor1, message;
586
+
587
+ before(() => {
588
+ // Get another SDK instance for the second user
589
+ // so that one user can send the message
590
+ // and another can mark it as read
591
+ user1.webex = new WebexCore({credentials: user1.token});
592
+
593
+ return user1.webex.people
594
+ .get('me')
595
+ .then((person) => {
596
+ actor1 = person;
597
+
598
+ return webex.rooms.create({title: 'Read Receipt Test'});
599
+ })
600
+ .then((r) => {
601
+ room = r;
602
+
603
+ return webex.memberships.create({
604
+ roomId: room.id,
605
+ personId: actor1.id,
606
+ });
607
+ })
608
+ .then(() =>
609
+ webex.messages
610
+ .create({
611
+ roomId: room.id,
612
+ text: 'This is a test message',
613
+ })
614
+ .then((m) => {
615
+ message = m;
616
+ assert.isMessage(message);
617
+ })
618
+ );
619
+ });
620
+
621
+ it('marks a message as read and verifies membership:updated event', () => {
622
+ const seenPromise = new Promise((resolve) => {
623
+ webex.memberships.on('seen', (event) => {
624
+ debug('membership:seen event handler for updateLastSeen test called');
625
+ resolve(event);
626
+ });
627
+ });
628
+
629
+ debug(`${user1.displayName} marked message as read...`);
630
+
631
+ return webex.memberships.listen().then(() =>
632
+ user1.webex.memberships.updateLastSeen(message).then(async (m) => {
633
+ debug('membership seen');
634
+ validateMembership(m);
635
+ const event = await seenPromise;
636
+
637
+ debug(`...${user.displayName} got the membership:seen event.`);
638
+ assert.equal(
639
+ event.data.lastSeenId,
640
+ message.id,
641
+ 'message:seen lastSeenID matches id of message that was acknlowledged'
642
+ );
643
+ validateMembershipEvent(event, m, actor1, actor);
644
+ })
645
+ );
646
+ });
647
+ });
648
+
649
+ describe('#hide()', () => {
650
+ let membership, roomId;
651
+
652
+ // We need a one-on-on space for this test
653
+ // We create it by sending a message to the test user
654
+ before(() =>
655
+ // Ensure that room is a one-on-one object
656
+ webex.messages
657
+ .create({
658
+ toPersonId: user1.id,
659
+ text: 'This message will create a 1-1 space',
660
+ })
661
+ .then((message) => {
662
+ roomId = message.roomId;
663
+
664
+ // Get the test users membership
665
+ return webex.memberships.list({
666
+ roomId,
667
+ personId: user.id,
668
+ });
669
+ })
670
+ .then((memberships) => {
671
+ membership = memberships.items[0];
672
+ })
673
+ );
674
+
675
+ it('hides a space and validates the membership is updated', () => {
676
+ const updatedEventPromise = new Promise((resolve) => {
677
+ webex.memberships.on('updated', (event) => {
678
+ debug('membership:updated event handler called');
679
+ resolve(event);
680
+ });
681
+ });
682
+
683
+ return webex.memberships.listen().then(() => {
684
+ debug(`Hiding my membership in 1-1 space with ID: ${roomId}...`);
685
+ membership.isRoomHidden = true;
686
+
687
+ return webex.memberships
688
+ .update(membership)
689
+ .then(async (m) => {
690
+ debug(
691
+ 'memberships.update() request returned OK. Waiting for memberships:updated event'
692
+ );
693
+ assert(
694
+ m.isRoomHidden,
695
+ 'membership returned from meberships.update() did not have isRoomHidden set to true'
696
+ );
697
+ const event = await updatedEventPromise;
698
+
699
+ assert(
700
+ event.data.isRoomHidden,
701
+ 'memberships:updated event did not have isRoomHidden set to true'
702
+ );
703
+ validateMembershipEvent(event, m, actor);
704
+ })
705
+ .catch((e) => assert.fail(`Updating room to hidden failed: ${e.message}`));
706
+ });
707
+ });
708
+ });
709
+
710
+ describe('#remove()', () => {
711
+ let membership;
712
+
713
+ before(() =>
714
+ webex.rooms
715
+ .create({title: 'Membership F'})
716
+ .then((r) => {
717
+ room = r;
718
+
719
+ return webex.memberships.create({
720
+ roomId: room.id,
721
+ personId: user1.id,
722
+ });
723
+ })
724
+ .then((m) => {
725
+ membership = m;
726
+ })
727
+ );
728
+
729
+ it('deletes a single membership', () => {
730
+ const deletedEventPromise = new Promise((resolve) => {
731
+ webex.memberships.on('deleted', (event) => {
732
+ debug('membership:deleted event handler for delete single membership test called');
733
+ resolve(event);
734
+ });
735
+ });
736
+
737
+ return webex.memberships
738
+ .listen()
739
+ .then(() =>
740
+ webex.memberships.remove(membership).catch((reason) => {
741
+ webex.logger.error('Failed to delete membership', reason);
742
+ })
743
+ )
744
+ .then(async (body) => {
745
+ debug('member deleted');
746
+ assert.notOk(body);
747
+
748
+ return webex.memberships.list(room);
749
+ })
750
+ .then(async (memberships) => {
751
+ assert.notInclude(memberships, membership);
752
+ const event = await deletedEventPromise;
753
+
754
+ validateMembershipEvent(event, membership, actor);
755
+ });
756
+ });
757
+ });
758
+ });
759
+ });
760
+
761
+ /**
762
+ * Validate a Membership object.
763
+ * @param {Object} membership
764
+ * @param {Boolean} isModerator -- expected moderator status of member
765
+ * @returns {void}
766
+ */
767
+ function validateMembership(membership, isModerator = false) {
768
+ assert.isDefined(membership);
769
+ if ('isModerator' in membership) {
770
+ assert.equal(membership.isModerator, isModerator, 'unexpected isModerator status');
771
+ } else {
772
+ // moderator status not returned for membership:seen events
773
+ assert.exists(membership.lastSeenId);
774
+ // fudge the moderator field so we can do the general check
775
+ // eslint-disable-next-line no-param-reassign
776
+ membership.isModerator = 'manually added';
777
+ }
778
+ assert.isMembership(membership);
779
+ debug('membership validated');
780
+ }
781
+
782
+ /**
783
+ * Validate a membership event.
784
+ * @param {Object} event - membership event
785
+ * @param {Object} membership -- return from the API that generate this event
786
+ * @param {Object} actor - person object for user who performed action
787
+ * @param {Object} creator - person object for user who called listen()
788
+ * @returns {void}
789
+ */
790
+ function validateMembershipEvent(event, membership, actor, creator = actor) {
791
+ assert.isTrue(
792
+ event.resource === SDK_EVENT.EXTERNAL.RESOURCE.MEMBERSHIPS,
793
+ 'not a membership event'
794
+ );
795
+ assert.isDefined(event.event, 'membership event type not set');
796
+ assert.isDefined(event.created, 'event listener created date not set');
797
+ assert.equal(event.createdBy, creator.id, 'event listener createdBy not set to our actor');
798
+ assert.equal(event.orgId, actor.orgId, "event listener orgId not === to our actor's");
799
+ assert.equal(event.ownedBy, 'creator', 'event listener not owned by creator');
800
+ assert.equal(event.status, 'active', 'event listener status not active');
801
+ assert.equal(event.actorId, actor.id, "event actorId not equal to our actor's id");
802
+
803
+ // Ensure event data matches data returned from function call
804
+ // Skip this until we figure out how conversations converts the internal test user UUID
805
+ assert.equal(event.data.id, membership.id, 'event/membership.id not equal');
806
+ assert.equal(event.data.roomId, membership.roomId, 'event/membership.roomId not equal');
807
+ assert.equal(event.data.personId, membership.personId, 'event/membership.personId not equal');
808
+ assert.equal(
809
+ event.data.personOrgId,
810
+ membership.personOrgId,
811
+ 'event/membership.personId not equal'
812
+ );
813
+ assert.equal(
814
+ event.data.personEmail,
815
+ membership.personEmail,
816
+ 'event/membership.personEmail not equal'
817
+ );
818
+ assert.equal(
819
+ event.data.personDisplayName,
820
+ membership.personDisplayName,
821
+ 'event/membership.personDisplayName not equal'
822
+ );
823
+ assert.equal(event.data.roomType, membership.roomType, 'event/membership.roomType not equal');
824
+ assert.equal(event.data.isHidden, membership.isHidden, 'event/membership.isHidden not equal');
825
+ if (event.event !== 'seen') {
826
+ // moderator status not returned on membership:seen events
827
+ assert.equal(
828
+ event.data.isModerator,
829
+ membership.isModerator,
830
+ 'event/membership.isModerator not equal'
831
+ );
832
+ }
833
+ debug(`membership:${event.event} event validated`);
834
+ }
835
+
836
+ /**
837
+ * Validate a rooms:updated event for moderated space change
838
+ *
839
+ * We check this in membership tests (instead of the rooms tests)
840
+ * because a change in membership is the activity that triggers this event.
841
+ * @param {Object} event - rooms event
842
+ * @param {Object} room -- return from the API that created the room
843
+ * @param {Boolean} isLocked - expected value for isLocked
844
+ * @returns {void}
845
+ */
846
+ function validateRoomsUpdatedEvent(event, room, isLocked) {
847
+ const {data} = event;
848
+
849
+ assert(room.id === data.id, 'rooms:updated event did not have the expected roomId');
850
+ assert(room.type === data.type, 'rooms:updated event did not have the expected room type');
851
+ assert(data.isLocked === isLocked, 'rooms:updated did not have expected isLocked value');
852
+
853
+ debug(`rooms:${event.event} event validated`);
854
+ }