1use crate::{
2 rpc::RECONNECT_TIMEOUT,
3 tests::{room_participants, RoomParticipants, TestServer},
4};
5use call::ActiveCall;
6use client::{Channel, ChannelMembership, User};
7use gpui::{executor::Deterministic, TestAppContext};
8use rpc::{proto, RECEIVE_TIMEOUT};
9use std::sync::Arc;
10
11#[gpui::test]
12async fn test_core_channels(
13 deterministic: Arc<Deterministic>,
14 cx_a: &mut TestAppContext,
15 cx_b: &mut TestAppContext,
16) {
17 deterministic.forbid_parking();
18 let mut server = TestServer::start(&deterministic).await;
19 let client_a = server.create_client(cx_a, "user_a").await;
20 let client_b = server.create_client(cx_b, "user_b").await;
21
22 let channel_a_id = client_a
23 .channel_store()
24 .update(cx_a, |channel_store, cx| {
25 channel_store.create_channel("channel-a", None, cx)
26 })
27 .await
28 .unwrap();
29 let channel_b_id = client_a
30 .channel_store()
31 .update(cx_a, |channel_store, cx| {
32 channel_store.create_channel("channel-b", Some(channel_a_id), cx)
33 })
34 .await
35 .unwrap();
36
37 deterministic.run_until_parked();
38 client_a.channel_store().read_with(cx_a, |channels, _| {
39 assert_eq!(
40 channels.channels(),
41 &[
42 Arc::new(Channel {
43 id: channel_a_id,
44 name: "channel-a".to_string(),
45 parent_id: None,
46 depth: 0,
47 }),
48 Arc::new(Channel {
49 id: channel_b_id,
50 name: "channel-b".to_string(),
51 parent_id: Some(channel_a_id),
52 depth: 1,
53 })
54 ]
55 );
56 assert!(channels.is_user_admin(channel_a_id));
57 assert!(channels.is_user_admin(channel_b_id));
58 });
59
60 client_b
61 .channel_store()
62 .read_with(cx_b, |channels, _| assert_eq!(channels.channels(), &[]));
63
64 // Invite client B to channel A as client A.
65 client_a
66 .channel_store()
67 .update(cx_a, |store, cx| {
68 assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
69
70 let invite = store.invite_member(channel_a_id, client_b.user_id().unwrap(), false, cx);
71
72 // Make sure we're synchronously storing the pending invite
73 assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
74 invite
75 })
76 .await
77 .unwrap();
78
79 // Client A sees that B has been invited.
80 deterministic.run_until_parked();
81 client_b.channel_store().read_with(cx_b, |channels, _| {
82 assert_eq!(
83 channels.channel_invitations(),
84 &[Arc::new(Channel {
85 id: channel_a_id,
86 name: "channel-a".to_string(),
87 parent_id: None,
88 depth: 0,
89 })]
90 )
91 });
92
93 let members = client_a
94 .channel_store()
95 .update(cx_a, |store, cx| {
96 assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
97 store.get_channel_member_details(channel_a_id, cx)
98 })
99 .await
100 .unwrap();
101 assert_members_eq(
102 &members,
103 &[
104 (
105 client_a.user_id().unwrap(),
106 true,
107 proto::channel_member::Kind::Member,
108 ),
109 (
110 client_b.user_id().unwrap(),
111 false,
112 proto::channel_member::Kind::Invitee,
113 ),
114 ],
115 );
116
117 // Client B accepts the invitation.
118 client_b
119 .channel_store()
120 .update(cx_b, |channels, _| {
121 channels.respond_to_channel_invite(channel_a_id, true)
122 })
123 .await
124 .unwrap();
125 deterministic.run_until_parked();
126
127 // Client B now sees that they are a member of channel A and its existing subchannels.
128 client_b.channel_store().read_with(cx_b, |channels, _| {
129 assert_eq!(channels.channel_invitations(), &[]);
130 assert_eq!(
131 channels.channels(),
132 &[
133 Arc::new(Channel {
134 id: channel_a_id,
135 name: "channel-a".to_string(),
136 parent_id: None,
137 depth: 0,
138 }),
139 Arc::new(Channel {
140 id: channel_b_id,
141 name: "channel-b".to_string(),
142 parent_id: Some(channel_a_id),
143 depth: 1,
144 })
145 ]
146 );
147 assert!(!channels.is_user_admin(channel_a_id));
148 assert!(!channels.is_user_admin(channel_b_id));
149 });
150
151 let channel_c_id = client_a
152 .channel_store()
153 .update(cx_a, |channel_store, cx| {
154 channel_store.create_channel("channel-c", Some(channel_b_id), cx)
155 })
156 .await
157 .unwrap();
158
159 deterministic.run_until_parked();
160 client_b.channel_store().read_with(cx_b, |channels, _| {
161 assert_eq!(
162 channels.channels(),
163 &[
164 Arc::new(Channel {
165 id: channel_a_id,
166 name: "channel-a".to_string(),
167 parent_id: None,
168 depth: 0,
169 }),
170 Arc::new(Channel {
171 id: channel_b_id,
172 name: "channel-b".to_string(),
173 parent_id: Some(channel_a_id),
174 depth: 1,
175 }),
176 Arc::new(Channel {
177 id: channel_c_id,
178 name: "channel-c".to_string(),
179 parent_id: Some(channel_b_id),
180 depth: 2,
181 }),
182 ]
183 )
184 });
185
186 // Update client B's membership to channel A to be an admin.
187 client_a
188 .channel_store()
189 .update(cx_a, |store, cx| {
190 store.set_member_admin(channel_a_id, client_b.user_id().unwrap(), true, cx)
191 })
192 .await
193 .unwrap();
194 deterministic.run_until_parked();
195
196 // Observe that client B is now an admin of channel A, and that
197 // their admin priveleges extend to subchannels of channel A.
198 client_b.channel_store().read_with(cx_b, |channels, _| {
199 assert_eq!(channels.channel_invitations(), &[]);
200 assert_eq!(
201 channels.channels(),
202 &[
203 Arc::new(Channel {
204 id: channel_a_id,
205 name: "channel-a".to_string(),
206 parent_id: None,
207 depth: 0,
208 }),
209 Arc::new(Channel {
210 id: channel_b_id,
211 name: "channel-b".to_string(),
212 parent_id: Some(channel_a_id),
213 depth: 1,
214 }),
215 Arc::new(Channel {
216 id: channel_c_id,
217 name: "channel-c".to_string(),
218 parent_id: Some(channel_b_id),
219 depth: 2,
220 }),
221 ]
222 );
223
224 assert!(channels.is_user_admin(channel_c_id))
225 });
226
227 // Client A deletes the channel, deletion also deletes subchannels.
228 client_a
229 .channel_store()
230 .update(cx_a, |channel_store, _| {
231 channel_store.remove_channel(channel_b_id)
232 })
233 .await
234 .unwrap();
235
236 deterministic.run_until_parked();
237 client_a.channel_store().read_with(cx_a, |channels, _| {
238 assert_eq!(
239 channels.channels(),
240 &[Arc::new(Channel {
241 id: channel_a_id,
242 name: "channel-a".to_string(),
243 parent_id: None,
244
245 depth: 0,
246 })]
247 )
248 });
249 client_b.channel_store().read_with(cx_b, |channels, _| {
250 assert_eq!(
251 channels.channels(),
252 &[Arc::new(Channel {
253 id: channel_a_id,
254 name: "channel-a".to_string(),
255 parent_id: None,
256
257 depth: 0,
258 })]
259 )
260 });
261
262 // Remove client B
263 client_a
264 .channel_store()
265 .update(cx_a, |channel_store, cx| {
266 channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
267 })
268 .await
269 .unwrap();
270
271 deterministic.run_until_parked();
272
273 // Client A still has their channel
274 client_a.channel_store().read_with(cx_a, |channels, _| {
275 assert_eq!(
276 channels.channels(),
277 &[Arc::new(Channel {
278 id: channel_a_id,
279 name: "channel-a".to_string(),
280 parent_id: None,
281 depth: 0,
282 })]
283 )
284 });
285
286 // Client B is gone
287 client_b
288 .channel_store()
289 .read_with(cx_b, |channels, _| assert_eq!(channels.channels(), &[]));
290
291 // When disconnected, client A sees no channels.
292 server.forbid_connections();
293 server.disconnect_client(client_a.peer_id().unwrap());
294 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
295 client_a.channel_store().read_with(cx_a, |channels, _| {
296 assert_eq!(channels.channels(), &[]);
297 assert!(!channels.is_user_admin(channel_a_id));
298 });
299
300 server.allow_connections();
301 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
302 client_a.channel_store().read_with(cx_a, |channels, _| {
303 assert_eq!(
304 channels.channels(),
305 &[Arc::new(Channel {
306 id: channel_a_id,
307 name: "channel-a".to_string(),
308 parent_id: None,
309 depth: 0,
310 })]
311 );
312 assert!(channels.is_user_admin(channel_a_id));
313 });
314}
315
316fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
317 assert_eq!(
318 participants.iter().map(|p| p.id).collect::<Vec<_>>(),
319 expected_partitipants
320 );
321}
322
323fn assert_members_eq(
324 members: &[ChannelMembership],
325 expected_members: &[(u64, bool, proto::channel_member::Kind)],
326) {
327 assert_eq!(
328 members
329 .iter()
330 .map(|member| (member.user.id, member.admin, member.kind))
331 .collect::<Vec<_>>(),
332 expected_members
333 );
334}
335
336#[gpui::test]
337async fn test_joining_channel_ancestor_member(
338 deterministic: Arc<Deterministic>,
339 cx_a: &mut TestAppContext,
340 cx_b: &mut TestAppContext,
341) {
342 deterministic.forbid_parking();
343 let mut server = TestServer::start(&deterministic).await;
344
345 let client_a = server.create_client(cx_a, "user_a").await;
346 let client_b = server.create_client(cx_b, "user_b").await;
347
348 let parent_id = server
349 .make_channel("parent", (&client_a, cx_a), &mut [(&client_b, cx_b)])
350 .await;
351
352 let sub_id = client_a
353 .channel_store()
354 .update(cx_a, |channel_store, cx| {
355 channel_store.create_channel("sub_channel", Some(parent_id), cx)
356 })
357 .await
358 .unwrap();
359
360 let active_call_b = cx_b.read(ActiveCall::global);
361
362 assert!(active_call_b
363 .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
364 .await
365 .is_ok());
366}
367
368#[gpui::test]
369async fn test_channel_room(
370 deterministic: Arc<Deterministic>,
371 cx_a: &mut TestAppContext,
372 cx_b: &mut TestAppContext,
373 cx_c: &mut TestAppContext,
374) {
375 deterministic.forbid_parking();
376 let mut server = TestServer::start(&deterministic).await;
377 let client_a = server.create_client(cx_a, "user_a").await;
378 let client_b = server.create_client(cx_b, "user_b").await;
379 let client_c = server.create_client(cx_c, "user_c").await;
380
381 let zed_id = server
382 .make_channel(
383 "zed",
384 (&client_a, cx_a),
385 &mut [(&client_b, cx_b), (&client_c, cx_c)],
386 )
387 .await;
388
389 let active_call_a = cx_a.read(ActiveCall::global);
390 let active_call_b = cx_b.read(ActiveCall::global);
391
392 active_call_a
393 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
394 .await
395 .unwrap();
396
397 // Give everyone a chance to observe user A joining
398 deterministic.run_until_parked();
399
400 client_a.channel_store().read_with(cx_a, |channels, _| {
401 assert_participants_eq(
402 channels.channel_participants(zed_id),
403 &[client_a.user_id().unwrap()],
404 );
405 });
406
407 client_b.channel_store().read_with(cx_b, |channels, _| {
408 assert_participants_eq(
409 channels.channel_participants(zed_id),
410 &[client_a.user_id().unwrap()],
411 );
412 assert_eq!(
413 channels.channels(),
414 &[Arc::new(Channel {
415 id: zed_id,
416 name: "zed".to_string(),
417 parent_id: None,
418 depth: 0,
419 })]
420 )
421 });
422
423 client_c.channel_store().read_with(cx_c, |channels, _| {
424 assert_participants_eq(
425 channels.channel_participants(zed_id),
426 &[client_a.user_id().unwrap()],
427 );
428 });
429
430 active_call_b
431 .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
432 .await
433 .unwrap();
434
435 deterministic.run_until_parked();
436
437 client_a.channel_store().read_with(cx_a, |channels, _| {
438 assert_participants_eq(
439 channels.channel_participants(zed_id),
440 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
441 );
442 });
443
444 client_b.channel_store().read_with(cx_b, |channels, _| {
445 assert_participants_eq(
446 channels.channel_participants(zed_id),
447 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
448 );
449 });
450
451 client_c.channel_store().read_with(cx_c, |channels, _| {
452 assert_participants_eq(
453 channels.channel_participants(zed_id),
454 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
455 );
456 });
457
458 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
459 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
460 assert_eq!(
461 room_participants(&room_a, cx_a),
462 RoomParticipants {
463 remote: vec!["user_b".to_string()],
464 pending: vec![]
465 }
466 );
467
468 let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
469 room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
470 assert_eq!(
471 room_participants(&room_b, cx_b),
472 RoomParticipants {
473 remote: vec!["user_a".to_string()],
474 pending: vec![]
475 }
476 );
477
478 // Make sure that leaving and rejoining works
479
480 active_call_a
481 .update(cx_a, |active_call, cx| active_call.hang_up(cx))
482 .await
483 .unwrap();
484
485 deterministic.run_until_parked();
486
487 client_a.channel_store().read_with(cx_a, |channels, _| {
488 assert_participants_eq(
489 channels.channel_participants(zed_id),
490 &[client_b.user_id().unwrap()],
491 );
492 });
493
494 client_b.channel_store().read_with(cx_b, |channels, _| {
495 assert_participants_eq(
496 channels.channel_participants(zed_id),
497 &[client_b.user_id().unwrap()],
498 );
499 });
500
501 client_c.channel_store().read_with(cx_c, |channels, _| {
502 assert_participants_eq(
503 channels.channel_participants(zed_id),
504 &[client_b.user_id().unwrap()],
505 );
506 });
507
508 active_call_b
509 .update(cx_b, |active_call, cx| active_call.hang_up(cx))
510 .await
511 .unwrap();
512
513 deterministic.run_until_parked();
514
515 client_a.channel_store().read_with(cx_a, |channels, _| {
516 assert_participants_eq(channels.channel_participants(zed_id), &[]);
517 });
518
519 client_b.channel_store().read_with(cx_b, |channels, _| {
520 assert_participants_eq(channels.channel_participants(zed_id), &[]);
521 });
522
523 client_c.channel_store().read_with(cx_c, |channels, _| {
524 assert_participants_eq(channels.channel_participants(zed_id), &[]);
525 });
526
527 active_call_a
528 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
529 .await
530 .unwrap();
531
532 active_call_b
533 .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
534 .await
535 .unwrap();
536
537 deterministic.run_until_parked();
538
539 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
540 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
541 assert_eq!(
542 room_participants(&room_a, cx_a),
543 RoomParticipants {
544 remote: vec!["user_b".to_string()],
545 pending: vec![]
546 }
547 );
548
549 let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
550 room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
551 assert_eq!(
552 room_participants(&room_b, cx_b),
553 RoomParticipants {
554 remote: vec!["user_a".to_string()],
555 pending: vec![]
556 }
557 );
558}
559
560#[gpui::test]
561async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
562 deterministic.forbid_parking();
563 let mut server = TestServer::start(&deterministic).await;
564 let client_a = server.create_client(cx_a, "user_a").await;
565
566 let zed_id = server.make_channel("zed", (&client_a, cx_a), &mut []).await;
567 let rust_id = server
568 .make_channel("rust", (&client_a, cx_a), &mut [])
569 .await;
570
571 let active_call_a = cx_a.read(ActiveCall::global);
572
573 active_call_a
574 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
575 .await
576 .unwrap();
577
578 // Give everything a chance to observe user A joining
579 deterministic.run_until_parked();
580
581 client_a.channel_store().read_with(cx_a, |channels, _| {
582 assert_participants_eq(
583 channels.channel_participants(zed_id),
584 &[client_a.user_id().unwrap()],
585 );
586 assert_participants_eq(channels.channel_participants(rust_id), &[]);
587 });
588
589 active_call_a
590 .update(cx_a, |active_call, cx| {
591 active_call.join_channel(rust_id, cx)
592 })
593 .await
594 .unwrap();
595
596 deterministic.run_until_parked();
597
598 client_a.channel_store().read_with(cx_a, |channels, _| {
599 assert_participants_eq(channels.channel_participants(zed_id), &[]);
600 assert_participants_eq(
601 channels.channel_participants(rust_id),
602 &[client_a.user_id().unwrap()],
603 );
604 });
605}
606
607#[gpui::test]
608async fn test_permissions_update_while_invited(
609 deterministic: Arc<Deterministic>,
610 cx_a: &mut TestAppContext,
611 cx_b: &mut TestAppContext,
612) {
613 deterministic.forbid_parking();
614 let mut server = TestServer::start(&deterministic).await;
615 let client_a = server.create_client(cx_a, "user_a").await;
616 let client_b = server.create_client(cx_b, "user_b").await;
617
618 let rust_id = server
619 .make_channel("rust", (&client_a, cx_a), &mut [])
620 .await;
621
622 client_a
623 .channel_store()
624 .update(cx_a, |channel_store, cx| {
625 channel_store.invite_member(rust_id, client_b.user_id().unwrap(), false, cx)
626 })
627 .await
628 .unwrap();
629
630 deterministic.run_until_parked();
631
632 client_b.channel_store().read_with(cx_b, |channels, _| {
633 assert_eq!(
634 channels.channel_invitations(),
635 &[Arc::new(Channel {
636 id: rust_id,
637 name: "rust".to_string(),
638 parent_id: None,
639
640 depth: 0,
641 })],
642 );
643
644 assert_eq!(channels.channels(), &[],);
645 });
646
647 // Update B's invite before they've accepted it
648 client_a
649 .channel_store()
650 .update(cx_a, |channel_store, cx| {
651 channel_store.set_member_admin(rust_id, client_b.user_id().unwrap(), true, cx)
652 })
653 .await
654 .unwrap();
655
656 deterministic.run_until_parked();
657
658 client_b.channel_store().read_with(cx_b, |channels, _| {
659 assert_eq!(
660 channels.channel_invitations(),
661 &[Arc::new(Channel {
662 id: rust_id,
663 name: "rust".to_string(),
664 parent_id: None,
665
666 depth: 0,
667 })],
668 );
669
670 assert_eq!(channels.channels(), &[],);
671 });
672}
673
674#[gpui::test]
675async fn test_channel_rename(
676 deterministic: Arc<Deterministic>,
677 cx_a: &mut TestAppContext,
678 cx_b: &mut TestAppContext,
679) {
680 deterministic.forbid_parking();
681 let mut server = TestServer::start(&deterministic).await;
682 let client_a = server.create_client(cx_a, "user_a").await;
683 let client_b = server.create_client(cx_b, "user_b").await;
684
685 let rust_id = server
686 .make_channel("rust", (&client_a, cx_a), &mut [(&client_b, cx_b)])
687 .await;
688
689 // Rename the channel
690 client_a
691 .channel_store()
692 .update(cx_a, |channel_store, cx| {
693 channel_store.rename(rust_id, "#rust-archive", cx)
694 })
695 .await
696 .unwrap();
697
698 let rust_archive_id = rust_id;
699 deterministic.run_until_parked();
700
701 // Client A sees the channel with its new name.
702 client_a.channel_store().read_with(cx_a, |channels, _| {
703 assert_eq!(
704 channels.channels(),
705 &[Arc::new(Channel {
706 id: rust_archive_id,
707 name: "rust-archive".to_string(),
708 parent_id: None,
709
710 depth: 0,
711 })],
712 );
713 });
714
715 // Client B sees the channel with its new name.
716 client_b.channel_store().read_with(cx_b, |channels, _| {
717 assert_eq!(
718 channels.channels(),
719 &[Arc::new(Channel {
720 id: rust_archive_id,
721 name: "rust-archive".to_string(),
722 parent_id: None,
723
724 depth: 0,
725 })],
726 );
727 });
728}