1use crate::{
2 rpc::RECONNECT_TIMEOUT,
3 tests::{room_participants, RoomParticipants, TestServer},
4};
5use call::ActiveCall;
6use channel::{ChannelId, ChannelMembership, ChannelStore};
7use client::User;
8use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
9use rpc::{
10 proto::{self, ChannelRole},
11 RECEIVE_TIMEOUT,
12};
13use std::sync::Arc;
14
15#[gpui::test]
16async fn test_core_channels(
17 deterministic: Arc<Deterministic>,
18 cx_a: &mut TestAppContext,
19 cx_b: &mut TestAppContext,
20) {
21 deterministic.forbid_parking();
22 let mut server = TestServer::start(&deterministic).await;
23 let client_a = server.create_client(cx_a, "user_a").await;
24 let client_b = server.create_client(cx_b, "user_b").await;
25
26 let channel_a_id = client_a
27 .channel_store()
28 .update(cx_a, |channel_store, cx| {
29 channel_store.create_channel("channel-a", None, cx)
30 })
31 .await
32 .unwrap();
33 let channel_b_id = client_a
34 .channel_store()
35 .update(cx_a, |channel_store, cx| {
36 channel_store.create_channel("channel-b", Some(channel_a_id), cx)
37 })
38 .await
39 .unwrap();
40
41 deterministic.run_until_parked();
42 assert_channels(
43 client_a.channel_store(),
44 cx_a,
45 &[
46 ExpectedChannel {
47 id: channel_a_id,
48 name: "channel-a".to_string(),
49 depth: 0,
50 user_is_admin: true,
51 },
52 ExpectedChannel {
53 id: channel_b_id,
54 name: "channel-b".to_string(),
55 depth: 1,
56 user_is_admin: true,
57 },
58 ],
59 );
60
61 client_b.channel_store().read_with(cx_b, |channels, _| {
62 assert!(channels
63 .channel_dag_entries()
64 .collect::<Vec<_>>()
65 .is_empty())
66 });
67
68 // Invite client B to channel A as client A.
69 client_a
70 .channel_store()
71 .update(cx_a, |store, cx| {
72 assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
73
74 let invite = store.invite_member(
75 channel_a_id,
76 client_b.user_id().unwrap(),
77 proto::ChannelRole::Member,
78 cx,
79 );
80
81 // Make sure we're synchronously storing the pending invite
82 assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
83 invite
84 })
85 .await
86 .unwrap();
87
88 // Client A sees that B has been invited.
89 deterministic.run_until_parked();
90 assert_channel_invitations(
91 client_b.channel_store(),
92 cx_b,
93 &[ExpectedChannel {
94 id: channel_a_id,
95 name: "channel-a".to_string(),
96 depth: 0,
97 user_is_admin: false,
98 }],
99 );
100
101 let members = client_a
102 .channel_store()
103 .update(cx_a, |store, cx| {
104 assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
105 store.get_channel_member_details(channel_a_id, cx)
106 })
107 .await
108 .unwrap();
109 assert_members_eq(
110 &members,
111 &[
112 (
113 client_a.user_id().unwrap(),
114 proto::ChannelRole::Admin,
115 proto::channel_member::Kind::Member,
116 ),
117 (
118 client_b.user_id().unwrap(),
119 proto::ChannelRole::Member,
120 proto::channel_member::Kind::Invitee,
121 ),
122 ],
123 );
124
125 // Client B accepts the invitation.
126 client_b
127 .channel_store()
128 .update(cx_b, |channels, _| {
129 channels.respond_to_channel_invite(channel_a_id, true)
130 })
131 .await
132 .unwrap();
133 deterministic.run_until_parked();
134
135 // Client B now sees that they are a member of channel A and its existing subchannels.
136 assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
137 assert_channels(
138 client_b.channel_store(),
139 cx_b,
140 &[
141 ExpectedChannel {
142 id: channel_a_id,
143 name: "channel-a".to_string(),
144 user_is_admin: false,
145 depth: 0,
146 },
147 ExpectedChannel {
148 id: channel_b_id,
149 name: "channel-b".to_string(),
150 user_is_admin: false,
151 depth: 1,
152 },
153 ],
154 );
155
156 let channel_c_id = client_a
157 .channel_store()
158 .update(cx_a, |channel_store, cx| {
159 channel_store.create_channel("channel-c", Some(channel_b_id), cx)
160 })
161 .await
162 .unwrap();
163
164 deterministic.run_until_parked();
165 assert_channels(
166 client_b.channel_store(),
167 cx_b,
168 &[
169 ExpectedChannel {
170 id: channel_a_id,
171 name: "channel-a".to_string(),
172 user_is_admin: false,
173 depth: 0,
174 },
175 ExpectedChannel {
176 id: channel_b_id,
177 name: "channel-b".to_string(),
178 user_is_admin: false,
179 depth: 1,
180 },
181 ExpectedChannel {
182 id: channel_c_id,
183 name: "channel-c".to_string(),
184 user_is_admin: false,
185 depth: 2,
186 },
187 ],
188 );
189
190 // Update client B's membership to channel A to be an admin.
191 client_a
192 .channel_store()
193 .update(cx_a, |store, cx| {
194 store.set_member_role(
195 channel_a_id,
196 client_b.user_id().unwrap(),
197 proto::ChannelRole::Admin,
198 cx,
199 )
200 })
201 .await
202 .unwrap();
203 deterministic.run_until_parked();
204
205 // Observe that client B is now an admin of channel A, and that
206 // their admin priveleges extend to subchannels of channel A.
207 assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
208 assert_channels(
209 client_b.channel_store(),
210 cx_b,
211 &[
212 ExpectedChannel {
213 id: channel_a_id,
214 name: "channel-a".to_string(),
215 depth: 0,
216 user_is_admin: true,
217 },
218 ExpectedChannel {
219 id: channel_b_id,
220 name: "channel-b".to_string(),
221 depth: 1,
222 user_is_admin: true,
223 },
224 ExpectedChannel {
225 id: channel_c_id,
226 name: "channel-c".to_string(),
227 depth: 2,
228 user_is_admin: true,
229 },
230 ],
231 );
232
233 // Client A deletes the channel, deletion also deletes subchannels.
234 client_a
235 .channel_store()
236 .update(cx_a, |channel_store, _| {
237 channel_store.remove_channel(channel_b_id)
238 })
239 .await
240 .unwrap();
241
242 deterministic.run_until_parked();
243 assert_channels(
244 client_a.channel_store(),
245 cx_a,
246 &[ExpectedChannel {
247 id: channel_a_id,
248 name: "channel-a".to_string(),
249 depth: 0,
250 user_is_admin: true,
251 }],
252 );
253 assert_channels(
254 client_b.channel_store(),
255 cx_b,
256 &[ExpectedChannel {
257 id: channel_a_id,
258 name: "channel-a".to_string(),
259 depth: 0,
260 user_is_admin: true,
261 }],
262 );
263
264 // Remove client B
265 client_a
266 .channel_store()
267 .update(cx_a, |channel_store, cx| {
268 channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
269 })
270 .await
271 .unwrap();
272
273 deterministic.run_until_parked();
274
275 // Client A still has their channel
276 assert_channels(
277 client_a.channel_store(),
278 cx_a,
279 &[ExpectedChannel {
280 id: channel_a_id,
281 name: "channel-a".to_string(),
282 depth: 0,
283 user_is_admin: true,
284 }],
285 );
286
287 // Client B no longer has access to the channel
288 assert_channels(client_b.channel_store(), cx_b, &[]);
289
290 // When disconnected, client A sees no channels.
291 server.forbid_connections();
292 server.disconnect_client(client_a.peer_id().unwrap());
293 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
294 assert_channels(client_a.channel_store(), cx_a, &[]);
295
296 server.allow_connections();
297 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
298 assert_channels(
299 client_a.channel_store(),
300 cx_a,
301 &[ExpectedChannel {
302 id: channel_a_id,
303 name: "channel-a".to_string(),
304 depth: 0,
305 user_is_admin: true,
306 }],
307 );
308}
309
310#[track_caller]
311fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
312 assert_eq!(
313 participants.iter().map(|p| p.id).collect::<Vec<_>>(),
314 expected_partitipants
315 );
316}
317
318#[track_caller]
319fn assert_members_eq(
320 members: &[ChannelMembership],
321 expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],
322) {
323 assert_eq!(
324 members
325 .iter()
326 .map(|member| (member.user.id, member.role, member.kind))
327 .collect::<Vec<_>>(),
328 expected_members
329 );
330}
331
332#[gpui::test]
333async fn test_joining_channel_ancestor_member(
334 deterministic: Arc<Deterministic>,
335 cx_a: &mut TestAppContext,
336 cx_b: &mut TestAppContext,
337) {
338 deterministic.forbid_parking();
339 let mut server = TestServer::start(&deterministic).await;
340
341 let client_a = server.create_client(cx_a, "user_a").await;
342 let client_b = server.create_client(cx_b, "user_b").await;
343
344 let parent_id = server
345 .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
346 .await;
347
348 let sub_id = client_a
349 .channel_store()
350 .update(cx_a, |channel_store, cx| {
351 channel_store.create_channel("sub_channel", Some(parent_id), cx)
352 })
353 .await
354 .unwrap();
355
356 let active_call_b = cx_b.read(ActiveCall::global);
357
358 assert!(active_call_b
359 .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
360 .await
361 .is_ok());
362}
363
364#[gpui::test]
365async fn test_channel_room(
366 deterministic: Arc<Deterministic>,
367 cx_a: &mut TestAppContext,
368 cx_b: &mut TestAppContext,
369 cx_c: &mut TestAppContext,
370) {
371 deterministic.forbid_parking();
372 let mut server = TestServer::start(&deterministic).await;
373 let client_a = server.create_client(cx_a, "user_a").await;
374 let client_b = server.create_client(cx_b, "user_b").await;
375 let client_c = server.create_client(cx_c, "user_c").await;
376
377 let zed_id = server
378 .make_channel(
379 "zed",
380 None,
381 (&client_a, cx_a),
382 &mut [(&client_b, cx_b), (&client_c, cx_c)],
383 )
384 .await;
385
386 let active_call_a = cx_a.read(ActiveCall::global);
387 let active_call_b = cx_b.read(ActiveCall::global);
388
389 active_call_a
390 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
391 .await
392 .unwrap();
393
394 // Give everyone a chance to observe user A joining
395 deterministic.run_until_parked();
396 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
397 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
398
399 client_a.channel_store().read_with(cx_a, |channels, _| {
400 assert_participants_eq(
401 channels.channel_participants(zed_id),
402 &[client_a.user_id().unwrap()],
403 );
404 });
405
406 assert_channels(
407 client_b.channel_store(),
408 cx_b,
409 &[ExpectedChannel {
410 id: zed_id,
411 name: "zed".to_string(),
412 depth: 0,
413 user_is_admin: false,
414 }],
415 );
416 client_b.channel_store().read_with(cx_b, |channels, _| {
417 assert_participants_eq(
418 channels.channel_participants(zed_id),
419 &[client_a.user_id().unwrap()],
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
567 .make_channel("zed", None, (&client_a, cx_a), &mut [])
568 .await;
569 let rust_id = server
570 .make_channel("rust", None, (&client_a, cx_a), &mut [])
571 .await;
572
573 let active_call_a = cx_a.read(ActiveCall::global);
574
575 active_call_a
576 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
577 .await
578 .unwrap();
579
580 // Give everything a chance to observe user A joining
581 deterministic.run_until_parked();
582
583 client_a.channel_store().read_with(cx_a, |channels, _| {
584 assert_participants_eq(
585 channels.channel_participants(zed_id),
586 &[client_a.user_id().unwrap()],
587 );
588 assert_participants_eq(channels.channel_participants(rust_id), &[]);
589 });
590
591 active_call_a
592 .update(cx_a, |active_call, cx| {
593 active_call.join_channel(rust_id, cx)
594 })
595 .await
596 .unwrap();
597
598 deterministic.run_until_parked();
599
600 client_a.channel_store().read_with(cx_a, |channels, _| {
601 assert_participants_eq(channels.channel_participants(zed_id), &[]);
602 assert_participants_eq(
603 channels.channel_participants(rust_id),
604 &[client_a.user_id().unwrap()],
605 );
606 });
607}
608
609#[gpui::test]
610async fn test_permissions_update_while_invited(
611 deterministic: Arc<Deterministic>,
612 cx_a: &mut TestAppContext,
613 cx_b: &mut TestAppContext,
614) {
615 deterministic.forbid_parking();
616 let mut server = TestServer::start(&deterministic).await;
617 let client_a = server.create_client(cx_a, "user_a").await;
618 let client_b = server.create_client(cx_b, "user_b").await;
619
620 let rust_id = server
621 .make_channel("rust", None, (&client_a, cx_a), &mut [])
622 .await;
623
624 client_a
625 .channel_store()
626 .update(cx_a, |channel_store, cx| {
627 channel_store.invite_member(
628 rust_id,
629 client_b.user_id().unwrap(),
630 proto::ChannelRole::Member,
631 cx,
632 )
633 })
634 .await
635 .unwrap();
636
637 deterministic.run_until_parked();
638
639 assert_channel_invitations(
640 client_b.channel_store(),
641 cx_b,
642 &[ExpectedChannel {
643 depth: 0,
644 id: rust_id,
645 name: "rust".to_string(),
646 user_is_admin: false,
647 }],
648 );
649 assert_channels(client_b.channel_store(), cx_b, &[]);
650
651 // Update B's invite before they've accepted it
652 client_a
653 .channel_store()
654 .update(cx_a, |channel_store, cx| {
655 channel_store.set_member_role(
656 rust_id,
657 client_b.user_id().unwrap(),
658 proto::ChannelRole::Admin,
659 cx,
660 )
661 })
662 .await
663 .unwrap();
664
665 deterministic.run_until_parked();
666
667 assert_channel_invitations(
668 client_b.channel_store(),
669 cx_b,
670 &[ExpectedChannel {
671 depth: 0,
672 id: rust_id,
673 name: "rust".to_string(),
674 user_is_admin: false,
675 }],
676 );
677 assert_channels(client_b.channel_store(), cx_b, &[]);
678}
679
680#[gpui::test]
681async fn test_channel_rename(
682 deterministic: Arc<Deterministic>,
683 cx_a: &mut TestAppContext,
684 cx_b: &mut TestAppContext,
685) {
686 deterministic.forbid_parking();
687 let mut server = TestServer::start(&deterministic).await;
688 let client_a = server.create_client(cx_a, "user_a").await;
689 let client_b = server.create_client(cx_b, "user_b").await;
690
691 let rust_id = server
692 .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
693 .await;
694
695 // Rename the channel
696 client_a
697 .channel_store()
698 .update(cx_a, |channel_store, cx| {
699 channel_store.rename(rust_id, "#rust-archive", cx)
700 })
701 .await
702 .unwrap();
703
704 deterministic.run_until_parked();
705
706 // Client A sees the channel with its new name.
707 assert_channels(
708 client_a.channel_store(),
709 cx_a,
710 &[ExpectedChannel {
711 depth: 0,
712 id: rust_id,
713 name: "rust-archive".to_string(),
714 user_is_admin: true,
715 }],
716 );
717
718 // Client B sees the channel with its new name.
719 assert_channels(
720 client_b.channel_store(),
721 cx_b,
722 &[ExpectedChannel {
723 depth: 0,
724 id: rust_id,
725 name: "rust-archive".to_string(),
726 user_is_admin: false,
727 }],
728 );
729}
730
731#[gpui::test]
732async fn test_call_from_channel(
733 deterministic: Arc<Deterministic>,
734 cx_a: &mut TestAppContext,
735 cx_b: &mut TestAppContext,
736 cx_c: &mut TestAppContext,
737) {
738 deterministic.forbid_parking();
739 let mut server = TestServer::start(&deterministic).await;
740 let client_a = server.create_client(cx_a, "user_a").await;
741 let client_b = server.create_client(cx_b, "user_b").await;
742 let client_c = server.create_client(cx_c, "user_c").await;
743 server
744 .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
745 .await;
746
747 let channel_id = server
748 .make_channel(
749 "x",
750 None,
751 (&client_a, cx_a),
752 &mut [(&client_b, cx_b), (&client_c, cx_c)],
753 )
754 .await;
755
756 let active_call_a = cx_a.read(ActiveCall::global);
757 let active_call_b = cx_b.read(ActiveCall::global);
758
759 active_call_a
760 .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
761 .await
762 .unwrap();
763
764 // Client A calls client B while in the channel.
765 active_call_a
766 .update(cx_a, |call, cx| {
767 call.invite(client_b.user_id().unwrap(), None, cx)
768 })
769 .await
770 .unwrap();
771
772 // Client B accepts the call.
773 deterministic.run_until_parked();
774 active_call_b
775 .update(cx_b, |call, cx| call.accept_incoming(cx))
776 .await
777 .unwrap();
778
779 // Client B sees that they are now in the channel
780 deterministic.run_until_parked();
781 active_call_b.read_with(cx_b, |call, cx| {
782 assert_eq!(call.channel_id(cx), Some(channel_id));
783 });
784 client_b.channel_store().read_with(cx_b, |channels, _| {
785 assert_participants_eq(
786 channels.channel_participants(channel_id),
787 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
788 );
789 });
790
791 // Clients A and C also see that client B is in the channel.
792 client_a.channel_store().read_with(cx_a, |channels, _| {
793 assert_participants_eq(
794 channels.channel_participants(channel_id),
795 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
796 );
797 });
798 client_c.channel_store().read_with(cx_c, |channels, _| {
799 assert_participants_eq(
800 channels.channel_participants(channel_id),
801 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
802 );
803 });
804}
805
806#[gpui::test]
807async fn test_lost_channel_creation(
808 deterministic: Arc<Deterministic>,
809 cx_a: &mut TestAppContext,
810 cx_b: &mut TestAppContext,
811) {
812 deterministic.forbid_parking();
813 let mut server = TestServer::start(&deterministic).await;
814 let client_a = server.create_client(cx_a, "user_a").await;
815 let client_b = server.create_client(cx_b, "user_b").await;
816
817 server
818 .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
819 .await;
820
821 let channel_id = server
822 .make_channel("x", None, (&client_a, cx_a), &mut [])
823 .await;
824
825 // Invite a member
826 client_a
827 .channel_store()
828 .update(cx_a, |channel_store, cx| {
829 channel_store.invite_member(
830 channel_id,
831 client_b.user_id().unwrap(),
832 proto::ChannelRole::Member,
833 cx,
834 )
835 })
836 .await
837 .unwrap();
838
839 deterministic.run_until_parked();
840
841 // Sanity check, B has the invitation
842 assert_channel_invitations(
843 client_b.channel_store(),
844 cx_b,
845 &[ExpectedChannel {
846 depth: 0,
847 id: channel_id,
848 name: "x".to_string(),
849 user_is_admin: false,
850 }],
851 );
852
853 // A creates a subchannel while the invite is still pending.
854 let subchannel_id = client_a
855 .channel_store()
856 .update(cx_a, |channel_store, cx| {
857 channel_store.create_channel("subchannel", Some(channel_id), cx)
858 })
859 .await
860 .unwrap();
861
862 deterministic.run_until_parked();
863
864 // Make sure A sees their new channel
865 assert_channels(
866 client_a.channel_store(),
867 cx_a,
868 &[
869 ExpectedChannel {
870 depth: 0,
871 id: channel_id,
872 name: "x".to_string(),
873 user_is_admin: true,
874 },
875 ExpectedChannel {
876 depth: 1,
877 id: subchannel_id,
878 name: "subchannel".to_string(),
879 user_is_admin: true,
880 },
881 ],
882 );
883
884 // Client B accepts the invite
885 client_b
886 .channel_store()
887 .update(cx_b, |channel_store, _| {
888 channel_store.respond_to_channel_invite(channel_id, true)
889 })
890 .await
891 .unwrap();
892
893 deterministic.run_until_parked();
894
895 // Client B should now see the channel
896 assert_channels(
897 client_b.channel_store(),
898 cx_b,
899 &[
900 ExpectedChannel {
901 depth: 0,
902 id: channel_id,
903 name: "x".to_string(),
904 user_is_admin: false,
905 },
906 ExpectedChannel {
907 depth: 1,
908 id: subchannel_id,
909 name: "subchannel".to_string(),
910 user_is_admin: false,
911 },
912 ],
913 );
914}
915#[gpui::test]
916async fn test_guest_access(
917 deterministic: Arc<Deterministic>,
918 cx_a: &mut TestAppContext,
919 cx_b: &mut TestAppContext,
920) {
921 deterministic.forbid_parking();
922
923 let mut server = TestServer::start(&deterministic).await;
924 let client_a = server.create_client(cx_a, "user_a").await;
925 let client_b = server.create_client(cx_b, "user_b").await;
926
927 let channels = server
928 .make_channel_tree(&[("channel-a", None)], (&client_a, cx_a))
929 .await;
930 let channel_a_id = channels[0];
931
932 let active_call_b = cx_b.read(ActiveCall::global);
933
934 // should not be allowed to join
935 assert!(active_call_b
936 .update(cx_b, |call, cx| call.join_channel(channel_a_id, cx))
937 .await
938 .is_err());
939
940 client_a
941 .channel_store()
942 .update(cx_a, |channel_store, cx| {
943 channel_store.set_channel_visibility(channel_a_id, proto::ChannelVisibility::Public, cx)
944 })
945 .await
946 .unwrap();
947
948 active_call_b
949 .update(cx_b, |call, cx| call.join_channel(channel_a_id, cx))
950 .await
951 .unwrap();
952
953 deterministic.run_until_parked();
954
955 assert!(client_b
956 .channel_store()
957 .update(cx_b, |channel_store, _| channel_store
958 .channel_for_id(channel_a_id)
959 .is_some()));
960
961 client_a.channel_store().update(cx_a, |channel_store, _| {
962 let participants = channel_store.channel_participants(channel_a_id);
963 assert_eq!(participants.len(), 1);
964 assert_eq!(participants[0].id, client_b.user_id().unwrap());
965 })
966}
967
968#[gpui::test]
969async fn test_invite_access(
970 deterministic: Arc<Deterministic>,
971 cx_a: &mut TestAppContext,
972 cx_b: &mut TestAppContext,
973) {
974 deterministic.forbid_parking();
975
976 let mut server = TestServer::start(&deterministic).await;
977 let client_a = server.create_client(cx_a, "user_a").await;
978 let client_b = server.create_client(cx_b, "user_b").await;
979
980 let channels = server
981 .make_channel_tree(
982 &[("channel-a", None), ("channel-b", Some("channel-a"))],
983 (&client_a, cx_a),
984 )
985 .await;
986 let channel_a_id = channels[0];
987 let channel_b_id = channels[0];
988
989 let active_call_b = cx_b.read(ActiveCall::global);
990
991 // should not be allowed to join
992 assert!(active_call_b
993 .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
994 .await
995 .is_err());
996
997 client_a
998 .channel_store()
999 .update(cx_a, |channel_store, cx| {
1000 channel_store.invite_member(
1001 channel_a_id,
1002 client_b.user_id().unwrap(),
1003 ChannelRole::Member,
1004 cx,
1005 )
1006 })
1007 .await
1008 .unwrap();
1009
1010 active_call_b
1011 .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1012 .await
1013 .unwrap();
1014
1015 deterministic.run_until_parked();
1016
1017 client_b.channel_store().update(cx_b, |channel_store, _| {
1018 assert!(channel_store.channel_for_id(channel_b_id).is_some());
1019 assert!(channel_store.channel_for_id(channel_a_id).is_some());
1020 });
1021
1022 client_a.channel_store().update(cx_a, |channel_store, _| {
1023 let participants = channel_store.channel_participants(channel_b_id);
1024 assert_eq!(participants.len(), 1);
1025 assert_eq!(participants[0].id, client_b.user_id().unwrap());
1026 })
1027}
1028
1029#[gpui::test]
1030async fn test_channel_moving(
1031 deterministic: Arc<Deterministic>,
1032 cx_a: &mut TestAppContext,
1033 cx_b: &mut TestAppContext,
1034 cx_c: &mut TestAppContext,
1035) {
1036 deterministic.forbid_parking();
1037 let mut server = TestServer::start(&deterministic).await;
1038 let client_a = server.create_client(cx_a, "user_a").await;
1039 let client_b = server.create_client(cx_b, "user_b").await;
1040 let client_c = server.create_client(cx_c, "user_c").await;
1041
1042 let channels = server
1043 .make_channel_tree(
1044 &[
1045 ("channel-a", None),
1046 ("channel-b", Some("channel-a")),
1047 ("channel-c", Some("channel-b")),
1048 ("channel-d", Some("channel-c")),
1049 ],
1050 (&client_a, cx_a),
1051 )
1052 .await;
1053 let channel_a_id = channels[0];
1054 let channel_b_id = channels[1];
1055 let channel_c_id = channels[2];
1056 let channel_d_id = channels[3];
1057
1058 // Current shape:
1059 // a - b - c - d
1060 assert_channels_list_shape(
1061 client_a.channel_store(),
1062 cx_a,
1063 &[
1064 (channel_a_id, 0),
1065 (channel_b_id, 1),
1066 (channel_c_id, 2),
1067 (channel_d_id, 3),
1068 ],
1069 );
1070
1071 client_a
1072 .channel_store()
1073 .update(cx_a, |channel_store, cx| {
1074 channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
1075 })
1076 .await
1077 .unwrap();
1078
1079 // Current shape:
1080 // /- d
1081 // a - b -- c
1082 assert_channels_list_shape(
1083 client_a.channel_store(),
1084 cx_a,
1085 &[
1086 (channel_a_id, 0),
1087 (channel_b_id, 1),
1088 (channel_c_id, 2),
1089 (channel_d_id, 2),
1090 ],
1091 );
1092
1093 client_a
1094 .channel_store()
1095 .update(cx_a, |channel_store, cx| {
1096 channel_store.link_channel(channel_d_id, channel_c_id, cx)
1097 })
1098 .await
1099 .unwrap();
1100
1101 // Current shape for A:
1102 // /------\
1103 // a - b -- c -- d
1104 assert_channels_list_shape(
1105 client_a.channel_store(),
1106 cx_a,
1107 &[
1108 (channel_a_id, 0),
1109 (channel_b_id, 1),
1110 (channel_c_id, 2),
1111 (channel_d_id, 3),
1112 (channel_d_id, 2),
1113 ],
1114 );
1115
1116 let b_channels = server
1117 .make_channel_tree(
1118 &[
1119 ("channel-mu", None),
1120 ("channel-gamma", Some("channel-mu")),
1121 ("channel-epsilon", Some("channel-mu")),
1122 ],
1123 (&client_b, cx_b),
1124 )
1125 .await;
1126 let channel_mu_id = b_channels[0];
1127 let channel_ga_id = b_channels[1];
1128 let channel_ep_id = b_channels[2];
1129
1130 // Current shape for B:
1131 // /- ep
1132 // mu -- ga
1133 assert_channels_list_shape(
1134 client_b.channel_store(),
1135 cx_b,
1136 &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
1137 );
1138
1139 client_a
1140 .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
1141 .await;
1142
1143 // Current shape for B:
1144 // /- ep
1145 // mu -- ga
1146 // /---------\
1147 // b -- c -- d
1148 assert_channels_list_shape(
1149 client_b.channel_store(),
1150 cx_b,
1151 &[
1152 // New channels from a
1153 (channel_b_id, 0),
1154 (channel_c_id, 1),
1155 (channel_d_id, 2),
1156 (channel_d_id, 1),
1157 // B's old channels
1158 (channel_mu_id, 0),
1159 (channel_ep_id, 1),
1160 (channel_ga_id, 1),
1161 ],
1162 );
1163
1164 client_b
1165 .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
1166 .await;
1167
1168 // Current shape for C:
1169 // - ep
1170 assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
1171
1172 client_b
1173 .channel_store()
1174 .update(cx_b, |channel_store, cx| {
1175 channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1176 })
1177 .await
1178 .unwrap();
1179
1180 // Current shape for B:
1181 // /---------\
1182 // /- ep -- b -- c -- d
1183 // mu -- ga
1184 assert_channels_list_shape(
1185 client_b.channel_store(),
1186 cx_b,
1187 &[
1188 (channel_mu_id, 0),
1189 (channel_ep_id, 1),
1190 (channel_b_id, 2),
1191 (channel_c_id, 3),
1192 (channel_d_id, 4),
1193 (channel_d_id, 3),
1194 (channel_ga_id, 1),
1195 ],
1196 );
1197
1198 // Current shape for C:
1199 // /---------\
1200 // ep -- b -- c -- d
1201 assert_channels_list_shape(
1202 client_c.channel_store(),
1203 cx_c,
1204 &[
1205 (channel_ep_id, 0),
1206 (channel_b_id, 1),
1207 (channel_c_id, 2),
1208 (channel_d_id, 3),
1209 (channel_d_id, 2),
1210 ],
1211 );
1212
1213 client_b
1214 .channel_store()
1215 .update(cx_b, |channel_store, cx| {
1216 channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1217 })
1218 .await
1219 .unwrap();
1220
1221 // Current shape for B:
1222 // /---------\
1223 // /- ep -- b -- c -- d
1224 // / \
1225 // mu ---------- ga
1226 assert_channels_list_shape(
1227 client_b.channel_store(),
1228 cx_b,
1229 &[
1230 (channel_mu_id, 0),
1231 (channel_ep_id, 1),
1232 (channel_b_id, 2),
1233 (channel_c_id, 3),
1234 (channel_d_id, 4),
1235 (channel_d_id, 3),
1236 (channel_ga_id, 3),
1237 (channel_ga_id, 1),
1238 ],
1239 );
1240
1241 // Current shape for A:
1242 // /------\
1243 // a - b -- c -- d
1244 // \-- ga
1245 assert_channels_list_shape(
1246 client_a.channel_store(),
1247 cx_a,
1248 &[
1249 (channel_a_id, 0),
1250 (channel_b_id, 1),
1251 (channel_c_id, 2),
1252 (channel_d_id, 3),
1253 (channel_d_id, 2),
1254 (channel_ga_id, 2),
1255 ],
1256 );
1257
1258 // Current shape for C:
1259 // /-------\
1260 // ep -- b -- c -- d
1261 // \-- ga
1262 assert_channels_list_shape(
1263 client_c.channel_store(),
1264 cx_c,
1265 &[
1266 (channel_ep_id, 0),
1267 (channel_b_id, 1),
1268 (channel_c_id, 2),
1269 (channel_d_id, 3),
1270 (channel_d_id, 2),
1271 (channel_ga_id, 2),
1272 ],
1273 );
1274}
1275
1276#[derive(Debug, PartialEq)]
1277struct ExpectedChannel {
1278 depth: usize,
1279 id: ChannelId,
1280 name: String,
1281 user_is_admin: bool,
1282}
1283
1284#[track_caller]
1285fn assert_channel_invitations(
1286 channel_store: &ModelHandle<ChannelStore>,
1287 cx: &TestAppContext,
1288 expected_channels: &[ExpectedChannel],
1289) {
1290 let actual = channel_store.read_with(cx, |store, _| {
1291 store
1292 .channel_invitations()
1293 .iter()
1294 .map(|channel| ExpectedChannel {
1295 depth: 0,
1296 name: channel.name.clone(),
1297 id: channel.id,
1298 user_is_admin: store.is_user_admin(channel.id),
1299 })
1300 .collect::<Vec<_>>()
1301 });
1302 assert_eq!(actual, expected_channels);
1303}
1304
1305#[track_caller]
1306fn assert_channels(
1307 channel_store: &ModelHandle<ChannelStore>,
1308 cx: &TestAppContext,
1309 expected_channels: &[ExpectedChannel],
1310) {
1311 let actual = channel_store.read_with(cx, |store, _| {
1312 store
1313 .channel_dag_entries()
1314 .map(|(depth, channel)| ExpectedChannel {
1315 depth,
1316 name: channel.name.clone(),
1317 id: channel.id,
1318 user_is_admin: store.is_user_admin(channel.id),
1319 })
1320 .collect::<Vec<_>>()
1321 });
1322 pretty_assertions::assert_eq!(actual, expected_channels);
1323}
1324
1325#[track_caller]
1326fn assert_channels_list_shape(
1327 channel_store: &ModelHandle<ChannelStore>,
1328 cx: &TestAppContext,
1329 expected_channels: &[(u64, usize)],
1330) {
1331 cx.foreground().run_until_parked();
1332
1333 let actual = channel_store.read_with(cx, |store, _| {
1334 store
1335 .channel_dag_entries()
1336 .map(|(depth, channel)| (channel.id, depth))
1337 .collect::<Vec<_>>()
1338 });
1339 pretty_assertions::assert_eq!(actual, expected_channels);
1340}