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