channel_tests.rs

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