channel_tests.rs

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