1use collections::{HashMap, HashSet};
2use rpc::{
3 proto::{self},
4 ConnectionId,
5};
6
7use crate::{
8 db::{
9 queries::channels::ChannelGraph,
10 tests::{graph, TEST_RELEASE_CHANNEL},
11 ChannelId, ChannelRole, Database, NewUserParams, RoomId, UserId,
12 },
13 test_both_dbs,
14};
15use std::sync::{
16 atomic::{AtomicI32, Ordering},
17 Arc,
18};
19
20test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
21
22async fn test_channels(db: &Arc<Database>) {
23 let a_id = db
24 .create_user(
25 "user1@example.com",
26 false,
27 NewUserParams {
28 github_login: "user1".into(),
29 github_user_id: 5,
30 invite_count: 0,
31 },
32 )
33 .await
34 .unwrap()
35 .user_id;
36
37 let b_id = db
38 .create_user(
39 "user2@example.com",
40 false,
41 NewUserParams {
42 github_login: "user2".into(),
43 github_user_id: 6,
44 invite_count: 0,
45 },
46 )
47 .await
48 .unwrap()
49 .user_id;
50
51 let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
52
53 // Make sure that people cannot read channels they haven't been invited to
54 assert!(db.get_channel(zed_id, b_id).await.is_err());
55
56 db.invite_channel_member(zed_id, b_id, a_id, ChannelRole::Member)
57 .await
58 .unwrap();
59
60 db.respond_to_channel_invite(zed_id, b_id, true)
61 .await
62 .unwrap();
63
64 let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
65 let livestreaming_id = db
66 .create_channel("livestreaming", Some(zed_id), a_id)
67 .await
68 .unwrap();
69 let replace_id = db
70 .create_channel("replace", Some(zed_id), a_id)
71 .await
72 .unwrap();
73
74 let mut members = db.get_channel_members(replace_id).await.unwrap();
75 members.sort();
76 assert_eq!(members, &[a_id, b_id]);
77
78 let rust_id = db.create_root_channel("rust", a_id).await.unwrap();
79 let cargo_id = db
80 .create_channel("cargo", Some(rust_id), a_id)
81 .await
82 .unwrap();
83
84 let cargo_ra_id = db
85 .create_channel("cargo-ra", Some(cargo_id), a_id)
86 .await
87 .unwrap();
88
89 let result = db.get_channels_for_user(a_id).await.unwrap();
90 assert_eq!(
91 result.channels,
92 graph(
93 &[
94 (zed_id, "zed"),
95 (crdb_id, "crdb"),
96 (livestreaming_id, "livestreaming"),
97 (replace_id, "replace"),
98 (rust_id, "rust"),
99 (cargo_id, "cargo"),
100 (cargo_ra_id, "cargo-ra")
101 ],
102 &[
103 (crdb_id, zed_id),
104 (livestreaming_id, zed_id),
105 (replace_id, zed_id),
106 (cargo_id, rust_id),
107 (cargo_ra_id, cargo_id),
108 ]
109 )
110 );
111
112 let result = db.get_channels_for_user(b_id).await.unwrap();
113 assert_eq!(
114 result.channels,
115 graph(
116 &[
117 (zed_id, "zed"),
118 (crdb_id, "crdb"),
119 (livestreaming_id, "livestreaming"),
120 (replace_id, "replace")
121 ],
122 &[
123 (crdb_id, zed_id),
124 (livestreaming_id, zed_id),
125 (replace_id, zed_id)
126 ]
127 )
128 );
129
130 // Update member permissions
131 let set_subchannel_admin = db
132 .set_channel_member_role(crdb_id, a_id, b_id, ChannelRole::Admin)
133 .await;
134 assert!(set_subchannel_admin.is_err());
135 let set_channel_admin = db
136 .set_channel_member_role(zed_id, a_id, b_id, ChannelRole::Admin)
137 .await;
138 assert!(set_channel_admin.is_ok());
139
140 let result = db.get_channels_for_user(b_id).await.unwrap();
141 assert_eq!(
142 result.channels,
143 graph(
144 &[
145 (zed_id, "zed"),
146 (crdb_id, "crdb"),
147 (livestreaming_id, "livestreaming"),
148 (replace_id, "replace")
149 ],
150 &[
151 (crdb_id, zed_id),
152 (livestreaming_id, zed_id),
153 (replace_id, zed_id)
154 ]
155 )
156 );
157
158 // Remove a single channel
159 db.delete_channel(crdb_id, a_id).await.unwrap();
160 assert!(db.get_channel(crdb_id, a_id).await.is_err());
161
162 // Remove a channel tree
163 let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
164 channel_ids.sort();
165 assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
166 assert_eq!(user_ids, &[a_id]);
167
168 assert!(db.get_channel(rust_id, a_id).await.is_err());
169 assert!(db.get_channel(cargo_id, a_id).await.is_err());
170 assert!(db.get_channel(cargo_ra_id, a_id).await.is_err());
171}
172
173test_both_dbs!(
174 test_joining_channels,
175 test_joining_channels_postgres,
176 test_joining_channels_sqlite
177);
178
179async fn test_joining_channels(db: &Arc<Database>) {
180 let owner_id = db.create_server("test").await.unwrap().0 as u32;
181
182 let user_1 = db
183 .create_user(
184 "user1@example.com",
185 false,
186 NewUserParams {
187 github_login: "user1".into(),
188 github_user_id: 5,
189 invite_count: 0,
190 },
191 )
192 .await
193 .unwrap()
194 .user_id;
195 let user_2 = db
196 .create_user(
197 "user2@example.com",
198 false,
199 NewUserParams {
200 github_login: "user2".into(),
201 github_user_id: 6,
202 invite_count: 0,
203 },
204 )
205 .await
206 .unwrap()
207 .user_id;
208
209 let channel_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
210
211 // can join a room with membership to its channel
212 let (joined_room, _) = db
213 .join_channel(
214 channel_1,
215 user_1,
216 ConnectionId { owner_id, id: 1 },
217 TEST_RELEASE_CHANNEL,
218 )
219 .await
220 .unwrap();
221 assert_eq!(joined_room.room.participants.len(), 1);
222
223 let room_id = RoomId::from_proto(joined_room.room.id);
224 drop(joined_room);
225 // cannot join a room without membership to its channel
226 assert!(db
227 .join_room(
228 room_id,
229 user_2,
230 ConnectionId { owner_id, id: 1 },
231 TEST_RELEASE_CHANNEL
232 )
233 .await
234 .is_err());
235}
236
237test_both_dbs!(
238 test_channel_invites,
239 test_channel_invites_postgres,
240 test_channel_invites_sqlite
241);
242
243async fn test_channel_invites(db: &Arc<Database>) {
244 db.create_server("test").await.unwrap();
245
246 let user_1 = new_test_user(db, "user1@example.com").await;
247 let user_2 = new_test_user(db, "user2@example.com").await;
248 let user_3 = new_test_user(db, "user3@example.com").await;
249
250 let channel_1_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
251
252 let channel_1_2 = db.create_root_channel("channel_2", user_1).await.unwrap();
253
254 db.invite_channel_member(channel_1_1, user_2, user_1, ChannelRole::Member)
255 .await
256 .unwrap();
257 db.invite_channel_member(channel_1_2, user_2, user_1, ChannelRole::Member)
258 .await
259 .unwrap();
260 db.invite_channel_member(channel_1_1, user_3, user_1, ChannelRole::Admin)
261 .await
262 .unwrap();
263
264 let user_2_invites = db
265 .get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
266 .await
267 .unwrap()
268 .into_iter()
269 .map(|channel| channel.id)
270 .collect::<Vec<_>>();
271
272 assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
273
274 let user_3_invites = db
275 .get_channel_invites_for_user(user_3) // -> [channel_1_1]
276 .await
277 .unwrap()
278 .into_iter()
279 .map(|channel| channel.id)
280 .collect::<Vec<_>>();
281
282 assert_eq!(user_3_invites, &[channel_1_1]);
283
284 let mut members = db
285 .get_channel_participant_details(channel_1_1, user_1)
286 .await
287 .unwrap();
288
289 members.sort_by_key(|member| member.user_id);
290 assert_eq!(
291 members,
292 &[
293 proto::ChannelMember {
294 user_id: user_1.to_proto(),
295 kind: proto::channel_member::Kind::Member.into(),
296 role: proto::ChannelRole::Admin.into(),
297 },
298 proto::ChannelMember {
299 user_id: user_2.to_proto(),
300 kind: proto::channel_member::Kind::Invitee.into(),
301 role: proto::ChannelRole::Member.into(),
302 },
303 proto::ChannelMember {
304 user_id: user_3.to_proto(),
305 kind: proto::channel_member::Kind::Invitee.into(),
306 role: proto::ChannelRole::Admin.into(),
307 },
308 ]
309 );
310
311 db.respond_to_channel_invite(channel_1_1, user_2, true)
312 .await
313 .unwrap();
314
315 let channel_1_3 = db
316 .create_channel("channel_3", Some(channel_1_1), user_1)
317 .await
318 .unwrap();
319
320 let members = db
321 .get_channel_participant_details(channel_1_3, user_1)
322 .await
323 .unwrap();
324 assert_eq!(
325 members,
326 &[
327 proto::ChannelMember {
328 user_id: user_1.to_proto(),
329 kind: proto::channel_member::Kind::Member.into(),
330 role: proto::ChannelRole::Admin.into(),
331 },
332 proto::ChannelMember {
333 user_id: user_2.to_proto(),
334 kind: proto::channel_member::Kind::AncestorMember.into(),
335 role: proto::ChannelRole::Member.into(),
336 },
337 ]
338 );
339}
340
341test_both_dbs!(
342 test_channel_renames,
343 test_channel_renames_postgres,
344 test_channel_renames_sqlite
345);
346
347async fn test_channel_renames(db: &Arc<Database>) {
348 db.create_server("test").await.unwrap();
349
350 let user_1 = db
351 .create_user(
352 "user1@example.com",
353 false,
354 NewUserParams {
355 github_login: "user1".into(),
356 github_user_id: 5,
357 invite_count: 0,
358 },
359 )
360 .await
361 .unwrap()
362 .user_id;
363
364 let user_2 = db
365 .create_user(
366 "user2@example.com",
367 false,
368 NewUserParams {
369 github_login: "user2".into(),
370 github_user_id: 6,
371 invite_count: 0,
372 },
373 )
374 .await
375 .unwrap()
376 .user_id;
377
378 let zed_id = db.create_root_channel("zed", user_1).await.unwrap();
379
380 db.rename_channel(zed_id, user_1, "#zed-archive")
381 .await
382 .unwrap();
383
384 let zed_archive_id = zed_id;
385
386 let channel = db.get_channel(zed_archive_id, user_1).await.unwrap();
387 assert_eq!(channel.name, "zed-archive");
388
389 let non_permissioned_rename = db
390 .rename_channel(zed_archive_id, user_2, "hacked-lol")
391 .await;
392 assert!(non_permissioned_rename.is_err());
393
394 let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
395 assert!(bad_name_rename.is_err())
396}
397
398test_both_dbs!(
399 test_db_channel_moving,
400 test_channels_moving_postgres,
401 test_channels_moving_sqlite
402);
403
404async fn test_db_channel_moving(db: &Arc<Database>) {
405 let a_id = db
406 .create_user(
407 "user1@example.com",
408 false,
409 NewUserParams {
410 github_login: "user1".into(),
411 github_user_id: 5,
412 invite_count: 0,
413 },
414 )
415 .await
416 .unwrap()
417 .user_id;
418
419 let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
420
421 let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
422
423 let gpui2_id = db
424 .create_channel("gpui2", Some(zed_id), a_id)
425 .await
426 .unwrap();
427
428 let livestreaming_id = db
429 .create_channel("livestreaming", Some(crdb_id), a_id)
430 .await
431 .unwrap();
432
433 let livestreaming_dag_id = db
434 .create_channel("livestreaming_dag", Some(livestreaming_id), a_id)
435 .await
436 .unwrap();
437
438 // ========================================================================
439 // sanity check
440 // Initial DAG:
441 // /- gpui2
442 // zed -- crdb - livestreaming - livestreaming_dag
443 let result = db.get_channels_for_user(a_id).await.unwrap();
444 assert_dag(
445 result.channels,
446 &[
447 (zed_id, None),
448 (crdb_id, Some(zed_id)),
449 (gpui2_id, Some(zed_id)),
450 (livestreaming_id, Some(crdb_id)),
451 (livestreaming_dag_id, Some(livestreaming_id)),
452 ],
453 );
454
455 // Attempt to make a cycle
456 assert!(db
457 .link_channel(a_id, zed_id, livestreaming_id)
458 .await
459 .is_err());
460
461 // ========================================================================
462 // Make a link
463 db.link_channel(a_id, livestreaming_id, zed_id)
464 .await
465 .unwrap();
466
467 // DAG is now:
468 // /- gpui2
469 // zed -- crdb - livestreaming - livestreaming_dag
470 // \---------/
471 let result = db.get_channels_for_user(a_id).await.unwrap();
472 assert_dag(
473 result.channels,
474 &[
475 (zed_id, None),
476 (crdb_id, Some(zed_id)),
477 (gpui2_id, Some(zed_id)),
478 (livestreaming_id, Some(zed_id)),
479 (livestreaming_id, Some(crdb_id)),
480 (livestreaming_dag_id, Some(livestreaming_id)),
481 ],
482 );
483
484 // ========================================================================
485 // Create a new channel below a channel with multiple parents
486 let livestreaming_dag_sub_id = db
487 .create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
488 .await
489 .unwrap();
490
491 // DAG is now:
492 // /- gpui2
493 // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
494 // \---------/
495 let result = db.get_channels_for_user(a_id).await.unwrap();
496 assert_dag(
497 result.channels,
498 &[
499 (zed_id, None),
500 (crdb_id, Some(zed_id)),
501 (gpui2_id, Some(zed_id)),
502 (livestreaming_id, Some(zed_id)),
503 (livestreaming_id, Some(crdb_id)),
504 (livestreaming_dag_id, Some(livestreaming_id)),
505 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
506 ],
507 );
508
509 // ========================================================================
510 // Test a complex DAG by making another link
511 let returned_channels = db
512 .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
513 .await
514 .unwrap();
515
516 // DAG is now:
517 // /- gpui2 /---------------------\
518 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
519 // \--------/
520
521 // make sure we're getting just the new link
522 // Not using the assert_dag helper because we want to make sure we're returning the full data
523 pretty_assertions::assert_eq!(
524 returned_channels,
525 graph(
526 &[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
527 &[(livestreaming_dag_sub_id, livestreaming_id)]
528 )
529 );
530
531 let result = db.get_channels_for_user(a_id).await.unwrap();
532 assert_dag(
533 result.channels,
534 &[
535 (zed_id, None),
536 (crdb_id, Some(zed_id)),
537 (gpui2_id, Some(zed_id)),
538 (livestreaming_id, Some(zed_id)),
539 (livestreaming_id, Some(crdb_id)),
540 (livestreaming_dag_id, Some(livestreaming_id)),
541 (livestreaming_dag_sub_id, Some(livestreaming_id)),
542 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
543 ],
544 );
545
546 // ========================================================================
547 // Test a complex DAG by making another link
548 let returned_channels = db
549 .link_channel(a_id, livestreaming_id, gpui2_id)
550 .await
551 .unwrap();
552
553 // DAG is now:
554 // /- gpui2 -\ /---------------------\
555 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
556 // \---------/
557
558 // Make sure that we're correctly getting the full sub-dag
559 pretty_assertions::assert_eq!(
560 returned_channels,
561 graph(
562 &[
563 (livestreaming_id, "livestreaming"),
564 (livestreaming_dag_id, "livestreaming_dag"),
565 (livestreaming_dag_sub_id, "livestreaming_dag_sub"),
566 ],
567 &[
568 (livestreaming_id, gpui2_id),
569 (livestreaming_dag_id, livestreaming_id),
570 (livestreaming_dag_sub_id, livestreaming_id),
571 (livestreaming_dag_sub_id, livestreaming_dag_id),
572 ]
573 )
574 );
575
576 let result = db.get_channels_for_user(a_id).await.unwrap();
577 assert_dag(
578 result.channels,
579 &[
580 (zed_id, None),
581 (crdb_id, Some(zed_id)),
582 (gpui2_id, Some(zed_id)),
583 (livestreaming_id, Some(zed_id)),
584 (livestreaming_id, Some(crdb_id)),
585 (livestreaming_id, Some(gpui2_id)),
586 (livestreaming_dag_id, Some(livestreaming_id)),
587 (livestreaming_dag_sub_id, Some(livestreaming_id)),
588 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
589 ],
590 );
591
592 // ========================================================================
593 // Test unlinking in a complex DAG by removing the inner link
594 db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
595 .await
596 .unwrap();
597
598 // DAG is now:
599 // /- gpui2 -\
600 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
601 // \---------/
602
603 let result = db.get_channels_for_user(a_id).await.unwrap();
604 assert_dag(
605 result.channels,
606 &[
607 (zed_id, None),
608 (crdb_id, Some(zed_id)),
609 (gpui2_id, Some(zed_id)),
610 (livestreaming_id, Some(gpui2_id)),
611 (livestreaming_id, Some(zed_id)),
612 (livestreaming_id, Some(crdb_id)),
613 (livestreaming_dag_id, Some(livestreaming_id)),
614 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
615 ],
616 );
617
618 // ========================================================================
619 // Test unlinking in a complex DAG by removing the inner link
620 db.unlink_channel(a_id, livestreaming_id, gpui2_id)
621 .await
622 .unwrap();
623
624 // DAG is now:
625 // /- gpui2
626 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
627 // \---------/
628 let result = db.get_channels_for_user(a_id).await.unwrap();
629 assert_dag(
630 result.channels,
631 &[
632 (zed_id, None),
633 (crdb_id, Some(zed_id)),
634 (gpui2_id, Some(zed_id)),
635 (livestreaming_id, Some(zed_id)),
636 (livestreaming_id, Some(crdb_id)),
637 (livestreaming_dag_id, Some(livestreaming_id)),
638 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
639 ],
640 );
641
642 // ========================================================================
643 // Test moving DAG nodes by moving livestreaming to be below gpui2
644 db.move_channel(a_id, livestreaming_id, crdb_id, gpui2_id)
645 .await
646 .unwrap();
647
648 // DAG is now:
649 // /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
650 // zed - crdb /
651 // \---------/
652 let result = db.get_channels_for_user(a_id).await.unwrap();
653 assert_dag(
654 result.channels,
655 &[
656 (zed_id, None),
657 (crdb_id, Some(zed_id)),
658 (gpui2_id, Some(zed_id)),
659 (livestreaming_id, Some(zed_id)),
660 (livestreaming_id, Some(gpui2_id)),
661 (livestreaming_dag_id, Some(livestreaming_id)),
662 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
663 ],
664 );
665
666 // ========================================================================
667 // Deleting a channel should not delete children that still have other parents
668 db.delete_channel(gpui2_id, a_id).await.unwrap();
669
670 // DAG is now:
671 // zed - crdb
672 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
673 let result = db.get_channels_for_user(a_id).await.unwrap();
674 assert_dag(
675 result.channels,
676 &[
677 (zed_id, None),
678 (crdb_id, Some(zed_id)),
679 (livestreaming_id, Some(zed_id)),
680 (livestreaming_dag_id, Some(livestreaming_id)),
681 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
682 ],
683 );
684
685 // ========================================================================
686 // Unlinking a channel from it's parent should automatically promote it to a root channel
687 db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
688
689 // DAG is now:
690 // crdb
691 // zed
692 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
693
694 let result = db.get_channels_for_user(a_id).await.unwrap();
695 assert_dag(
696 result.channels,
697 &[
698 (zed_id, None),
699 (crdb_id, None),
700 (livestreaming_id, Some(zed_id)),
701 (livestreaming_dag_id, Some(livestreaming_id)),
702 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
703 ],
704 );
705
706 // ========================================================================
707 // You should be able to move a root channel into a non-root channel
708 db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
709
710 // DAG is now:
711 // zed - crdb
712 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
713
714 let result = db.get_channels_for_user(a_id).await.unwrap();
715 assert_dag(
716 result.channels,
717 &[
718 (zed_id, None),
719 (crdb_id, Some(zed_id)),
720 (livestreaming_id, Some(zed_id)),
721 (livestreaming_dag_id, Some(livestreaming_id)),
722 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
723 ],
724 );
725
726 // ========================================================================
727 // Prep for DAG deletion test
728 db.link_channel(a_id, livestreaming_id, crdb_id)
729 .await
730 .unwrap();
731
732 // DAG is now:
733 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
734 // \--------/
735
736 let result = db.get_channels_for_user(a_id).await.unwrap();
737 assert_dag(
738 result.channels,
739 &[
740 (zed_id, None),
741 (crdb_id, Some(zed_id)),
742 (livestreaming_id, Some(zed_id)),
743 (livestreaming_id, Some(crdb_id)),
744 (livestreaming_dag_id, Some(livestreaming_id)),
745 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
746 ],
747 );
748
749 // Deleting the parent of a DAG should delete the whole DAG:
750 db.delete_channel(zed_id, a_id).await.unwrap();
751 let result = db.get_channels_for_user(a_id).await.unwrap();
752
753 assert!(result.channels.is_empty())
754}
755
756test_both_dbs!(
757 test_db_channel_moving_bugs,
758 test_db_channel_moving_bugs_postgres,
759 test_db_channel_moving_bugs_sqlite
760);
761
762async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
763 let user_id = db
764 .create_user(
765 "user1@example.com",
766 false,
767 NewUserParams {
768 github_login: "user1".into(),
769 github_user_id: 5,
770 invite_count: 0,
771 },
772 )
773 .await
774 .unwrap()
775 .user_id;
776
777 let zed_id = db.create_root_channel("zed", user_id).await.unwrap();
778
779 let projects_id = db
780 .create_channel("projects", Some(zed_id), user_id)
781 .await
782 .unwrap();
783
784 let livestreaming_id = db
785 .create_channel("livestreaming", Some(projects_id), user_id)
786 .await
787 .unwrap();
788
789 // Dag is: zed - projects - livestreaming
790
791 // Move to same parent should be a no-op
792 assert!(db
793 .move_channel(user_id, projects_id, zed_id, zed_id)
794 .await
795 .unwrap()
796 .is_empty());
797
798 // Stranding a channel should retain it's sub channels
799 db.unlink_channel(user_id, projects_id, zed_id)
800 .await
801 .unwrap();
802
803 let result = db.get_channels_for_user(user_id).await.unwrap();
804 assert_dag(
805 result.channels,
806 &[
807 (zed_id, None),
808 (projects_id, None),
809 (livestreaming_id, Some(projects_id)),
810 ],
811 );
812}
813
814test_both_dbs!(
815 test_user_is_channel_participant,
816 test_user_is_channel_participant_postgres,
817 test_user_is_channel_participant_sqlite
818);
819
820async fn test_user_is_channel_participant(db: &Arc<Database>) {
821 let admin = new_test_user(db, "admin@example.com").await;
822 let member = new_test_user(db, "member@example.com").await;
823 let guest = new_test_user(db, "guest@example.com").await;
824
825 let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
826 let active_channel = db
827 .create_channel("active", Some(zed_channel), admin)
828 .await
829 .unwrap();
830 let vim_channel = db
831 .create_channel("vim", Some(active_channel), admin)
832 .await
833 .unwrap();
834
835 db.set_channel_visibility(vim_channel, crate::db::ChannelVisibility::Public, admin)
836 .await
837 .unwrap();
838 db.invite_channel_member(active_channel, member, admin, ChannelRole::Member)
839 .await
840 .unwrap();
841 db.invite_channel_member(vim_channel, guest, admin, ChannelRole::Guest)
842 .await
843 .unwrap();
844
845 db.respond_to_channel_invite(active_channel, member, true)
846 .await
847 .unwrap();
848
849 db.transaction(|tx| async move {
850 db.check_user_is_channel_participant(vim_channel, admin, &*tx)
851 .await
852 })
853 .await
854 .unwrap();
855 db.transaction(|tx| async move {
856 db.check_user_is_channel_participant(vim_channel, member, &*tx)
857 .await
858 })
859 .await
860 .unwrap();
861
862 let mut members = db
863 .get_channel_participant_details(vim_channel, admin)
864 .await
865 .unwrap();
866
867 members.sort_by_key(|member| member.user_id);
868
869 assert_eq!(
870 members,
871 &[
872 proto::ChannelMember {
873 user_id: admin.to_proto(),
874 kind: proto::channel_member::Kind::Member.into(),
875 role: proto::ChannelRole::Admin.into(),
876 },
877 proto::ChannelMember {
878 user_id: member.to_proto(),
879 kind: proto::channel_member::Kind::AncestorMember.into(),
880 role: proto::ChannelRole::Member.into(),
881 },
882 proto::ChannelMember {
883 user_id: guest.to_proto(),
884 kind: proto::channel_member::Kind::Invitee.into(),
885 role: proto::ChannelRole::Guest.into(),
886 },
887 ]
888 );
889
890 db.respond_to_channel_invite(vim_channel, guest, true)
891 .await
892 .unwrap();
893
894 db.transaction(|tx| async move {
895 db.check_user_is_channel_participant(vim_channel, guest, &*tx)
896 .await
897 })
898 .await
899 .unwrap();
900
901 let channels = db.get_channels_for_user(guest).await.unwrap().channels;
902 assert_dag(channels, &[(vim_channel, None)]);
903 let channels = db.get_channels_for_user(member).await.unwrap().channels;
904 assert_dag(
905 channels,
906 &[(active_channel, None), (vim_channel, Some(active_channel))],
907 );
908
909 db.set_channel_member_role(vim_channel, admin, guest, ChannelRole::Banned)
910 .await
911 .unwrap();
912 assert!(db
913 .transaction(|tx| async move {
914 db.check_user_is_channel_participant(vim_channel, guest, &*tx)
915 .await
916 })
917 .await
918 .is_err());
919
920 let mut members = db
921 .get_channel_participant_details(vim_channel, admin)
922 .await
923 .unwrap();
924
925 members.sort_by_key(|member| member.user_id);
926
927 assert_eq!(
928 members,
929 &[
930 proto::ChannelMember {
931 user_id: admin.to_proto(),
932 kind: proto::channel_member::Kind::Member.into(),
933 role: proto::ChannelRole::Admin.into(),
934 },
935 proto::ChannelMember {
936 user_id: member.to_proto(),
937 kind: proto::channel_member::Kind::AncestorMember.into(),
938 role: proto::ChannelRole::Member.into(),
939 },
940 proto::ChannelMember {
941 user_id: guest.to_proto(),
942 kind: proto::channel_member::Kind::Member.into(),
943 role: proto::ChannelRole::Banned.into(),
944 },
945 ]
946 );
947
948 db.remove_channel_member(vim_channel, guest, admin)
949 .await
950 .unwrap();
951
952 db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
953 .await
954 .unwrap();
955
956 db.invite_channel_member(zed_channel, guest, admin, ChannelRole::Guest)
957 .await
958 .unwrap();
959
960 // currently people invited to parent channels are not shown here
961 let mut members = db
962 .get_channel_participant_details(vim_channel, admin)
963 .await
964 .unwrap();
965
966 members.sort_by_key(|member| member.user_id);
967
968 assert_eq!(
969 members,
970 &[
971 proto::ChannelMember {
972 user_id: admin.to_proto(),
973 kind: proto::channel_member::Kind::Member.into(),
974 role: proto::ChannelRole::Admin.into(),
975 },
976 proto::ChannelMember {
977 user_id: member.to_proto(),
978 kind: proto::channel_member::Kind::AncestorMember.into(),
979 role: proto::ChannelRole::Member.into(),
980 },
981 ]
982 );
983
984 db.respond_to_channel_invite(zed_channel, guest, true)
985 .await
986 .unwrap();
987
988 db.transaction(|tx| async move {
989 db.check_user_is_channel_participant(zed_channel, guest, &*tx)
990 .await
991 })
992 .await
993 .unwrap();
994 assert!(db
995 .transaction(|tx| async move {
996 db.check_user_is_channel_participant(active_channel, guest, &*tx)
997 .await
998 })
999 .await
1000 .is_err(),);
1001
1002 db.transaction(|tx| async move {
1003 db.check_user_is_channel_participant(vim_channel, guest, &*tx)
1004 .await
1005 })
1006 .await
1007 .unwrap();
1008
1009 let mut members = db
1010 .get_channel_participant_details(vim_channel, admin)
1011 .await
1012 .unwrap();
1013
1014 members.sort_by_key(|member| member.user_id);
1015
1016 assert_eq!(
1017 members,
1018 &[
1019 proto::ChannelMember {
1020 user_id: admin.to_proto(),
1021 kind: proto::channel_member::Kind::Member.into(),
1022 role: proto::ChannelRole::Admin.into(),
1023 },
1024 proto::ChannelMember {
1025 user_id: member.to_proto(),
1026 kind: proto::channel_member::Kind::AncestorMember.into(),
1027 role: proto::ChannelRole::Member.into(),
1028 },
1029 proto::ChannelMember {
1030 user_id: guest.to_proto(),
1031 kind: proto::channel_member::Kind::AncestorMember.into(),
1032 role: proto::ChannelRole::Guest.into(),
1033 },
1034 ]
1035 );
1036
1037 let channels = db.get_channels_for_user(guest).await.unwrap().channels;
1038 assert_dag(
1039 channels,
1040 &[(zed_channel, None), (vim_channel, Some(zed_channel))],
1041 )
1042}
1043
1044test_both_dbs!(
1045 test_user_joins_correct_channel,
1046 test_user_joins_correct_channel_postgres,
1047 test_user_joins_correct_channel_sqlite
1048);
1049
1050async fn test_user_joins_correct_channel(db: &Arc<Database>) {
1051 let admin = new_test_user(db, "admin@example.com").await;
1052
1053 let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
1054
1055 let active_channel = db
1056 .create_channel("active", Some(zed_channel), admin)
1057 .await
1058 .unwrap();
1059
1060 let vim_channel = db
1061 .create_channel("vim", Some(active_channel), admin)
1062 .await
1063 .unwrap();
1064
1065 let vim2_channel = db
1066 .create_channel("vim2", Some(vim_channel), admin)
1067 .await
1068 .unwrap();
1069
1070 db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
1071 .await
1072 .unwrap();
1073
1074 db.set_channel_visibility(vim_channel, crate::db::ChannelVisibility::Public, admin)
1075 .await
1076 .unwrap();
1077
1078 db.set_channel_visibility(vim2_channel, crate::db::ChannelVisibility::Public, admin)
1079 .await
1080 .unwrap();
1081
1082 let most_public = db
1083 .transaction(
1084 |tx| async move { db.most_public_ancestor_for_channel(vim_channel, &*tx).await },
1085 )
1086 .await
1087 .unwrap();
1088
1089 assert_eq!(most_public, Some(zed_channel))
1090}
1091
1092#[track_caller]
1093fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
1094 let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
1095 for channel in actual.channels {
1096 actual_map.insert(channel.id, HashSet::default());
1097 }
1098 for edge in actual.edges {
1099 actual_map
1100 .get_mut(&ChannelId::from_proto(edge.channel_id))
1101 .unwrap()
1102 .insert(ChannelId::from_proto(edge.parent_id));
1103 }
1104
1105 let mut expected_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
1106
1107 for (child, parent) in expected {
1108 let entry = expected_map.entry(*child).or_default();
1109 if let Some(parent) = parent {
1110 entry.insert(*parent);
1111 }
1112 }
1113
1114 pretty_assertions::assert_eq!(actual_map, expected_map)
1115}
1116
1117static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
1118
1119async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
1120 db.create_user(
1121 email,
1122 false,
1123 NewUserParams {
1124 github_login: email[0..email.find("@").unwrap()].to_string(),
1125 github_user_id: GITHUB_USER_ID.fetch_add(1, Ordering::SeqCst),
1126 invite_count: 0,
1127 },
1128 )
1129 .await
1130 .unwrap()
1131 .user_id
1132}