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