channel_tests.rs

   1use crate::{
   2    db::{self, UserId},
   3    rpc::RECONNECT_TIMEOUT,
   4    tests::{RoomParticipants, TestServer, room_participants},
   5};
   6use call::ActiveCall;
   7use channel::{ChannelMembership, ChannelStore};
   8use client::{ChannelId, User};
   9use futures::future::try_join_all;
  10use gpui::{BackgroundExecutor, Entity, SharedString, TestAppContext};
  11use rpc::{
  12    RECEIVE_TIMEOUT,
  13    proto::{self, ChannelRole},
  14};
  15use std::sync::Arc;
  16
  17#[gpui::test]
  18async fn test_core_channels(
  19    executor: BackgroundExecutor,
  20    cx_a: &mut TestAppContext,
  21    cx_b: &mut TestAppContext,
  22) {
  23    let mut server = TestServer::start(executor.clone()).await;
  24    let client_a = server.create_client(cx_a, "user_a").await;
  25    let client_b = server.create_client(cx_b, "user_b").await;
  26
  27    let channel_a_id = client_a
  28        .channel_store()
  29        .update(cx_a, |channel_store, cx| {
  30            channel_store.create_channel("channel-a", None, cx)
  31        })
  32        .await
  33        .unwrap();
  34    let channel_b_id = client_a
  35        .channel_store()
  36        .update(cx_a, |channel_store, cx| {
  37            channel_store.create_channel("channel-b", Some(channel_a_id), cx)
  38        })
  39        .await
  40        .unwrap();
  41
  42    executor.run_until_parked();
  43    assert_channels(
  44        client_a.channel_store(),
  45        cx_a,
  46        &[
  47            ExpectedChannel {
  48                id: channel_a_id,
  49                name: "channel-a".into(),
  50                depth: 0,
  51            },
  52            ExpectedChannel {
  53                id: channel_b_id,
  54                name: "channel-b".into(),
  55                depth: 1,
  56            },
  57        ],
  58    );
  59
  60    cx_b.read(|cx| {
  61        client_b.channel_store().read_with(cx, |channels, _| {
  62            assert!(channels.ordered_channels().collect::<Vec<_>>().is_empty())
  63        })
  64    });
  65
  66    // Invite client B to channel A as client A.
  67    client_a
  68        .channel_store()
  69        .update(cx_a, |store, cx| {
  70            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  71
  72            let invite = store.invite_member(
  73                channel_a_id,
  74                client_b.user_id().unwrap(),
  75                proto::ChannelRole::Member,
  76                cx,
  77            );
  78
  79            // Make sure we're synchronously storing the pending invite
  80            assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  81            invite
  82        })
  83        .await
  84        .unwrap();
  85
  86    // Client A sees that B has been invited.
  87    executor.run_until_parked();
  88    assert_channel_invitations(
  89        client_b.channel_store(),
  90        cx_b,
  91        &[ExpectedChannel {
  92            id: channel_a_id,
  93            name: "channel-a".into(),
  94            depth: 0,
  95        }],
  96    );
  97
  98    let members = client_a
  99        .channel_store()
 100        .update(cx_a, |store, cx| {
 101            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
 102            store.fuzzy_search_members(channel_a_id, "".to_string(), 10, cx)
 103        })
 104        .await
 105        .unwrap();
 106    assert_members_eq(
 107        &members,
 108        &[
 109            (
 110                client_a.user_id().unwrap(),
 111                proto::ChannelRole::Admin,
 112                proto::channel_member::Kind::Member,
 113            ),
 114            (
 115                client_b.user_id().unwrap(),
 116                proto::ChannelRole::Member,
 117                proto::channel_member::Kind::Invitee,
 118            ),
 119        ],
 120    );
 121
 122    // Client B accepts the invitation.
 123    client_b
 124        .channel_store()
 125        .update(cx_b, |channels, cx| {
 126            channels.respond_to_channel_invite(channel_a_id, true, cx)
 127        })
 128        .await
 129        .unwrap();
 130    executor.run_until_parked();
 131
 132    // Client B now sees that they are a member of channel A and its existing subchannels.
 133    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 134    assert_channels(
 135        client_b.channel_store(),
 136        cx_b,
 137        &[
 138            ExpectedChannel {
 139                id: channel_a_id,
 140                name: "channel-a".into(),
 141                depth: 0,
 142            },
 143            ExpectedChannel {
 144                id: channel_b_id,
 145                name: "channel-b".into(),
 146                depth: 1,
 147            },
 148        ],
 149    );
 150
 151    let channel_c_id = client_a
 152        .channel_store()
 153        .update(cx_a, |channel_store, cx| {
 154            channel_store.create_channel("channel-c", Some(channel_b_id), cx)
 155        })
 156        .await
 157        .unwrap();
 158
 159    executor.run_until_parked();
 160    assert_channels(
 161        client_b.channel_store(),
 162        cx_b,
 163        &[
 164            ExpectedChannel {
 165                id: channel_a_id,
 166                name: "channel-a".into(),
 167                depth: 0,
 168            },
 169            ExpectedChannel {
 170                id: channel_b_id,
 171                name: "channel-b".into(),
 172                depth: 1,
 173            },
 174            ExpectedChannel {
 175                id: channel_c_id,
 176                name: "channel-c".into(),
 177                depth: 2,
 178            },
 179        ],
 180    );
 181
 182    // Update client B's membership to channel A to be an admin.
 183    client_a
 184        .channel_store()
 185        .update(cx_a, |store, cx| {
 186            store.set_member_role(
 187                channel_a_id,
 188                client_b.user_id().unwrap(),
 189                proto::ChannelRole::Admin,
 190                cx,
 191            )
 192        })
 193        .await
 194        .unwrap();
 195    executor.run_until_parked();
 196
 197    // Observe that client B is now an admin of channel A, and that
 198    // their admin privileges extend to subchannels of channel A.
 199    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 200    assert_channels(
 201        client_b.channel_store(),
 202        cx_b,
 203        &[
 204            ExpectedChannel {
 205                id: channel_a_id,
 206                name: "channel-a".into(),
 207                depth: 0,
 208            },
 209            ExpectedChannel {
 210                id: channel_b_id,
 211                name: "channel-b".into(),
 212                depth: 1,
 213            },
 214            ExpectedChannel {
 215                id: channel_c_id,
 216                name: "channel-c".into(),
 217                depth: 2,
 218            },
 219        ],
 220    );
 221
 222    // Client A deletes the channel, deletion also deletes subchannels.
 223    client_a
 224        .channel_store()
 225        .update(cx_a, |channel_store, _| {
 226            channel_store.remove_channel(channel_b_id)
 227        })
 228        .await
 229        .unwrap();
 230
 231    executor.run_until_parked();
 232    assert_channels(
 233        client_a.channel_store(),
 234        cx_a,
 235        &[ExpectedChannel {
 236            id: channel_a_id,
 237            name: "channel-a".into(),
 238            depth: 0,
 239        }],
 240    );
 241    assert_channels(
 242        client_b.channel_store(),
 243        cx_b,
 244        &[ExpectedChannel {
 245            id: channel_a_id,
 246            name: "channel-a".into(),
 247            depth: 0,
 248        }],
 249    );
 250
 251    // Remove client B
 252    client_a
 253        .channel_store()
 254        .update(cx_a, |channel_store, cx| {
 255            channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
 256        })
 257        .await
 258        .unwrap();
 259
 260    executor.run_until_parked();
 261
 262    // Client A still has their channel
 263    assert_channels(
 264        client_a.channel_store(),
 265        cx_a,
 266        &[ExpectedChannel {
 267            id: channel_a_id,
 268            name: "channel-a".into(),
 269            depth: 0,
 270        }],
 271    );
 272
 273    // Client B no longer has access to the channel
 274    assert_channels(client_b.channel_store(), cx_b, &[]);
 275
 276    server.forbid_connections();
 277    server.disconnect_client(client_a.peer_id().unwrap());
 278    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 279
 280    server
 281        .app_state
 282        .db
 283        .rename_channel(
 284            db::ChannelId::from_proto(channel_a_id.0),
 285            UserId::from_proto(client_a.id()),
 286            "channel-a-renamed",
 287        )
 288        .await
 289        .unwrap();
 290
 291    server.allow_connections();
 292    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 293    assert_channels(
 294        client_a.channel_store(),
 295        cx_a,
 296        &[ExpectedChannel {
 297            id: channel_a_id,
 298            name: "channel-a-renamed".into(),
 299            depth: 0,
 300        }],
 301    );
 302}
 303
 304#[track_caller]
 305fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
 306    assert_eq!(
 307        participants.iter().map(|p| p.id).collect::<Vec<_>>(),
 308        expected_partitipants
 309    );
 310}
 311
 312#[track_caller]
 313fn assert_members_eq(
 314    members: &[ChannelMembership],
 315    expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],
 316) {
 317    assert_eq!(
 318        members
 319            .iter()
 320            .map(|member| (member.user.id, member.role, member.kind))
 321            .collect::<Vec<_>>(),
 322        expected_members
 323    );
 324}
 325
 326#[gpui::test]
 327async fn test_joining_channel_ancestor_member(
 328    executor: BackgroundExecutor,
 329    cx_a: &mut TestAppContext,
 330    cx_b: &mut TestAppContext,
 331) {
 332    let mut server = TestServer::start(executor.clone()).await;
 333
 334    let client_a = server.create_client(cx_a, "user_a").await;
 335    let client_b = server.create_client(cx_b, "user_b").await;
 336
 337    let parent_id = server
 338        .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 339        .await;
 340
 341    let sub_id = client_a
 342        .channel_store()
 343        .update(cx_a, |channel_store, cx| {
 344            channel_store.create_channel("sub_channel", Some(parent_id), cx)
 345        })
 346        .await
 347        .unwrap();
 348
 349    let active_call_b = cx_b.read(ActiveCall::global);
 350
 351    assert!(
 352        active_call_b
 353            .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
 354            .await
 355            .is_ok()
 356    );
 357}
 358
 359#[gpui::test]
 360async fn test_channel_room(
 361    executor: BackgroundExecutor,
 362    cx_a: &mut TestAppContext,
 363    cx_b: &mut TestAppContext,
 364    cx_c: &mut TestAppContext,
 365) {
 366    let mut server = TestServer::start(executor.clone()).await;
 367    let client_a = server.create_client(cx_a, "user_a").await;
 368    let client_b = server.create_client(cx_b, "user_b").await;
 369    let client_c = server.create_client(cx_c, "user_c").await;
 370
 371    let zed_id = server
 372        .make_channel(
 373            "zed",
 374            None,
 375            (&client_a, cx_a),
 376            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 377        )
 378        .await;
 379
 380    let active_call_a = cx_a.read(ActiveCall::global);
 381    let active_call_b = cx_b.read(ActiveCall::global);
 382
 383    active_call_a
 384        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 385        .await
 386        .unwrap();
 387
 388    // Give everyone a chance to observe user A joining
 389    executor.run_until_parked();
 390    let room_a =
 391        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 392    cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx))));
 393
 394    cx_a.read(|cx| {
 395        client_a.channel_store().read_with(cx, |channels, _| {
 396            assert_participants_eq(
 397                channels.channel_participants(zed_id),
 398                &[client_a.user_id().unwrap()],
 399            );
 400        })
 401    });
 402
 403    assert_channels(
 404        client_b.channel_store(),
 405        cx_b,
 406        &[ExpectedChannel {
 407            id: zed_id,
 408            name: "zed".into(),
 409            depth: 0,
 410        }],
 411    );
 412    cx_b.read(|cx| {
 413        client_b.channel_store().read_with(cx, |channels, _| {
 414            assert_participants_eq(
 415                channels.channel_participants(zed_id),
 416                &[client_a.user_id().unwrap()],
 417            );
 418        })
 419    });
 420
 421    cx_c.read(|cx| {
 422        client_c.channel_store().read_with(cx, |channels, _| {
 423            assert_participants_eq(
 424                channels.channel_participants(zed_id),
 425                &[client_a.user_id().unwrap()],
 426            );
 427        })
 428    });
 429
 430    active_call_b
 431        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 432        .await
 433        .unwrap();
 434
 435    executor.run_until_parked();
 436
 437    cx_a.read(|cx| {
 438        client_a.channel_store().read_with(cx, |channels, _| {
 439            assert_participants_eq(
 440                channels.channel_participants(zed_id),
 441                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 442            );
 443        })
 444    });
 445
 446    cx_b.read(|cx| {
 447        client_b.channel_store().read_with(cx, |channels, _| {
 448            assert_participants_eq(
 449                channels.channel_participants(zed_id),
 450                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 451            );
 452        })
 453    });
 454
 455    cx_c.read(|cx| {
 456        client_c.channel_store().read_with(cx, |channels, _| {
 457            assert_participants_eq(
 458                channels.channel_participants(zed_id),
 459                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 460            );
 461        })
 462    });
 463
 464    let room_a =
 465        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 466    cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx))));
 467    assert_eq!(
 468        room_participants(&room_a, cx_a),
 469        RoomParticipants {
 470            remote: vec!["user_b".to_string()],
 471            pending: vec![]
 472        }
 473    );
 474
 475    let room_b =
 476        cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone()));
 477    cx_b.read(|cx| room_b.read_with(cx, |room, cx| assert!(room.is_connected(cx))));
 478    assert_eq!(
 479        room_participants(&room_b, cx_b),
 480        RoomParticipants {
 481            remote: vec!["user_a".to_string()],
 482            pending: vec![]
 483        }
 484    );
 485
 486    // Make sure that leaving and rejoining works
 487
 488    active_call_a
 489        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 490        .await
 491        .unwrap();
 492
 493    executor.run_until_parked();
 494
 495    cx_a.read(|cx| {
 496        client_a.channel_store().read_with(cx, |channels, _| {
 497            assert_participants_eq(
 498                channels.channel_participants(zed_id),
 499                &[client_b.user_id().unwrap()],
 500            );
 501        })
 502    });
 503
 504    cx_b.read(|cx| {
 505        client_b.channel_store().read_with(cx, |channels, _| {
 506            assert_participants_eq(
 507                channels.channel_participants(zed_id),
 508                &[client_b.user_id().unwrap()],
 509            );
 510        })
 511    });
 512
 513    cx_c.read(|cx| {
 514        client_c.channel_store().read_with(cx, |channels, _| {
 515            assert_participants_eq(
 516                channels.channel_participants(zed_id),
 517                &[client_b.user_id().unwrap()],
 518            );
 519        })
 520    });
 521
 522    active_call_b
 523        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 524        .await
 525        .unwrap();
 526
 527    executor.run_until_parked();
 528
 529    cx_a.read(|cx| {
 530        client_a.channel_store().read_with(cx, |channels, _| {
 531            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 532        })
 533    });
 534
 535    cx_b.read(|cx| {
 536        client_b.channel_store().read_with(cx, |channels, _| {
 537            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 538        })
 539    });
 540
 541    cx_c.read(|cx| {
 542        client_c.channel_store().read_with(cx, |channels, _| {
 543            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 544        })
 545    });
 546
 547    active_call_a
 548        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 549        .await
 550        .unwrap();
 551
 552    active_call_b
 553        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 554        .await
 555        .unwrap();
 556
 557    executor.run_until_parked();
 558
 559    let room_a =
 560        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 561    cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx))));
 562    assert_eq!(
 563        room_participants(&room_a, cx_a),
 564        RoomParticipants {
 565            remote: vec!["user_b".to_string()],
 566            pending: vec![]
 567        }
 568    );
 569
 570    let room_b =
 571        cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone()));
 572    cx_b.read(|cx| room_b.read_with(cx, |room, cx| assert!(room.is_connected(cx))));
 573    assert_eq!(
 574        room_participants(&room_b, cx_b),
 575        RoomParticipants {
 576            remote: vec!["user_a".to_string()],
 577            pending: vec![]
 578        }
 579    );
 580}
 581
 582#[gpui::test]
 583async fn test_channel_jumping(executor: BackgroundExecutor, cx_a: &mut TestAppContext) {
 584    let mut server = TestServer::start(executor.clone()).await;
 585    let client_a = server.create_client(cx_a, "user_a").await;
 586
 587    let zed_id = server
 588        .make_channel("zed", None, (&client_a, cx_a), &mut [])
 589        .await;
 590    let rust_id = server
 591        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 592        .await;
 593
 594    let active_call_a = cx_a.read(ActiveCall::global);
 595
 596    active_call_a
 597        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 598        .await
 599        .unwrap();
 600
 601    // Give everything a chance to observe user A joining
 602    executor.run_until_parked();
 603
 604    cx_a.read(|cx| {
 605        client_a.channel_store().read_with(cx, |channels, _| {
 606            assert_participants_eq(
 607                channels.channel_participants(zed_id),
 608                &[client_a.user_id().unwrap()],
 609            );
 610            assert_participants_eq(channels.channel_participants(rust_id), &[]);
 611        })
 612    });
 613
 614    active_call_a
 615        .update(cx_a, |active_call, cx| {
 616            active_call.join_channel(rust_id, cx)
 617        })
 618        .await
 619        .unwrap();
 620
 621    executor.run_until_parked();
 622
 623    cx_a.read(|cx| {
 624        client_a.channel_store().read_with(cx, |channels, _| {
 625            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 626            assert_participants_eq(
 627                channels.channel_participants(rust_id),
 628                &[client_a.user_id().unwrap()],
 629            );
 630        })
 631    });
 632}
 633
 634#[gpui::test]
 635async fn test_permissions_update_while_invited(
 636    executor: BackgroundExecutor,
 637    cx_a: &mut TestAppContext,
 638    cx_b: &mut TestAppContext,
 639) {
 640    let mut server = TestServer::start(executor.clone()).await;
 641    let client_a = server.create_client(cx_a, "user_a").await;
 642    let client_b = server.create_client(cx_b, "user_b").await;
 643
 644    let rust_id = server
 645        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 646        .await;
 647
 648    client_a
 649        .channel_store()
 650        .update(cx_a, |channel_store, cx| {
 651            channel_store.invite_member(
 652                rust_id,
 653                client_b.user_id().unwrap(),
 654                proto::ChannelRole::Member,
 655                cx,
 656            )
 657        })
 658        .await
 659        .unwrap();
 660
 661    executor.run_until_parked();
 662
 663    assert_channel_invitations(
 664        client_b.channel_store(),
 665        cx_b,
 666        &[ExpectedChannel {
 667            depth: 0,
 668            id: rust_id,
 669            name: "rust".into(),
 670        }],
 671    );
 672    assert_channels(client_b.channel_store(), cx_b, &[]);
 673
 674    // Update B's invite before they've accepted it
 675    client_a
 676        .channel_store()
 677        .update(cx_a, |channel_store, cx| {
 678            channel_store.set_member_role(
 679                rust_id,
 680                client_b.user_id().unwrap(),
 681                proto::ChannelRole::Admin,
 682                cx,
 683            )
 684        })
 685        .await
 686        .unwrap();
 687
 688    executor.run_until_parked();
 689
 690    assert_channel_invitations(
 691        client_b.channel_store(),
 692        cx_b,
 693        &[ExpectedChannel {
 694            depth: 0,
 695            id: rust_id,
 696            name: "rust".into(),
 697        }],
 698    );
 699    assert_channels(client_b.channel_store(), cx_b, &[]);
 700}
 701
 702#[gpui::test]
 703async fn test_channel_rename(
 704    executor: BackgroundExecutor,
 705    cx_a: &mut TestAppContext,
 706    cx_b: &mut TestAppContext,
 707) {
 708    let mut server = TestServer::start(executor.clone()).await;
 709    let client_a = server.create_client(cx_a, "user_a").await;
 710    let client_b = server.create_client(cx_b, "user_b").await;
 711
 712    let rust_id = server
 713        .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 714        .await;
 715
 716    // Rename the channel
 717    client_a
 718        .channel_store()
 719        .update(cx_a, |channel_store, cx| {
 720            channel_store.rename(rust_id, "#rust-archive", cx)
 721        })
 722        .await
 723        .unwrap();
 724
 725    executor.run_until_parked();
 726
 727    // Client A sees the channel with its new name.
 728    assert_channels(
 729        client_a.channel_store(),
 730        cx_a,
 731        &[ExpectedChannel {
 732            depth: 0,
 733            id: rust_id,
 734            name: "rust-archive".into(),
 735        }],
 736    );
 737
 738    // Client B sees the channel with its new name.
 739    assert_channels(
 740        client_b.channel_store(),
 741        cx_b,
 742        &[ExpectedChannel {
 743            depth: 0,
 744            id: rust_id,
 745            name: "rust-archive".into(),
 746        }],
 747    );
 748}
 749
 750#[gpui::test]
 751async fn test_call_from_channel(
 752    executor: BackgroundExecutor,
 753    cx_a: &mut TestAppContext,
 754    cx_b: &mut TestAppContext,
 755    cx_c: &mut TestAppContext,
 756) {
 757    let mut server = TestServer::start(executor.clone()).await;
 758    let client_a = server.create_client(cx_a, "user_a").await;
 759    let client_b = server.create_client(cx_b, "user_b").await;
 760    let client_c = server.create_client(cx_c, "user_c").await;
 761    server
 762        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 763        .await;
 764
 765    let channel_id = server
 766        .make_channel(
 767            "x",
 768            None,
 769            (&client_a, cx_a),
 770            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 771        )
 772        .await;
 773
 774    let active_call_a = cx_a.read(ActiveCall::global);
 775    let active_call_b = cx_b.read(ActiveCall::global);
 776
 777    active_call_a
 778        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 779        .await
 780        .unwrap();
 781
 782    // Client A calls client B while in the channel.
 783    active_call_a
 784        .update(cx_a, |call, cx| {
 785            call.invite(client_b.user_id().unwrap(), None, cx)
 786        })
 787        .await
 788        .unwrap();
 789
 790    // Client B accepts the call.
 791    executor.run_until_parked();
 792    active_call_b
 793        .update(cx_b, |call, cx| call.accept_incoming(cx))
 794        .await
 795        .unwrap();
 796
 797    // Client B sees that they are now in the channel
 798    executor.run_until_parked();
 799    cx_b.read(|cx| {
 800        active_call_b.read_with(cx, |call, cx| {
 801            assert_eq!(call.channel_id(cx), Some(channel_id));
 802        })
 803    });
 804    cx_b.read(|cx| {
 805        client_b.channel_store().read_with(cx, |channels, _| {
 806            assert_participants_eq(
 807                channels.channel_participants(channel_id),
 808                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 809            );
 810        })
 811    });
 812
 813    // Clients A and C also see that client B is in the channel.
 814    cx_a.read(|cx| {
 815        client_a.channel_store().read_with(cx, |channels, _| {
 816            assert_participants_eq(
 817                channels.channel_participants(channel_id),
 818                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 819            );
 820        })
 821    });
 822    cx_c.read(|cx| {
 823        client_c.channel_store().read_with(cx, |channels, _| {
 824            assert_participants_eq(
 825                channels.channel_participants(channel_id),
 826                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 827            );
 828        })
 829    });
 830}
 831
 832#[gpui::test]
 833async fn test_lost_channel_creation(
 834    executor: BackgroundExecutor,
 835    cx_a: &mut TestAppContext,
 836    cx_b: &mut TestAppContext,
 837) {
 838    let mut server = TestServer::start(executor.clone()).await;
 839    let client_a = server.create_client(cx_a, "user_a").await;
 840    let client_b = server.create_client(cx_b, "user_b").await;
 841
 842    server
 843        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 844        .await;
 845
 846    let channel_id = server
 847        .make_channel("x", None, (&client_a, cx_a), &mut [])
 848        .await;
 849
 850    // Invite a member
 851    client_a
 852        .channel_store()
 853        .update(cx_a, |channel_store, cx| {
 854            channel_store.invite_member(
 855                channel_id,
 856                client_b.user_id().unwrap(),
 857                proto::ChannelRole::Member,
 858                cx,
 859            )
 860        })
 861        .await
 862        .unwrap();
 863
 864    executor.run_until_parked();
 865
 866    // Sanity check, B has the invitation
 867    assert_channel_invitations(
 868        client_b.channel_store(),
 869        cx_b,
 870        &[ExpectedChannel {
 871            depth: 0,
 872            id: channel_id,
 873            name: "x".into(),
 874        }],
 875    );
 876
 877    // A creates a subchannel while the invite is still pending.
 878    let subchannel_id = client_a
 879        .channel_store()
 880        .update(cx_a, |channel_store, cx| {
 881            channel_store.create_channel("subchannel", Some(channel_id), cx)
 882        })
 883        .await
 884        .unwrap();
 885
 886    executor.run_until_parked();
 887
 888    // Make sure A sees their new channel
 889    assert_channels(
 890        client_a.channel_store(),
 891        cx_a,
 892        &[
 893            ExpectedChannel {
 894                depth: 0,
 895                id: channel_id,
 896                name: "x".into(),
 897            },
 898            ExpectedChannel {
 899                depth: 1,
 900                id: subchannel_id,
 901                name: "subchannel".into(),
 902            },
 903        ],
 904    );
 905
 906    // Client B accepts the invite
 907    client_b
 908        .channel_store()
 909        .update(cx_b, |channel_store, cx| {
 910            channel_store.respond_to_channel_invite(channel_id, true, cx)
 911        })
 912        .await
 913        .unwrap();
 914
 915    executor.run_until_parked();
 916
 917    // Client B should now see the channel
 918    assert_channels(
 919        client_b.channel_store(),
 920        cx_b,
 921        &[
 922            ExpectedChannel {
 923                depth: 0,
 924                id: channel_id,
 925                name: "x".into(),
 926            },
 927            ExpectedChannel {
 928                depth: 1,
 929                id: subchannel_id,
 930                name: "subchannel".into(),
 931            },
 932        ],
 933    );
 934}
 935
 936#[gpui::test]
 937async fn test_channel_link_notifications(
 938    executor: BackgroundExecutor,
 939    cx_a: &mut TestAppContext,
 940    cx_b: &mut TestAppContext,
 941    cx_c: &mut TestAppContext,
 942) {
 943    let mut server = TestServer::start(executor.clone()).await;
 944    let client_a = server.create_client(cx_a, "user_a").await;
 945    let client_b = server.create_client(cx_b, "user_b").await;
 946    let client_c = server.create_client(cx_c, "user_c").await;
 947
 948    let user_b = client_b.user_id().unwrap();
 949    let user_c = client_c.user_id().unwrap();
 950
 951    let channels = server
 952        .make_channel_tree(&[("zed", None)], (&client_a, cx_a))
 953        .await;
 954    let zed_channel = channels[0];
 955
 956    try_join_all(client_a.channel_store().update(cx_a, |channel_store, cx| {
 957        [
 958            channel_store.set_channel_visibility(zed_channel, proto::ChannelVisibility::Public, cx),
 959            channel_store.invite_member(zed_channel, user_b, proto::ChannelRole::Member, cx),
 960            channel_store.invite_member(zed_channel, user_c, proto::ChannelRole::Guest, cx),
 961        ]
 962    }))
 963    .await
 964    .unwrap();
 965
 966    executor.run_until_parked();
 967
 968    client_b
 969        .channel_store()
 970        .update(cx_b, |channel_store, cx| {
 971            channel_store.respond_to_channel_invite(zed_channel, true, cx)
 972        })
 973        .await
 974        .unwrap();
 975
 976    client_c
 977        .channel_store()
 978        .update(cx_c, |channel_store, cx| {
 979            channel_store.respond_to_channel_invite(zed_channel, true, cx)
 980        })
 981        .await
 982        .unwrap();
 983
 984    executor.run_until_parked();
 985
 986    // we have an admin (a), member (b) and guest (c) all part of the zed channel.
 987
 988    // create a new private channel, make it public, and move it under the previous one, and verify it shows for b and not c
 989    let active_channel = client_a
 990        .channel_store()
 991        .update(cx_a, |channel_store, cx| {
 992            channel_store.create_channel("active", Some(zed_channel), cx)
 993        })
 994        .await
 995        .unwrap();
 996
 997    executor.run_until_parked();
 998
 999    // the new channel shows for b and not c
1000    assert_channels_list_shape(
1001        client_a.channel_store(),
1002        cx_a,
1003        &[(zed_channel, 0), (active_channel, 1)],
1004    );
1005    assert_channels_list_shape(
1006        client_b.channel_store(),
1007        cx_b,
1008        &[(zed_channel, 0), (active_channel, 1)],
1009    );
1010    assert_channels_list_shape(client_c.channel_store(), cx_c, &[(zed_channel, 0)]);
1011
1012    let vim_channel = client_a
1013        .channel_store()
1014        .update(cx_a, |channel_store, cx| {
1015            channel_store.create_channel("vim", Some(zed_channel), cx)
1016        })
1017        .await
1018        .unwrap();
1019
1020    client_a
1021        .channel_store()
1022        .update(cx_a, |channel_store, cx| {
1023            channel_store.set_channel_visibility(vim_channel, proto::ChannelVisibility::Public, cx)
1024        })
1025        .await
1026        .unwrap();
1027
1028    executor.run_until_parked();
1029
1030    // the new channel shows for b and c
1031    assert_channels_list_shape(
1032        client_a.channel_store(),
1033        cx_a,
1034        &[(zed_channel, 0), (active_channel, 1), (vim_channel, 1)],
1035    );
1036    assert_channels_list_shape(
1037        client_b.channel_store(),
1038        cx_b,
1039        &[(zed_channel, 0), (active_channel, 1), (vim_channel, 1)],
1040    );
1041    assert_channels_list_shape(
1042        client_c.channel_store(),
1043        cx_c,
1044        &[(zed_channel, 0), (vim_channel, 1)],
1045    );
1046
1047    let helix_channel = client_a
1048        .channel_store()
1049        .update(cx_a, |channel_store, cx| {
1050            channel_store.create_channel("helix", Some(zed_channel), cx)
1051        })
1052        .await
1053        .unwrap();
1054
1055    client_a
1056        .channel_store()
1057        .update(cx_a, |channel_store, cx| {
1058            channel_store.move_channel(helix_channel, vim_channel, cx)
1059        })
1060        .await
1061        .unwrap();
1062
1063    client_a
1064        .channel_store()
1065        .update(cx_a, |channel_store, cx| {
1066            channel_store.set_channel_visibility(
1067                helix_channel,
1068                proto::ChannelVisibility::Public,
1069                cx,
1070            )
1071        })
1072        .await
1073        .unwrap();
1074    cx_a.run_until_parked();
1075
1076    // the new channel shows for b and c
1077    assert_channels_list_shape(
1078        client_b.channel_store(),
1079        cx_b,
1080        &[
1081            (zed_channel, 0),
1082            (active_channel, 1),
1083            (vim_channel, 1),
1084            (helix_channel, 2),
1085        ],
1086    );
1087    assert_channels_list_shape(
1088        client_c.channel_store(),
1089        cx_c,
1090        &[(zed_channel, 0), (vim_channel, 1), (helix_channel, 2)],
1091    );
1092}
1093
1094#[gpui::test]
1095async fn test_channel_membership_notifications(
1096    executor: BackgroundExecutor,
1097    cx_a: &mut TestAppContext,
1098    cx_b: &mut TestAppContext,
1099) {
1100    let mut server = TestServer::start(executor.clone()).await;
1101    let client_a = server.create_client(cx_a, "user_a").await;
1102    let client_b = server.create_client(cx_b, "user_c").await;
1103
1104    let user_b = client_b.user_id().unwrap();
1105
1106    let channels = server
1107        .make_channel_tree(
1108            &[("zed", None), ("vim", Some("zed")), ("opensource", None)],
1109            (&client_a, cx_a),
1110        )
1111        .await;
1112    let zed_channel = channels[0];
1113    let vim_channel = channels[1];
1114    let opensource_channel = channels[2];
1115
1116    try_join_all(client_a.channel_store().update(cx_a, |channel_store, cx| {
1117        [
1118            channel_store.set_channel_visibility(zed_channel, proto::ChannelVisibility::Public, cx),
1119            channel_store.set_channel_visibility(vim_channel, proto::ChannelVisibility::Public, cx),
1120            channel_store.invite_member(zed_channel, user_b, proto::ChannelRole::Admin, cx),
1121            channel_store.invite_member(opensource_channel, user_b, proto::ChannelRole::Member, cx),
1122        ]
1123    }))
1124    .await
1125    .unwrap();
1126
1127    executor.run_until_parked();
1128
1129    client_b
1130        .channel_store()
1131        .update(cx_b, |channel_store, cx| {
1132            channel_store.respond_to_channel_invite(zed_channel, true, cx)
1133        })
1134        .await
1135        .unwrap();
1136
1137    executor.run_until_parked();
1138
1139    // we have an admin (a), and a guest (b) with access to all of zed, and membership in vim.
1140    assert_channels(
1141        client_b.channel_store(),
1142        cx_b,
1143        &[
1144            ExpectedChannel {
1145                depth: 0,
1146                id: zed_channel,
1147                name: "zed".into(),
1148            },
1149            ExpectedChannel {
1150                depth: 1,
1151                id: vim_channel,
1152                name: "vim".into(),
1153            },
1154        ],
1155    );
1156
1157    client_b.channel_store().update(cx_b, |channel_store, _| {
1158        channel_store.is_channel_admin(zed_channel)
1159    });
1160
1161    client_b
1162        .channel_store()
1163        .update(cx_b, |channel_store, cx| {
1164            channel_store.respond_to_channel_invite(opensource_channel, true, cx)
1165        })
1166        .await
1167        .unwrap();
1168
1169    cx_a.run_until_parked();
1170
1171    client_a
1172        .channel_store()
1173        .update(cx_a, |channel_store, cx| {
1174            channel_store.set_member_role(opensource_channel, user_b, ChannelRole::Admin, cx)
1175        })
1176        .await
1177        .unwrap();
1178
1179    cx_a.run_until_parked();
1180
1181    client_b.channel_store().update(cx_b, |channel_store, _| {
1182        channel_store.is_channel_admin(opensource_channel)
1183    });
1184}
1185
1186#[gpui::test]
1187async fn test_guest_access(
1188    executor: BackgroundExecutor,
1189    cx_a: &mut TestAppContext,
1190    cx_b: &mut TestAppContext,
1191) {
1192    let mut server = TestServer::start(executor.clone()).await;
1193    let client_a = server.create_client(cx_a, "user_a").await;
1194    let client_b = server.create_client(cx_b, "user_b").await;
1195
1196    let channels = server
1197        .make_channel_tree(
1198            &[("channel-a", None), ("channel-b", Some("channel-a"))],
1199            (&client_a, cx_a),
1200        )
1201        .await;
1202    let channel_a = channels[0];
1203    let channel_b = channels[1];
1204
1205    let active_call_b = cx_b.read(ActiveCall::global);
1206
1207    // Non-members should not be allowed to join
1208    assert!(
1209        active_call_b
1210            .update(cx_b, |call, cx| call.join_channel(channel_a, cx))
1211            .await
1212            .is_err()
1213    );
1214
1215    // Make channels A and B public
1216    client_a
1217        .channel_store()
1218        .update(cx_a, |channel_store, cx| {
1219            channel_store.set_channel_visibility(channel_a, proto::ChannelVisibility::Public, cx)
1220        })
1221        .await
1222        .unwrap();
1223    client_a
1224        .channel_store()
1225        .update(cx_a, |channel_store, cx| {
1226            channel_store.set_channel_visibility(channel_b, proto::ChannelVisibility::Public, cx)
1227        })
1228        .await
1229        .unwrap();
1230
1231    // Client B joins channel A as a guest
1232    active_call_b
1233        .update(cx_b, |call, cx| call.join_channel(channel_a, cx))
1234        .await
1235        .unwrap();
1236
1237    executor.run_until_parked();
1238    assert_channels_list_shape(
1239        client_a.channel_store(),
1240        cx_a,
1241        &[(channel_a, 0), (channel_b, 1)],
1242    );
1243    assert_channels_list_shape(
1244        client_b.channel_store(),
1245        cx_b,
1246        &[(channel_a, 0), (channel_b, 1)],
1247    );
1248
1249    client_a.channel_store().update(cx_a, |channel_store, _| {
1250        let participants = channel_store.channel_participants(channel_a);
1251        assert_eq!(participants.len(), 1);
1252        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1253    });
1254}
1255
1256#[gpui::test]
1257async fn test_invite_access(
1258    executor: BackgroundExecutor,
1259    cx_a: &mut TestAppContext,
1260    cx_b: &mut TestAppContext,
1261) {
1262    let mut server = TestServer::start(executor.clone()).await;
1263    let client_a = server.create_client(cx_a, "user_a").await;
1264    let client_b = server.create_client(cx_b, "user_b").await;
1265
1266    let channels = server
1267        .make_channel_tree(
1268            &[("channel-a", None), ("channel-b", Some("channel-a"))],
1269            (&client_a, cx_a),
1270        )
1271        .await;
1272    let channel_a_id = channels[0];
1273    let channel_b_id = channels[0];
1274
1275    let active_call_b = cx_b.read(ActiveCall::global);
1276
1277    // should not be allowed to join
1278    assert!(
1279        active_call_b
1280            .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1281            .await
1282            .is_err()
1283    );
1284
1285    client_a
1286        .channel_store()
1287        .update(cx_a, |channel_store, cx| {
1288            channel_store.invite_member(
1289                channel_a_id,
1290                client_b.user_id().unwrap(),
1291                ChannelRole::Member,
1292                cx,
1293            )
1294        })
1295        .await
1296        .unwrap();
1297
1298    active_call_b
1299        .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1300        .await
1301        .unwrap();
1302
1303    executor.run_until_parked();
1304
1305    client_b.channel_store().update(cx_b, |channel_store, _| {
1306        assert!(channel_store.channel_for_id(channel_b_id).is_some());
1307        assert!(channel_store.channel_for_id(channel_a_id).is_some());
1308    });
1309
1310    client_a.channel_store().update(cx_a, |channel_store, _| {
1311        let participants = channel_store.channel_participants(channel_b_id);
1312        assert_eq!(participants.len(), 1);
1313        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1314    })
1315}
1316
1317#[gpui::test]
1318async fn test_leave_channel(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1319    let (_server, _client_a, client_b, channel_id) = TestServer::start2(cx_a, cx_b).await;
1320
1321    client_b
1322        .channel_store()
1323        .update(cx_b, |channel_store, cx| {
1324            channel_store.remove_member(channel_id, client_b.user_id().unwrap(), cx)
1325        })
1326        .await
1327        .unwrap();
1328
1329    cx_a.run_until_parked();
1330
1331    assert_eq!(
1332        client_b
1333            .channel_store()
1334            .read_with(cx_b, |store, _| store.channels().count()),
1335        0
1336    );
1337}
1338
1339#[gpui::test]
1340async fn test_channel_moving(
1341    executor: BackgroundExecutor,
1342    cx_a: &mut TestAppContext,
1343    _cx_b: &mut TestAppContext,
1344    _cx_c: &mut TestAppContext,
1345) {
1346    let mut server = TestServer::start(executor.clone()).await;
1347    let client_a = server.create_client(cx_a, "user_a").await;
1348
1349    let channels = server
1350        .make_channel_tree(
1351            &[
1352                ("channel-a", None),
1353                ("channel-b", Some("channel-a")),
1354                ("channel-c", Some("channel-b")),
1355                ("channel-d", Some("channel-c")),
1356            ],
1357            (&client_a, cx_a),
1358        )
1359        .await;
1360    let channel_a_id = channels[0];
1361    let channel_b_id = channels[1];
1362    let channel_c_id = channels[2];
1363    let channel_d_id = channels[3];
1364
1365    // Current shape:
1366    // a - b - c - d
1367    assert_channels_list_shape(
1368        client_a.channel_store(),
1369        cx_a,
1370        &[
1371            (channel_a_id, 0),
1372            (channel_b_id, 1),
1373            (channel_c_id, 2),
1374            (channel_d_id, 3),
1375        ],
1376    );
1377
1378    client_a
1379        .channel_store()
1380        .update(cx_a, |channel_store, cx| {
1381            channel_store.move_channel(channel_d_id, channel_b_id, cx)
1382        })
1383        .await
1384        .unwrap();
1385
1386    // Current shape:
1387    //       /- d
1388    // a - b -- c
1389    assert_channels_list_shape(
1390        client_a.channel_store(),
1391        cx_a,
1392        &[
1393            (channel_a_id, 0),
1394            (channel_b_id, 1),
1395            (channel_c_id, 2),
1396            (channel_d_id, 2),
1397        ],
1398    );
1399}
1400
1401#[derive(Debug, PartialEq)]
1402struct ExpectedChannel {
1403    depth: usize,
1404    id: ChannelId,
1405    name: SharedString,
1406}
1407
1408#[track_caller]
1409fn assert_channel_invitations(
1410    channel_store: &Entity<ChannelStore>,
1411    cx: &TestAppContext,
1412    expected_channels: &[ExpectedChannel],
1413) {
1414    let actual = cx.read(|cx| {
1415        channel_store.read_with(cx, |store, _| {
1416            store
1417                .channel_invitations()
1418                .iter()
1419                .map(|channel| ExpectedChannel {
1420                    depth: 0,
1421                    name: channel.name.clone(),
1422                    id: channel.id,
1423                })
1424                .collect::<Vec<_>>()
1425        })
1426    });
1427    assert_eq!(actual, expected_channels);
1428}
1429
1430#[track_caller]
1431fn assert_channels(
1432    channel_store: &Entity<ChannelStore>,
1433    cx: &TestAppContext,
1434    expected_channels: &[ExpectedChannel],
1435) {
1436    let actual = cx.read(|cx| {
1437        channel_store.read_with(cx, |store, _| {
1438            store
1439                .ordered_channels()
1440                .map(|(depth, channel)| ExpectedChannel {
1441                    depth,
1442                    name: channel.name.clone(),
1443                    id: channel.id,
1444                })
1445                .collect::<Vec<_>>()
1446        })
1447    });
1448    pretty_assertions::assert_eq!(actual, expected_channels);
1449}
1450
1451#[track_caller]
1452fn assert_channels_list_shape(
1453    channel_store: &Entity<ChannelStore>,
1454    cx: &TestAppContext,
1455    expected_channels: &[(ChannelId, usize)],
1456) {
1457    let actual = cx.read(|cx| {
1458        channel_store.read_with(cx, |store, _| {
1459            store
1460                .ordered_channels()
1461                .map(|(depth, channel)| (channel.id, depth))
1462                .collect::<Vec<_>>()
1463        })
1464    });
1465    pretty_assertions::assert_eq!(actual, expected_channels);
1466}