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, 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.unwrap().is_none());
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.unwrap().is_none());
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.unwrap().is_none());
169 assert!(db.get_channel(cargo_id, a_id).await.unwrap().is_none());
170 assert!(db.get_channel(cargo_ra_id, a_id).await.unwrap().is_none());
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 let room_1 = db
211 .get_or_create_channel_room(channel_1, "1", TEST_RELEASE_CHANNEL)
212 .await
213 .unwrap();
214
215 // can join a room with membership to its channel
216 let joined_room = db
217 .join_room(
218 room_1,
219 user_1,
220 ConnectionId { owner_id, id: 1 },
221 TEST_RELEASE_CHANNEL,
222 )
223 .await
224 .unwrap();
225 assert_eq!(joined_room.room.participants.len(), 1);
226
227 drop(joined_room);
228 // cannot join a room without membership to its channel
229 assert!(db
230 .join_room(
231 room_1,
232 user_2,
233 ConnectionId { owner_id, id: 1 },
234 TEST_RELEASE_CHANNEL
235 )
236 .await
237 .is_err());
238}
239
240test_both_dbs!(
241 test_channel_invites,
242 test_channel_invites_postgres,
243 test_channel_invites_sqlite
244);
245
246async fn test_channel_invites(db: &Arc<Database>) {
247 db.create_server("test").await.unwrap();
248
249 let user_1 = db
250 .create_user(
251 "user1@example.com",
252 false,
253 NewUserParams {
254 github_login: "user1".into(),
255 github_user_id: 5,
256 invite_count: 0,
257 },
258 )
259 .await
260 .unwrap()
261 .user_id;
262 let user_2 = db
263 .create_user(
264 "user2@example.com",
265 false,
266 NewUserParams {
267 github_login: "user2".into(),
268 github_user_id: 6,
269 invite_count: 0,
270 },
271 )
272 .await
273 .unwrap()
274 .user_id;
275
276 let user_3 = db
277 .create_user(
278 "user3@example.com",
279 false,
280 NewUserParams {
281 github_login: "user3".into(),
282 github_user_id: 7,
283 invite_count: 0,
284 },
285 )
286 .await
287 .unwrap()
288 .user_id;
289
290 let channel_1_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
291
292 let channel_1_2 = db.create_root_channel("channel_2", user_1).await.unwrap();
293
294 db.invite_channel_member(channel_1_1, user_2, user_1, ChannelRole::Member)
295 .await
296 .unwrap();
297 db.invite_channel_member(channel_1_2, user_2, user_1, ChannelRole::Member)
298 .await
299 .unwrap();
300 db.invite_channel_member(channel_1_1, user_3, user_1, ChannelRole::Admin)
301 .await
302 .unwrap();
303
304 let user_2_invites = db
305 .get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
306 .await
307 .unwrap()
308 .into_iter()
309 .map(|channel| channel.id)
310 .collect::<Vec<_>>();
311
312 assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
313
314 let user_3_invites = db
315 .get_channel_invites_for_user(user_3) // -> [channel_1_1]
316 .await
317 .unwrap()
318 .into_iter()
319 .map(|channel| channel.id)
320 .collect::<Vec<_>>();
321
322 assert_eq!(user_3_invites, &[channel_1_1]);
323
324 let members = db
325 .get_channel_member_details(channel_1_1, user_1)
326 .await
327 .unwrap();
328 assert_eq!(
329 members,
330 &[
331 proto::ChannelMember {
332 user_id: user_1.to_proto(),
333 kind: proto::channel_member::Kind::Member.into(),
334 role: proto::ChannelRole::Admin.into(),
335 },
336 proto::ChannelMember {
337 user_id: user_2.to_proto(),
338 kind: proto::channel_member::Kind::Invitee.into(),
339 role: proto::ChannelRole::Member.into(),
340 },
341 proto::ChannelMember {
342 user_id: user_3.to_proto(),
343 kind: proto::channel_member::Kind::Invitee.into(),
344 role: proto::ChannelRole::Admin.into(),
345 },
346 ]
347 );
348
349 db.respond_to_channel_invite(channel_1_1, user_2, true)
350 .await
351 .unwrap();
352
353 let channel_1_3 = db
354 .create_channel("channel_3", Some(channel_1_1), user_1)
355 .await
356 .unwrap();
357
358 let members = db
359 .get_channel_member_details(channel_1_3, user_1)
360 .await
361 .unwrap();
362 assert_eq!(
363 members,
364 &[
365 proto::ChannelMember {
366 user_id: user_1.to_proto(),
367 kind: proto::channel_member::Kind::Member.into(),
368 role: proto::ChannelRole::Admin.into(),
369 },
370 proto::ChannelMember {
371 user_id: user_2.to_proto(),
372 kind: proto::channel_member::Kind::AncestorMember.into(),
373 role: proto::ChannelRole::Member.into(),
374 },
375 ]
376 );
377}
378
379test_both_dbs!(
380 test_channel_renames,
381 test_channel_renames_postgres,
382 test_channel_renames_sqlite
383);
384
385async fn test_channel_renames(db: &Arc<Database>) {
386 db.create_server("test").await.unwrap();
387
388 let user_1 = db
389 .create_user(
390 "user1@example.com",
391 false,
392 NewUserParams {
393 github_login: "user1".into(),
394 github_user_id: 5,
395 invite_count: 0,
396 },
397 )
398 .await
399 .unwrap()
400 .user_id;
401
402 let user_2 = db
403 .create_user(
404 "user2@example.com",
405 false,
406 NewUserParams {
407 github_login: "user2".into(),
408 github_user_id: 6,
409 invite_count: 0,
410 },
411 )
412 .await
413 .unwrap()
414 .user_id;
415
416 let zed_id = db.create_root_channel("zed", user_1).await.unwrap();
417
418 db.rename_channel(zed_id, user_1, "#zed-archive")
419 .await
420 .unwrap();
421
422 let zed_archive_id = zed_id;
423
424 let (channel, _) = db
425 .get_channel(zed_archive_id, user_1)
426 .await
427 .unwrap()
428 .unwrap();
429 assert_eq!(channel.name, "zed-archive");
430
431 let non_permissioned_rename = db
432 .rename_channel(zed_archive_id, user_2, "hacked-lol")
433 .await;
434 assert!(non_permissioned_rename.is_err());
435
436 let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
437 assert!(bad_name_rename.is_err())
438}
439
440test_both_dbs!(
441 test_db_channel_moving,
442 test_channels_moving_postgres,
443 test_channels_moving_sqlite
444);
445
446async fn test_db_channel_moving(db: &Arc<Database>) {
447 let a_id = db
448 .create_user(
449 "user1@example.com",
450 false,
451 NewUserParams {
452 github_login: "user1".into(),
453 github_user_id: 5,
454 invite_count: 0,
455 },
456 )
457 .await
458 .unwrap()
459 .user_id;
460
461 let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
462
463 let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
464
465 let gpui2_id = db
466 .create_channel("gpui2", Some(zed_id), a_id)
467 .await
468 .unwrap();
469
470 let livestreaming_id = db
471 .create_channel("livestreaming", Some(crdb_id), a_id)
472 .await
473 .unwrap();
474
475 let livestreaming_dag_id = db
476 .create_channel("livestreaming_dag", Some(livestreaming_id), a_id)
477 .await
478 .unwrap();
479
480 // ========================================================================
481 // sanity check
482 // Initial DAG:
483 // /- gpui2
484 // zed -- crdb - livestreaming - livestreaming_dag
485 let result = db.get_channels_for_user(a_id).await.unwrap();
486 assert_dag(
487 result.channels,
488 &[
489 (zed_id, None),
490 (crdb_id, Some(zed_id)),
491 (gpui2_id, Some(zed_id)),
492 (livestreaming_id, Some(crdb_id)),
493 (livestreaming_dag_id, Some(livestreaming_id)),
494 ],
495 );
496
497 // Attempt to make a cycle
498 assert!(db
499 .link_channel(a_id, zed_id, livestreaming_id)
500 .await
501 .is_err());
502
503 // ========================================================================
504 // Make a link
505 db.link_channel(a_id, livestreaming_id, zed_id)
506 .await
507 .unwrap();
508
509 // DAG is now:
510 // /- gpui2
511 // zed -- crdb - livestreaming - livestreaming_dag
512 // \---------/
513 let result = db.get_channels_for_user(a_id).await.unwrap();
514 assert_dag(
515 result.channels,
516 &[
517 (zed_id, None),
518 (crdb_id, Some(zed_id)),
519 (gpui2_id, Some(zed_id)),
520 (livestreaming_id, Some(zed_id)),
521 (livestreaming_id, Some(crdb_id)),
522 (livestreaming_dag_id, Some(livestreaming_id)),
523 ],
524 );
525
526 // ========================================================================
527 // Create a new channel below a channel with multiple parents
528 let livestreaming_dag_sub_id = db
529 .create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
530 .await
531 .unwrap();
532
533 // DAG is now:
534 // /- gpui2
535 // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
536 // \---------/
537 let result = db.get_channels_for_user(a_id).await.unwrap();
538 assert_dag(
539 result.channels,
540 &[
541 (zed_id, None),
542 (crdb_id, Some(zed_id)),
543 (gpui2_id, Some(zed_id)),
544 (livestreaming_id, Some(zed_id)),
545 (livestreaming_id, Some(crdb_id)),
546 (livestreaming_dag_id, Some(livestreaming_id)),
547 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
548 ],
549 );
550
551 // ========================================================================
552 // Test a complex DAG by making another link
553 let returned_channels = db
554 .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
555 .await
556 .unwrap();
557
558 // DAG is now:
559 // /- gpui2 /---------------------\
560 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
561 // \--------/
562
563 // make sure we're getting just the new link
564 // Not using the assert_dag helper because we want to make sure we're returning the full data
565 pretty_assertions::assert_eq!(
566 returned_channels,
567 graph(
568 &[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
569 &[(livestreaming_dag_sub_id, livestreaming_id)]
570 )
571 );
572
573 let result = db.get_channels_for_user(a_id).await.unwrap();
574 assert_dag(
575 result.channels,
576 &[
577 (zed_id, None),
578 (crdb_id, Some(zed_id)),
579 (gpui2_id, Some(zed_id)),
580 (livestreaming_id, Some(zed_id)),
581 (livestreaming_id, Some(crdb_id)),
582 (livestreaming_dag_id, Some(livestreaming_id)),
583 (livestreaming_dag_sub_id, Some(livestreaming_id)),
584 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
585 ],
586 );
587
588 // ========================================================================
589 // Test a complex DAG by making another link
590 let returned_channels = db
591 .link_channel(a_id, livestreaming_id, gpui2_id)
592 .await
593 .unwrap();
594
595 // DAG is now:
596 // /- gpui2 -\ /---------------------\
597 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
598 // \---------/
599
600 // Make sure that we're correctly getting the full sub-dag
601 pretty_assertions::assert_eq!(
602 returned_channels,
603 graph(
604 &[
605 (livestreaming_id, "livestreaming"),
606 (livestreaming_dag_id, "livestreaming_dag"),
607 (livestreaming_dag_sub_id, "livestreaming_dag_sub"),
608 ],
609 &[
610 (livestreaming_id, gpui2_id),
611 (livestreaming_dag_id, livestreaming_id),
612 (livestreaming_dag_sub_id, livestreaming_id),
613 (livestreaming_dag_sub_id, livestreaming_dag_id),
614 ]
615 )
616 );
617
618 let result = db.get_channels_for_user(a_id).await.unwrap();
619 assert_dag(
620 result.channels,
621 &[
622 (zed_id, None),
623 (crdb_id, Some(zed_id)),
624 (gpui2_id, Some(zed_id)),
625 (livestreaming_id, Some(zed_id)),
626 (livestreaming_id, Some(crdb_id)),
627 (livestreaming_id, Some(gpui2_id)),
628 (livestreaming_dag_id, Some(livestreaming_id)),
629 (livestreaming_dag_sub_id, Some(livestreaming_id)),
630 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
631 ],
632 );
633
634 // ========================================================================
635 // Test unlinking in a complex DAG by removing the inner link
636 db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
637 .await
638 .unwrap();
639
640 // DAG is now:
641 // /- gpui2 -\
642 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
643 // \---------/
644
645 let result = db.get_channels_for_user(a_id).await.unwrap();
646 assert_dag(
647 result.channels,
648 &[
649 (zed_id, None),
650 (crdb_id, Some(zed_id)),
651 (gpui2_id, Some(zed_id)),
652 (livestreaming_id, Some(gpui2_id)),
653 (livestreaming_id, Some(zed_id)),
654 (livestreaming_id, Some(crdb_id)),
655 (livestreaming_dag_id, Some(livestreaming_id)),
656 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
657 ],
658 );
659
660 // ========================================================================
661 // Test unlinking in a complex DAG by removing the inner link
662 db.unlink_channel(a_id, livestreaming_id, gpui2_id)
663 .await
664 .unwrap();
665
666 // DAG is now:
667 // /- gpui2
668 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
669 // \---------/
670 let result = db.get_channels_for_user(a_id).await.unwrap();
671 assert_dag(
672 result.channels,
673 &[
674 (zed_id, None),
675 (crdb_id, Some(zed_id)),
676 (gpui2_id, Some(zed_id)),
677 (livestreaming_id, Some(zed_id)),
678 (livestreaming_id, Some(crdb_id)),
679 (livestreaming_dag_id, Some(livestreaming_id)),
680 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
681 ],
682 );
683
684 // ========================================================================
685 // Test moving DAG nodes by moving livestreaming to be below gpui2
686 db.move_channel(a_id, livestreaming_id, crdb_id, gpui2_id)
687 .await
688 .unwrap();
689
690 // DAG is now:
691 // /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
692 // zed - crdb /
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, Some(zed_id)),
700 (gpui2_id, Some(zed_id)),
701 (livestreaming_id, Some(zed_id)),
702 (livestreaming_id, Some(gpui2_id)),
703 (livestreaming_dag_id, Some(livestreaming_id)),
704 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
705 ],
706 );
707
708 // ========================================================================
709 // Deleting a channel should not delete children that still have other parents
710 db.delete_channel(gpui2_id, a_id).await.unwrap();
711
712 // DAG is now:
713 // zed - crdb
714 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
715 let result = db.get_channels_for_user(a_id).await.unwrap();
716 assert_dag(
717 result.channels,
718 &[
719 (zed_id, None),
720 (crdb_id, Some(zed_id)),
721 (livestreaming_id, Some(zed_id)),
722 (livestreaming_dag_id, Some(livestreaming_id)),
723 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
724 ],
725 );
726
727 // ========================================================================
728 // Unlinking a channel from it's parent should automatically promote it to a root channel
729 db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
730
731 // DAG is now:
732 // crdb
733 // zed
734 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
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, None),
742 (livestreaming_id, Some(zed_id)),
743 (livestreaming_dag_id, Some(livestreaming_id)),
744 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
745 ],
746 );
747
748 // ========================================================================
749 // You should be able to move a root channel into a non-root channel
750 db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
751
752 // DAG is now:
753 // zed - crdb
754 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
755
756 let result = db.get_channels_for_user(a_id).await.unwrap();
757 assert_dag(
758 result.channels,
759 &[
760 (zed_id, None),
761 (crdb_id, Some(zed_id)),
762 (livestreaming_id, Some(zed_id)),
763 (livestreaming_dag_id, Some(livestreaming_id)),
764 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
765 ],
766 );
767
768 // ========================================================================
769 // Prep for DAG deletion test
770 db.link_channel(a_id, livestreaming_id, crdb_id)
771 .await
772 .unwrap();
773
774 // DAG is now:
775 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
776 // \--------/
777
778 let result = db.get_channels_for_user(a_id).await.unwrap();
779 assert_dag(
780 result.channels,
781 &[
782 (zed_id, None),
783 (crdb_id, Some(zed_id)),
784 (livestreaming_id, Some(zed_id)),
785 (livestreaming_id, Some(crdb_id)),
786 (livestreaming_dag_id, Some(livestreaming_id)),
787 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
788 ],
789 );
790
791 // Deleting the parent of a DAG should delete the whole DAG:
792 db.delete_channel(zed_id, a_id).await.unwrap();
793 let result = db.get_channels_for_user(a_id).await.unwrap();
794
795 assert!(result.channels.is_empty())
796}
797
798test_both_dbs!(
799 test_db_channel_moving_bugs,
800 test_db_channel_moving_bugs_postgres,
801 test_db_channel_moving_bugs_sqlite
802);
803
804async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
805 let user_id = db
806 .create_user(
807 "user1@example.com",
808 false,
809 NewUserParams {
810 github_login: "user1".into(),
811 github_user_id: 5,
812 invite_count: 0,
813 },
814 )
815 .await
816 .unwrap()
817 .user_id;
818
819 let zed_id = db.create_root_channel("zed", user_id).await.unwrap();
820
821 let projects_id = db
822 .create_channel("projects", Some(zed_id), user_id)
823 .await
824 .unwrap();
825
826 let livestreaming_id = db
827 .create_channel("livestreaming", Some(projects_id), user_id)
828 .await
829 .unwrap();
830
831 // Dag is: zed - projects - livestreaming
832
833 // Move to same parent should be a no-op
834 assert!(db
835 .move_channel(user_id, projects_id, zed_id, zed_id)
836 .await
837 .unwrap()
838 .is_empty());
839
840 // Stranding a channel should retain it's sub channels
841 db.unlink_channel(user_id, projects_id, zed_id)
842 .await
843 .unwrap();
844
845 let result = db.get_channels_for_user(user_id).await.unwrap();
846 assert_dag(
847 result.channels,
848 &[
849 (zed_id, None),
850 (projects_id, None),
851 (livestreaming_id, Some(projects_id)),
852 ],
853 );
854}
855
856test_both_dbs!(
857 test_user_is_channel_participant,
858 test_user_is_channel_participant_postgres,
859 test_user_is_channel_participant_sqlite
860);
861
862async fn test_user_is_channel_participant(db: &Arc<Database>) {
863 let admin_id = new_test_user(db, "admin@example.com").await;
864 let member_id = new_test_user(db, "member@example.com").await;
865 let guest_id = new_test_user(db, "guest@example.com").await;
866
867 let zed_id = db.create_root_channel("zed", admin_id).await.unwrap();
868 let intermediate_id = db
869 .create_channel("active", Some(zed_id), admin_id)
870 .await
871 .unwrap();
872 let public_id = db
873 .create_channel("active", Some(intermediate_id), admin_id)
874 .await
875 .unwrap();
876
877 db.set_channel_visibility(public_id, crate::db::ChannelVisibility::Public, admin_id)
878 .await
879 .unwrap();
880 db.invite_channel_member(intermediate_id, member_id, admin_id, ChannelRole::Member)
881 .await
882 .unwrap();
883 db.invite_channel_member(public_id, guest_id, admin_id, ChannelRole::Guest)
884 .await
885 .unwrap();
886
887 db.transaction(|tx| async move {
888 db.check_user_is_channel_participant(public_id, admin_id, &*tx)
889 .await
890 })
891 .await
892 .unwrap();
893 db.transaction(|tx| async move {
894 db.check_user_is_channel_participant(public_id, member_id, &*tx)
895 .await
896 })
897 .await
898 .unwrap();
899 db.transaction(|tx| async move {
900 db.check_user_is_channel_participant(public_id, guest_id, &*tx)
901 .await
902 })
903 .await
904 .unwrap();
905
906 db.set_channel_member_role(public_id, admin_id, guest_id, ChannelRole::Banned)
907 .await
908 .unwrap();
909 assert!(db
910 .transaction(|tx| async move {
911 db.check_user_is_channel_participant(public_id, guest_id, &*tx)
912 .await
913 })
914 .await
915 .is_err());
916
917 db.remove_channel_member(public_id, guest_id, admin_id)
918 .await
919 .unwrap();
920
921 db.set_channel_visibility(zed_id, crate::db::ChannelVisibility::Public, admin_id)
922 .await
923 .unwrap();
924
925 db.invite_channel_member(zed_id, guest_id, admin_id, ChannelRole::Guest)
926 .await
927 .unwrap();
928
929 db.transaction(|tx| async move {
930 db.check_user_is_channel_participant(zed_id, guest_id, &*tx)
931 .await
932 })
933 .await
934 .unwrap();
935 assert!(db
936 .transaction(|tx| async move {
937 db.check_user_is_channel_participant(intermediate_id, guest_id, &*tx)
938 .await
939 })
940 .await
941 .is_err(),);
942
943 db.transaction(|tx| async move {
944 db.check_user_is_channel_participant(public_id, guest_id, &*tx)
945 .await
946 })
947 .await
948 .unwrap();
949}
950
951#[track_caller]
952fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
953 let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
954 for channel in actual.channels {
955 actual_map.insert(channel.id, HashSet::default());
956 }
957 for edge in actual.edges {
958 actual_map
959 .get_mut(&ChannelId::from_proto(edge.channel_id))
960 .unwrap()
961 .insert(ChannelId::from_proto(edge.parent_id));
962 }
963
964 let mut expected_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
965
966 for (child, parent) in expected {
967 let entry = expected_map.entry(*child).or_default();
968 if let Some(parent) = parent {
969 entry.insert(*parent);
970 }
971 }
972
973 pretty_assertions::assert_eq!(actual_map, expected_map)
974}
975
976static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
977
978async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
979 let gid = GITHUB_USER_ID.fetch_add(1, Ordering::SeqCst);
980
981 db.create_user(
982 email,
983 false,
984 NewUserParams {
985 github_login: email[0..email.find("@").unwrap()].to_string(),
986 github_user_id: GITHUB_USER_ID.fetch_add(1, Ordering::SeqCst),
987 invite_count: 0,
988 },
989 )
990 .await
991 .unwrap()
992 .user_id
993}