1use collections::{HashMap, HashSet};
2use rpc::{
3 proto::{self},
4 ConnectionId,
5};
6
7use crate::{
8 db::{queries::channels::ChannelGraph, tests::graph, ChannelId, Database, NewUserParams},
9 test_both_dbs,
10};
11use std::sync::Arc;
12
13test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
14
15async fn test_channels(db: &Arc<Database>) {
16 let a_id = db
17 .create_user(
18 "user1@example.com",
19 false,
20 NewUserParams {
21 github_login: "user1".into(),
22 github_user_id: 5,
23 invite_count: 0,
24 },
25 )
26 .await
27 .unwrap()
28 .user_id;
29
30 let b_id = db
31 .create_user(
32 "user2@example.com",
33 false,
34 NewUserParams {
35 github_login: "user2".into(),
36 github_user_id: 6,
37 invite_count: 0,
38 },
39 )
40 .await
41 .unwrap()
42 .user_id;
43
44 let zed_id = db.create_root_channel("zed", "1", a_id).await.unwrap();
45
46 // Make sure that people cannot read channels they haven't been invited to
47 assert!(db.get_channel(zed_id, b_id).await.unwrap().is_none());
48
49 db.invite_channel_member(zed_id, b_id, a_id, false)
50 .await
51 .unwrap();
52
53 db.respond_to_channel_invite(zed_id, b_id, true)
54 .await
55 .unwrap();
56
57 let crdb_id = db
58 .create_channel("crdb", Some(zed_id), "2", a_id)
59 .await
60 .unwrap();
61 let livestreaming_id = db
62 .create_channel("livestreaming", Some(zed_id), "3", a_id)
63 .await
64 .unwrap();
65 let replace_id = db
66 .create_channel("replace", Some(zed_id), "4", a_id)
67 .await
68 .unwrap();
69
70 let mut members = db.get_channel_members(replace_id).await.unwrap();
71 members.sort();
72 assert_eq!(members, &[a_id, b_id]);
73
74 let rust_id = db.create_root_channel("rust", "5", a_id).await.unwrap();
75 let cargo_id = db
76 .create_channel("cargo", Some(rust_id), "6", a_id)
77 .await
78 .unwrap();
79
80 let cargo_ra_id = db
81 .create_channel("cargo-ra", Some(cargo_id), "7", a_id)
82 .await
83 .unwrap();
84
85 let result = db.get_channels_for_user(a_id).await.unwrap();
86 assert_eq!(
87 result.channels,
88 graph(
89 &[
90 (zed_id, "zed"),
91 (crdb_id, "crdb"),
92 (livestreaming_id, "livestreaming"),
93 (replace_id, "replace"),
94 (rust_id, "rust"),
95 (cargo_id, "cargo"),
96 (cargo_ra_id, "cargo-ra")
97 ],
98 &[
99 (crdb_id, zed_id),
100 (livestreaming_id, zed_id),
101 (replace_id, zed_id),
102 (cargo_id, rust_id),
103 (cargo_ra_id, cargo_id),
104 ]
105 )
106 );
107
108 let result = db.get_channels_for_user(b_id).await.unwrap();
109 assert_eq!(
110 result.channels,
111 graph(
112 &[
113 (zed_id, "zed"),
114 (crdb_id, "crdb"),
115 (livestreaming_id, "livestreaming"),
116 (replace_id, "replace")
117 ],
118 &[
119 (crdb_id, zed_id),
120 (livestreaming_id, zed_id),
121 (replace_id, zed_id)
122 ]
123 )
124 );
125
126 // Update member permissions
127 let set_subchannel_admin = db.set_channel_member_admin(crdb_id, a_id, b_id, true).await;
128 assert!(set_subchannel_admin.is_err());
129 let set_channel_admin = db.set_channel_member_admin(zed_id, a_id, b_id, true).await;
130 assert!(set_channel_admin.is_ok());
131
132 let result = db.get_channels_for_user(b_id).await.unwrap();
133 assert_eq!(
134 result.channels,
135 graph(
136 &[
137 (zed_id, "zed"),
138 (crdb_id, "crdb"),
139 (livestreaming_id, "livestreaming"),
140 (replace_id, "replace")
141 ],
142 &[
143 (crdb_id, zed_id),
144 (livestreaming_id, zed_id),
145 (replace_id, zed_id)
146 ]
147 )
148 );
149
150 // Remove a single channel
151 db.delete_channel(crdb_id, a_id).await.unwrap();
152 assert!(db.get_channel(crdb_id, a_id).await.unwrap().is_none());
153
154 // Remove a channel tree
155 let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
156 channel_ids.sort();
157 assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
158 assert_eq!(user_ids, &[a_id]);
159
160 assert!(db.get_channel(rust_id, a_id).await.unwrap().is_none());
161 assert!(db.get_channel(cargo_id, a_id).await.unwrap().is_none());
162 assert!(db.get_channel(cargo_ra_id, a_id).await.unwrap().is_none());
163}
164
165test_both_dbs!(
166 test_joining_channels,
167 test_joining_channels_postgres,
168 test_joining_channels_sqlite
169);
170
171async fn test_joining_channels(db: &Arc<Database>) {
172 let owner_id = db.create_server("test").await.unwrap().0 as u32;
173
174 let user_1 = db
175 .create_user(
176 "user1@example.com",
177 false,
178 NewUserParams {
179 github_login: "user1".into(),
180 github_user_id: 5,
181 invite_count: 0,
182 },
183 )
184 .await
185 .unwrap()
186 .user_id;
187 let user_2 = db
188 .create_user(
189 "user2@example.com",
190 false,
191 NewUserParams {
192 github_login: "user2".into(),
193 github_user_id: 6,
194 invite_count: 0,
195 },
196 )
197 .await
198 .unwrap()
199 .user_id;
200
201 let channel_1 = db
202 .create_root_channel("channel_1", "1", user_1)
203 .await
204 .unwrap();
205 let room_1 = db.room_id_for_channel(channel_1).await.unwrap();
206
207 // can join a room with membership to its channel
208 let joined_room = db
209 .join_room(room_1, user_1, ConnectionId { owner_id, id: 1 })
210 .await
211 .unwrap();
212 assert_eq!(joined_room.room.participants.len(), 1);
213
214 drop(joined_room);
215 // cannot join a room without membership to its channel
216 assert!(db
217 .join_room(room_1, user_2, ConnectionId { owner_id, id: 1 })
218 .await
219 .is_err());
220}
221
222test_both_dbs!(
223 test_channel_invites,
224 test_channel_invites_postgres,
225 test_channel_invites_sqlite
226);
227
228async fn test_channel_invites(db: &Arc<Database>) {
229 db.create_server("test").await.unwrap();
230
231 let user_1 = db
232 .create_user(
233 "user1@example.com",
234 false,
235 NewUserParams {
236 github_login: "user1".into(),
237 github_user_id: 5,
238 invite_count: 0,
239 },
240 )
241 .await
242 .unwrap()
243 .user_id;
244 let user_2 = db
245 .create_user(
246 "user2@example.com",
247 false,
248 NewUserParams {
249 github_login: "user2".into(),
250 github_user_id: 6,
251 invite_count: 0,
252 },
253 )
254 .await
255 .unwrap()
256 .user_id;
257
258 let user_3 = db
259 .create_user(
260 "user3@example.com",
261 false,
262 NewUserParams {
263 github_login: "user3".into(),
264 github_user_id: 7,
265 invite_count: 0,
266 },
267 )
268 .await
269 .unwrap()
270 .user_id;
271
272 let channel_1_1 = db
273 .create_root_channel("channel_1", "1", user_1)
274 .await
275 .unwrap();
276
277 let channel_1_2 = db
278 .create_root_channel("channel_2", "2", user_1)
279 .await
280 .unwrap();
281
282 db.invite_channel_member(channel_1_1, user_2, user_1, false)
283 .await
284 .unwrap();
285 db.invite_channel_member(channel_1_2, user_2, user_1, false)
286 .await
287 .unwrap();
288 db.invite_channel_member(channel_1_1, user_3, user_1, true)
289 .await
290 .unwrap();
291
292 let user_2_invites = db
293 .get_channel_invites_for_user(user_2) // -> [channel_1_1, channel_1_2]
294 .await
295 .unwrap()
296 .into_iter()
297 .map(|channel| channel.id)
298 .collect::<Vec<_>>();
299
300 assert_eq!(user_2_invites, &[channel_1_1, channel_1_2]);
301
302 let user_3_invites = db
303 .get_channel_invites_for_user(user_3) // -> [channel_1_1]
304 .await
305 .unwrap()
306 .into_iter()
307 .map(|channel| channel.id)
308 .collect::<Vec<_>>();
309
310 assert_eq!(user_3_invites, &[channel_1_1]);
311
312 let members = db
313 .get_channel_member_details(channel_1_1, user_1)
314 .await
315 .unwrap();
316 assert_eq!(
317 members,
318 &[
319 proto::ChannelMember {
320 user_id: user_1.to_proto(),
321 kind: proto::channel_member::Kind::Member.into(),
322 admin: true,
323 },
324 proto::ChannelMember {
325 user_id: user_2.to_proto(),
326 kind: proto::channel_member::Kind::Invitee.into(),
327 admin: false,
328 },
329 proto::ChannelMember {
330 user_id: user_3.to_proto(),
331 kind: proto::channel_member::Kind::Invitee.into(),
332 admin: true,
333 },
334 ]
335 );
336
337 db.respond_to_channel_invite(channel_1_1, user_2, true)
338 .await
339 .unwrap();
340
341 let channel_1_3 = db
342 .create_channel("channel_3", Some(channel_1_1), "1", user_1)
343 .await
344 .unwrap();
345
346 let members = db
347 .get_channel_member_details(channel_1_3, user_1)
348 .await
349 .unwrap();
350 assert_eq!(
351 members,
352 &[
353 proto::ChannelMember {
354 user_id: user_1.to_proto(),
355 kind: proto::channel_member::Kind::Member.into(),
356 admin: true,
357 },
358 proto::ChannelMember {
359 user_id: user_2.to_proto(),
360 kind: proto::channel_member::Kind::AncestorMember.into(),
361 admin: false,
362 },
363 ]
364 );
365}
366
367test_both_dbs!(
368 test_channel_renames,
369 test_channel_renames_postgres,
370 test_channel_renames_sqlite
371);
372
373async fn test_channel_renames(db: &Arc<Database>) {
374 db.create_server("test").await.unwrap();
375
376 let user_1 = db
377 .create_user(
378 "user1@example.com",
379 false,
380 NewUserParams {
381 github_login: "user1".into(),
382 github_user_id: 5,
383 invite_count: 0,
384 },
385 )
386 .await
387 .unwrap()
388 .user_id;
389
390 let user_2 = db
391 .create_user(
392 "user2@example.com",
393 false,
394 NewUserParams {
395 github_login: "user2".into(),
396 github_user_id: 6,
397 invite_count: 0,
398 },
399 )
400 .await
401 .unwrap()
402 .user_id;
403
404 let zed_id = db.create_root_channel("zed", "1", user_1).await.unwrap();
405
406 db.rename_channel(zed_id, user_1, "#zed-archive")
407 .await
408 .unwrap();
409
410 let zed_archive_id = zed_id;
411
412 let (channel, _) = db
413 .get_channel(zed_archive_id, user_1)
414 .await
415 .unwrap()
416 .unwrap();
417 assert_eq!(channel.name, "zed-archive");
418
419 let non_permissioned_rename = db
420 .rename_channel(zed_archive_id, user_2, "hacked-lol")
421 .await;
422 assert!(non_permissioned_rename.is_err());
423
424 let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
425 assert!(bad_name_rename.is_err())
426}
427
428test_both_dbs!(
429 test_db_channel_moving,
430 test_channels_moving_postgres,
431 test_channels_moving_sqlite
432);
433
434async fn test_db_channel_moving(db: &Arc<Database>) {
435 let a_id = db
436 .create_user(
437 "user1@example.com",
438 false,
439 NewUserParams {
440 github_login: "user1".into(),
441 github_user_id: 5,
442 invite_count: 0,
443 },
444 )
445 .await
446 .unwrap()
447 .user_id;
448
449 let zed_id = db.create_root_channel("zed", "1", a_id).await.unwrap();
450
451 let crdb_id = db
452 .create_channel("crdb", Some(zed_id), "2", a_id)
453 .await
454 .unwrap();
455
456 let gpui2_id = db
457 .create_channel("gpui2", Some(zed_id), "3", a_id)
458 .await
459 .unwrap();
460
461 let livestreaming_id = db
462 .create_channel("livestreaming", Some(crdb_id), "4", a_id)
463 .await
464 .unwrap();
465
466 let livestreaming_dag_id = db
467 .create_channel("livestreaming_dag", Some(livestreaming_id), "5", a_id)
468 .await
469 .unwrap();
470
471 // ========================================================================
472 // sanity check
473 // Initial DAG:
474 // /- gpui2
475 // zed -- crdb - livestreaming - livestreaming_dag
476 let result = db.get_channels_for_user(a_id).await.unwrap();
477 assert_dag(
478 result.channels,
479 &[
480 (zed_id, None),
481 (crdb_id, Some(zed_id)),
482 (gpui2_id, Some(zed_id)),
483 (livestreaming_id, Some(crdb_id)),
484 (livestreaming_dag_id, Some(livestreaming_id)),
485 ],
486 );
487
488 // Attempt to make a cycle
489 assert!(db
490 .link_channel(a_id, zed_id, livestreaming_id)
491 .await
492 .is_err());
493
494 // ========================================================================
495 // Make a link
496 db.link_channel(a_id, livestreaming_id, zed_id)
497 .await
498 .unwrap();
499
500 // DAG is now:
501 // /- gpui2
502 // zed -- crdb - livestreaming - livestreaming_dag
503 // \---------/
504 let result = db.get_channels_for_user(a_id).await.unwrap();
505 assert_dag(
506 result.channels,
507 &[
508 (zed_id, None),
509 (crdb_id, Some(zed_id)),
510 (gpui2_id, Some(zed_id)),
511 (livestreaming_id, Some(zed_id)),
512 (livestreaming_id, Some(crdb_id)),
513 (livestreaming_dag_id, Some(livestreaming_id)),
514 ],
515 );
516
517 // ========================================================================
518 // Create a new channel below a channel with multiple parents
519 let livestreaming_dag_sub_id = db
520 .create_channel(
521 "livestreaming_dag_sub",
522 Some(livestreaming_dag_id),
523 "6",
524 a_id,
525 )
526 .await
527 .unwrap();
528
529 // DAG is now:
530 // /- gpui2
531 // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
532 // \---------/
533 let result = db.get_channels_for_user(a_id).await.unwrap();
534 assert_dag(
535 result.channels,
536 &[
537 (zed_id, None),
538 (crdb_id, Some(zed_id)),
539 (gpui2_id, Some(zed_id)),
540 (livestreaming_id, Some(zed_id)),
541 (livestreaming_id, Some(crdb_id)),
542 (livestreaming_dag_id, Some(livestreaming_id)),
543 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
544 ],
545 );
546
547 // ========================================================================
548 // Test a complex DAG by making another link
549 let returned_channels = db
550 .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
551 .await
552 .unwrap();
553
554 // DAG is now:
555 // /- gpui2 /---------------------\
556 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
557 // \--------/
558
559 // make sure we're getting just the new link
560 // Not using the assert_dag helper because we want to make sure we're returning the full data
561 pretty_assertions::assert_eq!(
562 returned_channels,
563 graph(
564 &[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
565 &[(livestreaming_dag_sub_id, livestreaming_id)]
566 )
567 );
568
569 let result = db.get_channels_for_user(a_id).await.unwrap();
570 assert_dag(
571 result.channels,
572 &[
573 (zed_id, None),
574 (crdb_id, Some(zed_id)),
575 (gpui2_id, Some(zed_id)),
576 (livestreaming_id, Some(zed_id)),
577 (livestreaming_id, Some(crdb_id)),
578 (livestreaming_dag_id, Some(livestreaming_id)),
579 (livestreaming_dag_sub_id, Some(livestreaming_id)),
580 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
581 ],
582 );
583
584 // ========================================================================
585 // Test a complex DAG by making another link
586 let returned_channels = db
587 .link_channel(a_id, livestreaming_id, gpui2_id)
588 .await
589 .unwrap();
590
591 // DAG is now:
592 // /- gpui2 -\ /---------------------\
593 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
594 // \---------/
595
596 // Make sure that we're correctly getting the full sub-dag
597 pretty_assertions::assert_eq!(
598 returned_channels,
599 graph(
600 &[
601 (livestreaming_id, "livestreaming"),
602 (livestreaming_dag_id, "livestreaming_dag"),
603 (livestreaming_dag_sub_id, "livestreaming_dag_sub"),
604 ],
605 &[
606 (livestreaming_id, gpui2_id),
607 (livestreaming_dag_id, livestreaming_id),
608 (livestreaming_dag_sub_id, livestreaming_id),
609 (livestreaming_dag_sub_id, livestreaming_dag_id),
610 ]
611 )
612 );
613
614 let result = db.get_channels_for_user(a_id).await.unwrap();
615 assert_dag(
616 result.channels,
617 &[
618 (zed_id, None),
619 (crdb_id, Some(zed_id)),
620 (gpui2_id, Some(zed_id)),
621 (livestreaming_id, Some(zed_id)),
622 (livestreaming_id, Some(crdb_id)),
623 (livestreaming_id, Some(gpui2_id)),
624 (livestreaming_dag_id, Some(livestreaming_id)),
625 (livestreaming_dag_sub_id, Some(livestreaming_id)),
626 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
627 ],
628 );
629
630 // ========================================================================
631 // Test unlinking in a complex DAG by removing the inner link
632 db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
633 .await
634 .unwrap();
635
636 // DAG is now:
637 // /- gpui2 -\
638 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
639 // \---------/
640
641 let result = db.get_channels_for_user(a_id).await.unwrap();
642 assert_dag(
643 result.channels,
644 &[
645 (zed_id, None),
646 (crdb_id, Some(zed_id)),
647 (gpui2_id, Some(zed_id)),
648 (livestreaming_id, Some(gpui2_id)),
649 (livestreaming_id, Some(zed_id)),
650 (livestreaming_id, Some(crdb_id)),
651 (livestreaming_dag_id, Some(livestreaming_id)),
652 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
653 ],
654 );
655
656 // ========================================================================
657 // Test unlinking in a complex DAG by removing the inner link
658 db.unlink_channel(a_id, livestreaming_id, gpui2_id)
659 .await
660 .unwrap();
661
662 // DAG is now:
663 // /- gpui2
664 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
665 // \---------/
666 let result = db.get_channels_for_user(a_id).await.unwrap();
667 assert_dag(
668 result.channels,
669 &[
670 (zed_id, None),
671 (crdb_id, Some(zed_id)),
672 (gpui2_id, Some(zed_id)),
673 (livestreaming_id, Some(zed_id)),
674 (livestreaming_id, Some(crdb_id)),
675 (livestreaming_dag_id, Some(livestreaming_id)),
676 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
677 ],
678 );
679
680 // ========================================================================
681 // Test moving DAG nodes by moving livestreaming to be below gpui2
682 db.move_channel(a_id, livestreaming_id, crdb_id, gpui2_id)
683 .await
684 .unwrap();
685
686 // DAG is now:
687 // /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
688 // zed - crdb /
689 // \---------/
690 let result = db.get_channels_for_user(a_id).await.unwrap();
691 assert_dag(
692 result.channels,
693 &[
694 (zed_id, None),
695 (crdb_id, Some(zed_id)),
696 (gpui2_id, Some(zed_id)),
697 (livestreaming_id, Some(zed_id)),
698 (livestreaming_id, Some(gpui2_id)),
699 (livestreaming_dag_id, Some(livestreaming_id)),
700 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
701 ],
702 );
703
704 // ========================================================================
705 // Deleting a channel should not delete children that still have other parents
706 db.delete_channel(gpui2_id, a_id).await.unwrap();
707
708 // DAG is now:
709 // zed - crdb
710 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
711 let result = db.get_channels_for_user(a_id).await.unwrap();
712 assert_dag(
713 result.channels,
714 &[
715 (zed_id, None),
716 (crdb_id, Some(zed_id)),
717 (livestreaming_id, Some(zed_id)),
718 (livestreaming_dag_id, Some(livestreaming_id)),
719 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
720 ],
721 );
722
723 // ========================================================================
724 // Unlinking a channel from it's parent should automatically promote it to a root channel
725 db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
726
727 // DAG is now:
728 // crdb
729 // zed
730 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
731
732 let result = db.get_channels_for_user(a_id).await.unwrap();
733 assert_dag(
734 result.channels,
735 &[
736 (zed_id, None),
737 (crdb_id, None),
738 (livestreaming_id, Some(zed_id)),
739 (livestreaming_dag_id, Some(livestreaming_id)),
740 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
741 ],
742 );
743
744 // ========================================================================
745 // You should be able to move a root channel into a non-root channel
746 db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
747
748 // DAG is now:
749 // zed - crdb
750 // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
751
752 let result = db.get_channels_for_user(a_id).await.unwrap();
753 assert_dag(
754 result.channels,
755 &[
756 (zed_id, None),
757 (crdb_id, Some(zed_id)),
758 (livestreaming_id, Some(zed_id)),
759 (livestreaming_dag_id, Some(livestreaming_id)),
760 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
761 ],
762 );
763
764 // ========================================================================
765 // Prep for DAG deletion test
766 db.link_channel(a_id, livestreaming_id, crdb_id)
767 .await
768 .unwrap();
769
770 // DAG is now:
771 // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
772 // \--------/
773
774 let result = db.get_channels_for_user(a_id).await.unwrap();
775 assert_dag(
776 result.channels,
777 &[
778 (zed_id, None),
779 (crdb_id, Some(zed_id)),
780 (livestreaming_id, Some(zed_id)),
781 (livestreaming_id, Some(crdb_id)),
782 (livestreaming_dag_id, Some(livestreaming_id)),
783 (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
784 ],
785 );
786
787 // Deleting the parent of a DAG should delete the whole DAG:
788 db.delete_channel(zed_id, a_id).await.unwrap();
789 let result = db.get_channels_for_user(a_id).await.unwrap();
790
791 assert!(result.channels.is_empty())
792}
793
794test_both_dbs!(
795 test_db_channel_moving_bugs,
796 test_db_channel_moving_bugs_postgres,
797 test_db_channel_moving_bugs_sqlite
798);
799
800async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
801 let user_id = db
802 .create_user(
803 "user1@example.com",
804 false,
805 NewUserParams {
806 github_login: "user1".into(),
807 github_user_id: 5,
808 invite_count: 0,
809 },
810 )
811 .await
812 .unwrap()
813 .user_id;
814
815 let zed_id = db.create_root_channel("zed", "1", user_id).await.unwrap();
816
817 let projects_id = db
818 .create_channel("projects", Some(zed_id), "2", user_id)
819 .await
820 .unwrap();
821
822 let livestreaming_id = db
823 .create_channel("livestreaming", Some(projects_id), "3", user_id)
824 .await
825 .unwrap();
826
827 // Dag is: zed - projects - livestreaming
828
829 // Move to same parent should be a no-op
830 assert!(db
831 .move_channel(user_id, projects_id, zed_id, zed_id)
832 .await
833 .unwrap()
834 .is_empty());
835
836 // Stranding a channel should retain it's sub channels
837 db.unlink_channel(user_id, projects_id, zed_id)
838 .await
839 .unwrap();
840
841 let result = db.get_channels_for_user(user_id).await.unwrap();
842 assert_dag(
843 result.channels,
844 &[
845 (zed_id, None),
846 (projects_id, None),
847 (livestreaming_id, Some(projects_id)),
848 ],
849 );
850}
851
852#[track_caller]
853fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
854 let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
855 for channel in actual.channels {
856 actual_map.insert(channel.id, HashSet::default());
857 }
858 for edge in actual.edges {
859 actual_map
860 .get_mut(&ChannelId::from_proto(edge.channel_id))
861 .unwrap()
862 .insert(ChannelId::from_proto(edge.parent_id));
863 }
864
865 let mut expected_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
866
867 for (child, parent) in expected {
868 let entry = expected_map.entry(*child).or_default();
869 if let Some(parent) = parent {
870 entry.insert(*parent);
871 }
872 }
873
874 pretty_assertions::assert_eq!(actual_map, expected_map)
875}