channel_tests.rs

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