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