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 println!("STARTING CREATE CHANNEL C");
149
150 let channel_c_id = client_a
151 .channel_store()
152 .update(cx_a, |channel_store, cx| {
153 channel_store.create_channel("channel-c", Some(channel_b_id), cx)
154 })
155 .await
156 .unwrap();
157
158 deterministic.run_until_parked();
159 assert_channels(
160 client_b.channel_store(),
161 cx_b,
162 &[
163 ExpectedChannel {
164 id: channel_a_id,
165 name: "channel-a".to_string(),
166 user_is_admin: false,
167 depth: 0,
168 },
169 ExpectedChannel {
170 id: channel_b_id,
171 name: "channel-b".to_string(),
172 user_is_admin: false,
173 depth: 1,
174 },
175 ExpectedChannel {
176 id: channel_c_id,
177 name: "channel-c".to_string(),
178 user_is_admin: false,
179 depth: 2,
180 },
181 ],
182 );
183
184 // Update client B's membership to channel A to be an admin.
185 client_a
186 .channel_store()
187 .update(cx_a, |store, cx| {
188 store.set_member_admin(channel_a_id, client_b.user_id().unwrap(), true, cx)
189 })
190 .await
191 .unwrap();
192 deterministic.run_until_parked();
193
194 // Observe that client B is now an admin of channel A, and that
195 // their admin priveleges extend to subchannels of channel A.
196 assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
197 assert_channels(
198 client_b.channel_store(),
199 cx_b,
200 &[
201 ExpectedChannel {
202 id: channel_a_id,
203 name: "channel-a".to_string(),
204 depth: 0,
205 user_is_admin: true,
206 },
207 ExpectedChannel {
208 id: channel_b_id,
209 name: "channel-b".to_string(),
210 depth: 1,
211 user_is_admin: true,
212 },
213 ExpectedChannel {
214 id: channel_c_id,
215 name: "channel-c".to_string(),
216 depth: 2,
217 user_is_admin: true,
218 },
219 ],
220 );
221
222 // Client A deletes the channel, deletion also deletes subchannels.
223 client_a
224 .channel_store()
225 .update(cx_a, |channel_store, _| {
226 channel_store.remove_channel(channel_b_id)
227 })
228 .await
229 .unwrap();
230
231 deterministic.run_until_parked();
232 assert_channels(
233 client_a.channel_store(),
234 cx_a,
235 &[ExpectedChannel {
236 id: channel_a_id,
237 name: "channel-a".to_string(),
238 depth: 0,
239 user_is_admin: true,
240 }],
241 );
242 assert_channels(
243 client_b.channel_store(),
244 cx_b,
245 &[ExpectedChannel {
246 id: channel_a_id,
247 name: "channel-a".to_string(),
248 depth: 0,
249 user_is_admin: true,
250 }],
251 );
252
253 // Remove client B
254 client_a
255 .channel_store()
256 .update(cx_a, |channel_store, cx| {
257 channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
258 })
259 .await
260 .unwrap();
261
262 deterministic.run_until_parked();
263
264 // Client A still has their channel
265 assert_channels(
266 client_a.channel_store(),
267 cx_a,
268 &[ExpectedChannel {
269 id: channel_a_id,
270 name: "channel-a".to_string(),
271 depth: 0,
272 user_is_admin: true,
273 }],
274 );
275
276 // Client B no longer has access to the channel
277 assert_channels(client_b.channel_store(), cx_b, &[]);
278
279 // When disconnected, client A sees no channels.
280 server.forbid_connections();
281 server.disconnect_client(client_a.peer_id().unwrap());
282 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
283 assert_channels(client_a.channel_store(), cx_a, &[]);
284
285 server.allow_connections();
286 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
287 assert_channels(
288 client_a.channel_store(),
289 cx_a,
290 &[ExpectedChannel {
291 id: channel_a_id,
292 name: "channel-a".to_string(),
293 depth: 0,
294 user_is_admin: true,
295 }],
296 );
297}
298
299#[track_caller]
300fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
301 assert_eq!(
302 participants.iter().map(|p| p.id).collect::<Vec<_>>(),
303 expected_partitipants
304 );
305}
306
307#[track_caller]
308fn assert_members_eq(
309 members: &[ChannelMembership],
310 expected_members: &[(u64, bool, proto::channel_member::Kind)],
311) {
312 assert_eq!(
313 members
314 .iter()
315 .map(|member| (member.user.id, member.admin, member.kind))
316 .collect::<Vec<_>>(),
317 expected_members
318 );
319}
320
321#[gpui::test]
322async fn test_joining_channel_ancestor_member(
323 deterministic: Arc<Deterministic>,
324 cx_a: &mut TestAppContext,
325 cx_b: &mut TestAppContext,
326) {
327 deterministic.forbid_parking();
328 let mut server = TestServer::start(&deterministic).await;
329
330 let client_a = server.create_client(cx_a, "user_a").await;
331 let client_b = server.create_client(cx_b, "user_b").await;
332
333 let parent_id = server
334 .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
335 .await;
336
337 let sub_id = client_a
338 .channel_store()
339 .update(cx_a, |channel_store, cx| {
340 channel_store.create_channel("sub_channel", Some(parent_id), cx)
341 })
342 .await
343 .unwrap();
344
345 let active_call_b = cx_b.read(ActiveCall::global);
346
347 assert!(active_call_b
348 .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
349 .await
350 .is_ok());
351}
352
353#[gpui::test]
354async fn test_channel_room(
355 deterministic: Arc<Deterministic>,
356 cx_a: &mut TestAppContext,
357 cx_b: &mut TestAppContext,
358 cx_c: &mut TestAppContext,
359) {
360 deterministic.forbid_parking();
361 let mut server = TestServer::start(&deterministic).await;
362 let client_a = server.create_client(cx_a, "user_a").await;
363 let client_b = server.create_client(cx_b, "user_b").await;
364 let client_c = server.create_client(cx_c, "user_c").await;
365
366 let zed_id = server
367 .make_channel(
368 "zed",
369 None,
370 (&client_a, cx_a),
371 &mut [(&client_b, cx_b), (&client_c, cx_c)],
372 )
373 .await;
374
375 let active_call_a = cx_a.read(ActiveCall::global);
376 let active_call_b = cx_b.read(ActiveCall::global);
377
378 active_call_a
379 .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
380 .await
381 .unwrap();
382
383 // Give everyone a chance to observe user A joining
384 deterministic.run_until_parked();
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 println!("*******************************************");
1032 println!("********** STARTING LINK CHANNEL **********");
1033 println!("*******************************************");
1034 dbg!(client_b.user_id());
1035 client_b
1036 .channel_store()
1037 .update(cx_b, |channel_store, cx| {
1038 channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1039 })
1040 .await
1041 .unwrap();
1042
1043 // Current shape for B:
1044 // /---------\
1045 // /- ep -- b -- c -- d
1046 // mu -- ga
1047 assert_channels_list_shape(
1048 client_b.channel_store(),
1049 cx_b,
1050 &[
1051 (channel_mu_id, 0),
1052 (channel_ep_id, 1),
1053 (channel_b_id, 2),
1054 (channel_c_id, 3),
1055 (channel_d_id, 4),
1056 (channel_d_id, 3),
1057 (channel_ga_id, 1),
1058 ],
1059 );
1060
1061 // Current shape for C:
1062 // /---------\
1063 // ep -- b -- c -- d
1064 assert_channels_list_shape(
1065 client_c.channel_store(),
1066 cx_c,
1067 &[
1068 (channel_ep_id, 0),
1069 (channel_b_id, 1),
1070 (channel_c_id, 2),
1071 (channel_d_id, 3),
1072 (channel_d_id, 2),
1073 ],
1074 );
1075
1076 client_b
1077 .channel_store()
1078 .update(cx_b, |channel_store, cx| {
1079 channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1080 })
1081 .await
1082 .unwrap();
1083
1084 // Current shape for B:
1085 // /---------\
1086 // /- ep -- b -- c -- d
1087 // / \
1088 // mu ---------- ga
1089 assert_channels_list_shape(
1090 client_b.channel_store(),
1091 cx_b,
1092 &[
1093 (channel_mu_id, 0),
1094 (channel_ep_id, 1),
1095 (channel_b_id, 2),
1096 (channel_c_id, 3),
1097 (channel_d_id, 4),
1098 (channel_d_id, 3),
1099 (channel_ga_id, 3),
1100 (channel_ga_id, 1),
1101 ],
1102 );
1103
1104 // Current shape for A:
1105 // /------\
1106 // a - b -- c -- d
1107 // \-- ga
1108 assert_channels_list_shape(
1109 client_a.channel_store(),
1110 cx_a,
1111 &[
1112 (channel_a_id, 0),
1113 (channel_b_id, 1),
1114 (channel_c_id, 2),
1115 (channel_d_id, 3),
1116 (channel_d_id, 2),
1117 (channel_ga_id, 2),
1118 ],
1119 );
1120
1121 // Current shape for C:
1122 // /-------\
1123 // ep -- b -- c -- d
1124 // \-- ga
1125 assert_channels_list_shape(
1126 client_c.channel_store(),
1127 cx_c,
1128 &[
1129 (channel_ep_id, 0),
1130 (channel_b_id, 1),
1131 (channel_c_id, 2),
1132 (channel_d_id, 3),
1133 (channel_d_id, 2),
1134 (channel_ga_id, 2),
1135 ],
1136 );
1137}
1138
1139#[derive(Debug, PartialEq)]
1140struct ExpectedChannel {
1141 depth: usize,
1142 id: ChannelId,
1143 name: String,
1144 user_is_admin: bool,
1145}
1146
1147#[track_caller]
1148fn assert_channel_invitations(
1149 channel_store: &ModelHandle<ChannelStore>,
1150 cx: &TestAppContext,
1151 expected_channels: &[ExpectedChannel],
1152) {
1153 let actual = channel_store.read_with(cx, |store, _| {
1154 store
1155 .channel_invitations()
1156 .iter()
1157 .map(|channel| ExpectedChannel {
1158 depth: 0,
1159 name: channel.name.clone(),
1160 id: channel.id,
1161 user_is_admin: store.is_user_admin(channel.id),
1162 })
1163 .collect::<Vec<_>>()
1164 });
1165 assert_eq!(actual, expected_channels);
1166}
1167
1168#[track_caller]
1169fn assert_channels(
1170 channel_store: &ModelHandle<ChannelStore>,
1171 cx: &TestAppContext,
1172 expected_channels: &[ExpectedChannel],
1173) {
1174 let actual = channel_store.read_with(cx, |store, _| {
1175 store
1176 .channel_dag_entries()
1177 .map(|(depth, channel)| ExpectedChannel {
1178 depth,
1179 name: channel.name.clone(),
1180 id: channel.id,
1181 user_is_admin: store.is_user_admin(channel.id),
1182 })
1183 .collect::<Vec<_>>()
1184 });
1185 pretty_assertions::assert_eq!(actual, expected_channels);
1186}
1187
1188#[track_caller]
1189fn assert_channels_list_shape(
1190 channel_store: &ModelHandle<ChannelStore>,
1191 cx: &TestAppContext,
1192 expected_channels: &[(u64, usize)],
1193) {
1194 cx.foreground().run_until_parked();
1195
1196 let actual = channel_store.read_with(cx, |store, _| {
1197 store
1198 .channel_dag_entries()
1199 .map(|(depth, channel)| (channel.id, depth))
1200 .collect::<Vec<_>>()
1201 });
1202 pretty_assertions::assert_eq!(dbg!(actual), expected_channels);
1203}