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::{proto, RECEIVE_TIMEOUT};
10use std::sync::Arc;
11
12#[gpui::test]
13async fn test_core_channels(
14 deterministic: Arc<Deterministic>,
15 cx_a: &mut TestAppContext,
16 cx_b: &mut TestAppContext,
17) {
18 deterministic.forbid_parking();
19 let mut server = TestServer::start(&deterministic).await;
20 let client_a = server.create_client(cx_a, "user_a").await;
21 let client_b = server.create_client(cx_b, "user_b").await;
22
23 let channel_a_id = client_a
24 .channel_store()
25 .update(cx_a, |channel_store, cx| {
26 channel_store.create_channel("channel-a", None, cx)
27 })
28 .await
29 .unwrap();
30 let channel_b_id = client_a
31 .channel_store()
32 .update(cx_a, |channel_store, cx| {
33 channel_store.create_channel("channel-b", Some(channel_a_id), cx)
34 })
35 .await
36 .unwrap();
37
38 deterministic.run_until_parked();
39 assert_channels(
40 client_a.channel_store(),
41 cx_a,
42 &[
43 ExpectedChannel {
44 id: channel_a_id,
45 name: "channel-a".to_string(),
46 depth: 0,
47 user_is_admin: true,
48 },
49 ExpectedChannel {
50 id: channel_b_id,
51 name: "channel-b".to_string(),
52 depth: 1,
53 user_is_admin: true,
54 },
55 ],
56 );
57
58 client_b.channel_store().read_with(cx_b, |channels, _| {
59 assert!(channels
60 .channel_dag_entries()
61 .collect::<Vec<_>>()
62 .is_empty())
63 });
64
65 // Invite client B to channel A as client A.
66 client_a
67 .channel_store()
68 .update(cx_a, |store, cx| {
69 assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
70
71 let invite = store.invite_member(channel_a_id, client_b.user_id().unwrap(), false, cx);
72
73 // Make sure we're synchronously storing the pending invite
74 assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
75 invite
76 })
77 .await
78 .unwrap();
79
80 // Client A sees that B has been invited.
81 deterministic.run_until_parked();
82 assert_channel_invitations(
83 client_b.channel_store(),
84 cx_b,
85 &[ExpectedChannel {
86 id: channel_a_id,
87 name: "channel-a".to_string(),
88 depth: 0,
89 user_is_admin: false,
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 assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
129 assert_channels(
130 client_b.channel_store(),
131 cx_b,
132 &[
133 ExpectedChannel {
134 id: channel_a_id,
135 name: "channel-a".to_string(),
136 user_is_admin: false,
137 depth: 0,
138 },
139 ExpectedChannel {
140 id: channel_b_id,
141 name: "channel-b".to_string(),
142 user_is_admin: false,
143 depth: 1,
144 },
145 ],
146 );
147
148 let channel_c_id = client_a
149 .channel_store()
150 .update(cx_a, |channel_store, cx| {
151 channel_store.create_channel("channel-c", Some(channel_b_id), cx)
152 })
153 .await
154 .unwrap();
155
156 deterministic.run_until_parked();
157 assert_channels(
158 client_b.channel_store(),
159 cx_b,
160 &[
161 ExpectedChannel {
162 id: channel_a_id,
163 name: "channel-a".to_string(),
164 user_is_admin: false,
165 depth: 0,
166 },
167 ExpectedChannel {
168 id: channel_b_id,
169 name: "channel-b".to_string(),
170 user_is_admin: false,
171 depth: 1,
172 },
173 ExpectedChannel {
174 id: channel_c_id,
175 name: "channel-c".to_string(),
176 user_is_admin: false,
177 depth: 2,
178 },
179 ],
180 );
181
182 // Update client B's membership to channel A to be an admin.
183 client_a
184 .channel_store()
185 .update(cx_a, |store, cx| {
186 store.set_member_admin(channel_a_id, client_b.user_id().unwrap(), true, cx)
187 })
188 .await
189 .unwrap();
190 deterministic.run_until_parked();
191
192 // Observe that client B is now an admin of channel A, and that
193 // their admin priveleges extend to subchannels of channel A.
194 assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
195 assert_channels(
196 client_b.channel_store(),
197 cx_b,
198 &[
199 ExpectedChannel {
200 id: channel_a_id,
201 name: "channel-a".to_string(),
202 depth: 0,
203 user_is_admin: true,
204 },
205 ExpectedChannel {
206 id: channel_b_id,
207 name: "channel-b".to_string(),
208 depth: 1,
209 user_is_admin: true,
210 },
211 ExpectedChannel {
212 id: channel_c_id,
213 name: "channel-c".to_string(),
214 depth: 2,
215 user_is_admin: true,
216 },
217 ],
218 );
219
220 // Client A deletes the channel, deletion also deletes subchannels.
221 client_a
222 .channel_store()
223 .update(cx_a, |channel_store, _| {
224 channel_store.remove_channel(channel_b_id)
225 })
226 .await
227 .unwrap();
228
229 deterministic.run_until_parked();
230 assert_channels(
231 client_a.channel_store(),
232 cx_a,
233 &[ExpectedChannel {
234 id: channel_a_id,
235 name: "channel-a".to_string(),
236 depth: 0,
237 user_is_admin: true,
238 }],
239 );
240 assert_channels(
241 client_b.channel_store(),
242 cx_b,
243 &[ExpectedChannel {
244 id: channel_a_id,
245 name: "channel-a".to_string(),
246 depth: 0,
247 user_is_admin: true,
248 }],
249 );
250
251 // Remove client B
252 client_a
253 .channel_store()
254 .update(cx_a, |channel_store, cx| {
255 channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
256 })
257 .await
258 .unwrap();
259
260 deterministic.run_until_parked();
261
262 // Client A still has their channel
263 assert_channels(
264 client_a.channel_store(),
265 cx_a,
266 &[ExpectedChannel {
267 id: channel_a_id,
268 name: "channel-a".to_string(),
269 depth: 0,
270 user_is_admin: true,
271 }],
272 );
273
274 // Client B no longer has access to the channel
275 assert_channels(client_b.channel_store(), cx_b, &[]);
276
277 // When disconnected, client A sees no channels.
278 server.forbid_connections();
279 server.disconnect_client(client_a.peer_id().unwrap());
280 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
281 assert_channels(client_a.channel_store(), cx_a, &[]);
282
283 server.allow_connections();
284 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
285 assert_channels(
286 client_a.channel_store(),
287 cx_a,
288 &[ExpectedChannel {
289 id: channel_a_id,
290 name: "channel-a".to_string(),
291 depth: 0,
292 user_is_admin: true,
293 }],
294 );
295}
296
297#[track_caller]
298fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
299 assert_eq!(
300 participants.iter().map(|p| p.id).collect::<Vec<_>>(),
301 expected_partitipants
302 );
303}
304
305#[track_caller]
306fn assert_members_eq(
307 members: &[ChannelMembership],
308 expected_members: &[(u64, bool, proto::channel_member::Kind)],
309) {
310 assert_eq!(
311 members
312 .iter()
313 .map(|member| (member.user.id, member.admin, member.kind))
314 .collect::<Vec<_>>(),
315 expected_members
316 );
317}
318
319#[gpui::test]
320async fn test_joining_channel_ancestor_member(
321 deterministic: Arc<Deterministic>,
322 cx_a: &mut TestAppContext,
323 cx_b: &mut TestAppContext,
324) {
325 deterministic.forbid_parking();
326 let mut server = TestServer::start(&deterministic).await;
327
328 let client_a = server.create_client(cx_a, "user_a").await;
329 let client_b = server.create_client(cx_b, "user_b").await;
330
331 let parent_id = server
332 .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
333 .await;
334
335 let sub_id = client_a
336 .channel_store()
337 .update(cx_a, |channel_store, cx| {
338 channel_store.create_channel("sub_channel", Some(parent_id), cx)
339 })
340 .await
341 .unwrap();
342
343 let active_call_b = cx_b.read(ActiveCall::global);
344
345 assert!(active_call_b
346 .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
347 .await
348 .is_ok());
349}
350
351#[gpui::test]
352async fn test_channel_room(
353 deterministic: Arc<Deterministic>,
354 cx_a: &mut TestAppContext,
355 cx_b: &mut TestAppContext,
356 cx_c: &mut TestAppContext,
357) {
358 deterministic.forbid_parking();
359 let mut server = TestServer::start(&deterministic).await;
360 let client_a = server.create_client(cx_a, "user_a").await;
361 let client_b = server.create_client(cx_b, "user_b").await;
362 let client_c = server.create_client(cx_c, "user_c").await;
363
364 let zed_id = server
365 .make_channel(
366 "zed",
367 None,
368 (&client_a, cx_a),
369 &mut [(&client_b, cx_b), (&client_c, cx_c)],
370 )
371 .await;
372
373 let active_call_a = cx_a.read(ActiveCall::global);
374 let active_call_b = cx_b.read(ActiveCall::global);
375
376 active_call_a
377 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
378 .await
379 .unwrap();
380
381 // Give everyone a chance to observe user A joining
382 deterministic.run_until_parked();
383 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
384 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
385
386 client_a.channel_store().read_with(cx_a, |channels, _| {
387 assert_participants_eq(
388 channels.channel_participants(zed_id),
389 &[client_a.user_id().unwrap()],
390 );
391 });
392
393 assert_channels(
394 client_b.channel_store(),
395 cx_b,
396 &[ExpectedChannel {
397 id: zed_id,
398 name: "zed".to_string(),
399 depth: 0,
400 user_is_admin: false,
401 }],
402 );
403 client_b.channel_store().read_with(cx_b, |channels, _| {
404 assert_participants_eq(
405 channels.channel_participants(zed_id),
406 &[client_a.user_id().unwrap()],
407 );
408 });
409
410 client_c.channel_store().read_with(cx_c, |channels, _| {
411 assert_participants_eq(
412 channels.channel_participants(zed_id),
413 &[client_a.user_id().unwrap()],
414 );
415 });
416
417 active_call_b
418 .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
419 .await
420 .unwrap();
421
422 deterministic.run_until_parked();
423
424 client_a.channel_store().read_with(cx_a, |channels, _| {
425 assert_participants_eq(
426 channels.channel_participants(zed_id),
427 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
428 );
429 });
430
431 client_b.channel_store().read_with(cx_b, |channels, _| {
432 assert_participants_eq(
433 channels.channel_participants(zed_id),
434 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
435 );
436 });
437
438 client_c.channel_store().read_with(cx_c, |channels, _| {
439 assert_participants_eq(
440 channels.channel_participants(zed_id),
441 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
442 );
443 });
444
445 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
446 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
447 assert_eq!(
448 room_participants(&room_a, cx_a),
449 RoomParticipants {
450 remote: vec!["user_b".to_string()],
451 pending: vec![]
452 }
453 );
454
455 let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
456 room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
457 assert_eq!(
458 room_participants(&room_b, cx_b),
459 RoomParticipants {
460 remote: vec!["user_a".to_string()],
461 pending: vec![]
462 }
463 );
464
465 // Make sure that leaving and rejoining works
466
467 active_call_a
468 .update(cx_a, |active_call, cx| active_call.hang_up(cx))
469 .await
470 .unwrap();
471
472 deterministic.run_until_parked();
473
474 client_a.channel_store().read_with(cx_a, |channels, _| {
475 assert_participants_eq(
476 channels.channel_participants(zed_id),
477 &[client_b.user_id().unwrap()],
478 );
479 });
480
481 client_b.channel_store().read_with(cx_b, |channels, _| {
482 assert_participants_eq(
483 channels.channel_participants(zed_id),
484 &[client_b.user_id().unwrap()],
485 );
486 });
487
488 client_c.channel_store().read_with(cx_c, |channels, _| {
489 assert_participants_eq(
490 channels.channel_participants(zed_id),
491 &[client_b.user_id().unwrap()],
492 );
493 });
494
495 active_call_b
496 .update(cx_b, |active_call, cx| active_call.hang_up(cx))
497 .await
498 .unwrap();
499
500 deterministic.run_until_parked();
501
502 client_a.channel_store().read_with(cx_a, |channels, _| {
503 assert_participants_eq(channels.channel_participants(zed_id), &[]);
504 });
505
506 client_b.channel_store().read_with(cx_b, |channels, _| {
507 assert_participants_eq(channels.channel_participants(zed_id), &[]);
508 });
509
510 client_c.channel_store().read_with(cx_c, |channels, _| {
511 assert_participants_eq(channels.channel_participants(zed_id), &[]);
512 });
513
514 active_call_a
515 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
516 .await
517 .unwrap();
518
519 active_call_b
520 .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
521 .await
522 .unwrap();
523
524 deterministic.run_until_parked();
525
526 let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
527 room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
528 assert_eq!(
529 room_participants(&room_a, cx_a),
530 RoomParticipants {
531 remote: vec!["user_b".to_string()],
532 pending: vec![]
533 }
534 );
535
536 let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
537 room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
538 assert_eq!(
539 room_participants(&room_b, cx_b),
540 RoomParticipants {
541 remote: vec!["user_a".to_string()],
542 pending: vec![]
543 }
544 );
545}
546
547#[gpui::test]
548async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
549 deterministic.forbid_parking();
550 let mut server = TestServer::start(&deterministic).await;
551 let client_a = server.create_client(cx_a, "user_a").await;
552
553 let zed_id = server
554 .make_channel("zed", None, (&client_a, cx_a), &mut [])
555 .await;
556 let rust_id = server
557 .make_channel("rust", None, (&client_a, cx_a), &mut [])
558 .await;
559
560 let active_call_a = cx_a.read(ActiveCall::global);
561
562 active_call_a
563 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
564 .await
565 .unwrap();
566
567 // Give everything a chance to observe user A joining
568 deterministic.run_until_parked();
569
570 client_a.channel_store().read_with(cx_a, |channels, _| {
571 assert_participants_eq(
572 channels.channel_participants(zed_id),
573 &[client_a.user_id().unwrap()],
574 );
575 assert_participants_eq(channels.channel_participants(rust_id), &[]);
576 });
577
578 active_call_a
579 .update(cx_a, |active_call, cx| {
580 active_call.join_channel(rust_id, cx)
581 })
582 .await
583 .unwrap();
584
585 deterministic.run_until_parked();
586
587 client_a.channel_store().read_with(cx_a, |channels, _| {
588 assert_participants_eq(channels.channel_participants(zed_id), &[]);
589 assert_participants_eq(
590 channels.channel_participants(rust_id),
591 &[client_a.user_id().unwrap()],
592 );
593 });
594}
595
596#[gpui::test]
597async fn test_permissions_update_while_invited(
598 deterministic: Arc<Deterministic>,
599 cx_a: &mut TestAppContext,
600 cx_b: &mut TestAppContext,
601) {
602 deterministic.forbid_parking();
603 let mut server = TestServer::start(&deterministic).await;
604 let client_a = server.create_client(cx_a, "user_a").await;
605 let client_b = server.create_client(cx_b, "user_b").await;
606
607 let rust_id = server
608 .make_channel("rust", None, (&client_a, cx_a), &mut [])
609 .await;
610
611 client_a
612 .channel_store()
613 .update(cx_a, |channel_store, cx| {
614 channel_store.invite_member(rust_id, client_b.user_id().unwrap(), false, cx)
615 })
616 .await
617 .unwrap();
618
619 deterministic.run_until_parked();
620
621 assert_channel_invitations(
622 client_b.channel_store(),
623 cx_b,
624 &[ExpectedChannel {
625 depth: 0,
626 id: rust_id,
627 name: "rust".to_string(),
628 user_is_admin: false,
629 }],
630 );
631 assert_channels(client_b.channel_store(), cx_b, &[]);
632
633 // Update B's invite before they've accepted it
634 client_a
635 .channel_store()
636 .update(cx_a, |channel_store, cx| {
637 channel_store.set_member_admin(rust_id, client_b.user_id().unwrap(), true, cx)
638 })
639 .await
640 .unwrap();
641
642 deterministic.run_until_parked();
643
644 assert_channel_invitations(
645 client_b.channel_store(),
646 cx_b,
647 &[ExpectedChannel {
648 depth: 0,
649 id: rust_id,
650 name: "rust".to_string(),
651 user_is_admin: false,
652 }],
653 );
654 assert_channels(client_b.channel_store(), cx_b, &[]);
655}
656
657#[gpui::test]
658async fn test_channel_rename(
659 deterministic: Arc<Deterministic>,
660 cx_a: &mut TestAppContext,
661 cx_b: &mut TestAppContext,
662) {
663 deterministic.forbid_parking();
664 let mut server = TestServer::start(&deterministic).await;
665 let client_a = server.create_client(cx_a, "user_a").await;
666 let client_b = server.create_client(cx_b, "user_b").await;
667
668 let rust_id = server
669 .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
670 .await;
671
672 // Rename the channel
673 client_a
674 .channel_store()
675 .update(cx_a, |channel_store, cx| {
676 channel_store.rename(rust_id, "#rust-archive", cx)
677 })
678 .await
679 .unwrap();
680
681 deterministic.run_until_parked();
682
683 // Client A sees the channel with its new name.
684 assert_channels(
685 client_a.channel_store(),
686 cx_a,
687 &[ExpectedChannel {
688 depth: 0,
689 id: rust_id,
690 name: "rust-archive".to_string(),
691 user_is_admin: true,
692 }],
693 );
694
695 // Client B sees the channel with its new name.
696 assert_channels(
697 client_b.channel_store(),
698 cx_b,
699 &[ExpectedChannel {
700 depth: 0,
701 id: rust_id,
702 name: "rust-archive".to_string(),
703 user_is_admin: false,
704 }],
705 );
706}
707
708#[gpui::test]
709async fn test_call_from_channel(
710 deterministic: Arc<Deterministic>,
711 cx_a: &mut TestAppContext,
712 cx_b: &mut TestAppContext,
713 cx_c: &mut TestAppContext,
714) {
715 deterministic.forbid_parking();
716 let mut server = TestServer::start(&deterministic).await;
717 let client_a = server.create_client(cx_a, "user_a").await;
718 let client_b = server.create_client(cx_b, "user_b").await;
719 let client_c = server.create_client(cx_c, "user_c").await;
720 server
721 .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
722 .await;
723
724 let channel_id = server
725 .make_channel(
726 "x",
727 None,
728 (&client_a, cx_a),
729 &mut [(&client_b, cx_b), (&client_c, cx_c)],
730 )
731 .await;
732
733 let active_call_a = cx_a.read(ActiveCall::global);
734 let active_call_b = cx_b.read(ActiveCall::global);
735
736 active_call_a
737 .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
738 .await
739 .unwrap();
740
741 // Client A calls client B while in the channel.
742 active_call_a
743 .update(cx_a, |call, cx| {
744 call.invite(client_b.user_id().unwrap(), None, cx)
745 })
746 .await
747 .unwrap();
748
749 // Client B accepts the call.
750 deterministic.run_until_parked();
751 active_call_b
752 .update(cx_b, |call, cx| call.accept_incoming(cx))
753 .await
754 .unwrap();
755
756 // Client B sees that they are now in the channel
757 deterministic.run_until_parked();
758 active_call_b.read_with(cx_b, |call, cx| {
759 assert_eq!(call.channel_id(cx), Some(channel_id));
760 });
761 client_b.channel_store().read_with(cx_b, |channels, _| {
762 assert_participants_eq(
763 channels.channel_participants(channel_id),
764 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
765 );
766 });
767
768 // Clients A and C also see that client B is in the channel.
769 client_a.channel_store().read_with(cx_a, |channels, _| {
770 assert_participants_eq(
771 channels.channel_participants(channel_id),
772 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
773 );
774 });
775 client_c.channel_store().read_with(cx_c, |channels, _| {
776 assert_participants_eq(
777 channels.channel_participants(channel_id),
778 &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
779 );
780 });
781}
782
783#[gpui::test]
784async fn test_lost_channel_creation(
785 deterministic: Arc<Deterministic>,
786 cx_a: &mut TestAppContext,
787 cx_b: &mut TestAppContext,
788) {
789 deterministic.forbid_parking();
790 let mut server = TestServer::start(&deterministic).await;
791 let client_a = server.create_client(cx_a, "user_a").await;
792 let client_b = server.create_client(cx_b, "user_b").await;
793
794 server
795 .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
796 .await;
797
798 let channel_id = server
799 .make_channel("x", None, (&client_a, cx_a), &mut [])
800 .await;
801
802 // Invite a member
803 client_a
804 .channel_store()
805 .update(cx_a, |channel_store, cx| {
806 channel_store.invite_member(channel_id, client_b.user_id().unwrap(), false, cx)
807 })
808 .await
809 .unwrap();
810
811 deterministic.run_until_parked();
812
813 // Sanity check, B has the invitation
814 assert_channel_invitations(
815 client_b.channel_store(),
816 cx_b,
817 &[ExpectedChannel {
818 depth: 0,
819 id: channel_id,
820 name: "x".to_string(),
821 user_is_admin: false,
822 }],
823 );
824
825 // A creates a subchannel while the invite is still pending.
826 let subchannel_id = client_a
827 .channel_store()
828 .update(cx_a, |channel_store, cx| {
829 channel_store.create_channel("subchannel", Some(channel_id), cx)
830 })
831 .await
832 .unwrap();
833
834 deterministic.run_until_parked();
835
836 // Make sure A sees their new channel
837 assert_channels(
838 client_a.channel_store(),
839 cx_a,
840 &[
841 ExpectedChannel {
842 depth: 0,
843 id: channel_id,
844 name: "x".to_string(),
845 user_is_admin: true,
846 },
847 ExpectedChannel {
848 depth: 1,
849 id: subchannel_id,
850 name: "subchannel".to_string(),
851 user_is_admin: true,
852 },
853 ],
854 );
855
856 // Client B accepts the invite
857 client_b
858 .channel_store()
859 .update(cx_b, |channel_store, _| {
860 channel_store.respond_to_channel_invite(channel_id, true)
861 })
862 .await
863 .unwrap();
864
865 deterministic.run_until_parked();
866
867 // Client B should now see the channel
868 assert_channels(
869 client_b.channel_store(),
870 cx_b,
871 &[
872 ExpectedChannel {
873 depth: 0,
874 id: channel_id,
875 name: "x".to_string(),
876 user_is_admin: false,
877 },
878 ExpectedChannel {
879 depth: 1,
880 id: subchannel_id,
881 name: "subchannel".to_string(),
882 user_is_admin: false,
883 },
884 ],
885 );
886}
887
888#[gpui::test]
889async fn test_channel_moving(
890 deterministic: Arc<Deterministic>,
891 cx_a: &mut TestAppContext,
892 cx_b: &mut TestAppContext,
893 cx_c: &mut TestAppContext,
894) {
895 deterministic.forbid_parking();
896 let mut server = TestServer::start(&deterministic).await;
897 let client_a = server.create_client(cx_a, "user_a").await;
898 let client_b = server.create_client(cx_b, "user_b").await;
899 let client_c = server.create_client(cx_c, "user_c").await;
900
901 let channels = server
902 .make_channel_tree(
903 &[
904 ("channel-a", None),
905 ("channel-b", Some("channel-a")),
906 ("channel-c", Some("channel-b")),
907 ("channel-d", Some("channel-c")),
908 ],
909 (&client_a, cx_a),
910 )
911 .await;
912 let channel_a_id = channels[0];
913 let channel_b_id = channels[1];
914 let channel_c_id = channels[2];
915 let channel_d_id = channels[3];
916
917 // Current shape:
918 // a - b - c - d
919 assert_channels_list_shape(
920 client_a.channel_store(),
921 cx_a,
922 &[
923 (channel_a_id, 0),
924 (channel_b_id, 1),
925 (channel_c_id, 2),
926 (channel_d_id, 3),
927 ],
928 );
929
930 client_a
931 .channel_store()
932 .update(cx_a, |channel_store, cx| {
933 channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
934 })
935 .await
936 .unwrap();
937
938 // Current shape:
939 // /- d
940 // a - b -- c
941 assert_channels_list_shape(
942 client_a.channel_store(),
943 cx_a,
944 &[
945 (channel_a_id, 0),
946 (channel_b_id, 1),
947 (channel_c_id, 2),
948 (channel_d_id, 2),
949 ],
950 );
951
952 client_a
953 .channel_store()
954 .update(cx_a, |channel_store, cx| {
955 channel_store.link_channel(channel_d_id, channel_c_id, cx)
956 })
957 .await
958 .unwrap();
959
960 // Current shape for A:
961 // /------\
962 // a - b -- c -- d
963 assert_channels_list_shape(
964 client_a.channel_store(),
965 cx_a,
966 &[
967 (channel_a_id, 0),
968 (channel_b_id, 1),
969 (channel_c_id, 2),
970 (channel_d_id, 3),
971 (channel_d_id, 2),
972 ],
973 );
974
975 let b_channels = server
976 .make_channel_tree(
977 &[
978 ("channel-mu", None),
979 ("channel-gamma", Some("channel-mu")),
980 ("channel-epsilon", Some("channel-mu")),
981 ],
982 (&client_b, cx_b),
983 )
984 .await;
985 let channel_mu_id = b_channels[0];
986 let channel_ga_id = b_channels[1];
987 let channel_ep_id = b_channels[2];
988
989 // Current shape for B:
990 // /- ep
991 // mu -- ga
992 assert_channels_list_shape(
993 client_b.channel_store(),
994 cx_b,
995 &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
996 );
997
998 client_a
999 .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
1000 .await;
1001
1002 // Current shape for B:
1003 // /- ep
1004 // mu -- ga
1005 // /---------\
1006 // b -- c -- d
1007 assert_channels_list_shape(
1008 client_b.channel_store(),
1009 cx_b,
1010 &[
1011 // New channels from a
1012 (channel_b_id, 0),
1013 (channel_c_id, 1),
1014 (channel_d_id, 2),
1015 (channel_d_id, 1),
1016 // B's old channels
1017 (channel_mu_id, 0),
1018 (channel_ep_id, 1),
1019 (channel_ga_id, 1),
1020 ],
1021 );
1022
1023 client_b
1024 .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
1025 .await;
1026
1027 // Current shape for C:
1028 // - ep
1029 assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
1030
1031 client_b
1032 .channel_store()
1033 .update(cx_b, |channel_store, cx| {
1034 channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1035 })
1036 .await
1037 .unwrap();
1038
1039 // Current shape for B:
1040 // /---------\
1041 // /- ep -- b -- c -- d
1042 // mu -- ga
1043 assert_channels_list_shape(
1044 client_b.channel_store(),
1045 cx_b,
1046 &[
1047 (channel_mu_id, 0),
1048 (channel_ep_id, 1),
1049 (channel_b_id, 2),
1050 (channel_c_id, 3),
1051 (channel_d_id, 4),
1052 (channel_d_id, 3),
1053 (channel_ga_id, 1),
1054 ],
1055 );
1056
1057 // Current shape for C:
1058 // /---------\
1059 // ep -- b -- c -- d
1060 assert_channels_list_shape(
1061 client_c.channel_store(),
1062 cx_c,
1063 &[
1064 (channel_ep_id, 0),
1065 (channel_b_id, 1),
1066 (channel_c_id, 2),
1067 (channel_d_id, 3),
1068 (channel_d_id, 2),
1069 ],
1070 );
1071
1072 client_b
1073 .channel_store()
1074 .update(cx_b, |channel_store, cx| {
1075 channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1076 })
1077 .await
1078 .unwrap();
1079
1080 // Current shape for B:
1081 // /---------\
1082 // /- ep -- b -- c -- d
1083 // / \
1084 // mu ---------- ga
1085 assert_channels_list_shape(
1086 client_b.channel_store(),
1087 cx_b,
1088 &[
1089 (channel_mu_id, 0),
1090 (channel_ep_id, 1),
1091 (channel_b_id, 2),
1092 (channel_c_id, 3),
1093 (channel_d_id, 4),
1094 (channel_d_id, 3),
1095 (channel_ga_id, 3),
1096 (channel_ga_id, 1),
1097 ],
1098 );
1099
1100 // Current shape for A:
1101 // /------\
1102 // a - b -- c -- d
1103 // \-- ga
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 (channel_ga_id, 2),
1114 ],
1115 );
1116
1117 // Current shape for C:
1118 // /-------\
1119 // ep -- b -- c -- d
1120 // \-- ga
1121 assert_channels_list_shape(
1122 client_c.channel_store(),
1123 cx_c,
1124 &[
1125 (channel_ep_id, 0),
1126 (channel_b_id, 1),
1127 (channel_c_id, 2),
1128 (channel_d_id, 3),
1129 (channel_d_id, 2),
1130 (channel_ga_id, 2),
1131 ],
1132 );
1133}
1134
1135#[derive(Debug, PartialEq)]
1136struct ExpectedChannel {
1137 depth: usize,
1138 id: ChannelId,
1139 name: String,
1140 user_is_admin: bool,
1141}
1142
1143#[track_caller]
1144fn assert_channel_invitations(
1145 channel_store: &ModelHandle<ChannelStore>,
1146 cx: &TestAppContext,
1147 expected_channels: &[ExpectedChannel],
1148) {
1149 let actual = channel_store.read_with(cx, |store, _| {
1150 store
1151 .channel_invitations()
1152 .iter()
1153 .map(|channel| ExpectedChannel {
1154 depth: 0,
1155 name: channel.name.clone(),
1156 id: channel.id,
1157 user_is_admin: store.is_user_admin(channel.id),
1158 })
1159 .collect::<Vec<_>>()
1160 });
1161 assert_eq!(actual, expected_channels);
1162}
1163
1164#[track_caller]
1165fn assert_channels(
1166 channel_store: &ModelHandle<ChannelStore>,
1167 cx: &TestAppContext,
1168 expected_channels: &[ExpectedChannel],
1169) {
1170 let actual = channel_store.read_with(cx, |store, _| {
1171 store
1172 .channel_dag_entries()
1173 .map(|(depth, channel)| ExpectedChannel {
1174 depth,
1175 name: channel.name.clone(),
1176 id: channel.id,
1177 user_is_admin: store.is_user_admin(channel.id),
1178 })
1179 .collect::<Vec<_>>()
1180 });
1181 pretty_assertions::assert_eq!(actual, expected_channels);
1182}
1183
1184#[track_caller]
1185fn assert_channels_list_shape(
1186 channel_store: &ModelHandle<ChannelStore>,
1187 cx: &TestAppContext,
1188 expected_channels: &[(u64, usize)],
1189) {
1190 cx.foreground().run_until_parked();
1191
1192 let actual = channel_store.read_with(cx, |store, _| {
1193 store
1194 .channel_dag_entries()
1195 .map(|(depth, channel)| (channel.id, depth))
1196 .collect::<Vec<_>>()
1197 });
1198 pretty_assertions::assert_eq!(actual, expected_channels);
1199}