channel_tests.rs

   1use crate::{
   2    db::{self, UserId},
   3    rpc::RECONNECT_TIMEOUT,
   4    tests::{room_participants, test_server::join_channel_call, RoomParticipants, TestServer},
   5};
   6use call::ActiveCall;
   7use channel::{ChannelId, ChannelMembership, ChannelStore};
   8use client::User;
   9use futures::future::try_join_all;
  10use gpui::{BackgroundExecutor, Model, SharedString, 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".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.get_channel_member_details(channel_a_id, 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),
 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!(active_call_b
 352        .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
 353        .await
 354        .is_ok());
 355}
 356
 357#[gpui::test]
 358async fn test_channel_room(
 359    executor: BackgroundExecutor,
 360    cx_a: &mut TestAppContext,
 361    cx_b: &mut TestAppContext,
 362    cx_c: &mut TestAppContext,
 363) {
 364    let mut server = TestServer::start(executor.clone()).await;
 365    let client_a = server.create_client(cx_a, "user_a").await;
 366    let client_b = server.create_client(cx_b, "user_b").await;
 367    let client_c = server.create_client(cx_c, "user_c").await;
 368
 369    let zed_id = server
 370        .make_channel(
 371            "zed",
 372            None,
 373            (&client_a, cx_a),
 374            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 375        )
 376        .await;
 377
 378    let active_call_a = cx_a.read(ActiveCall::global);
 379    let active_call_b = cx_b.read(ActiveCall::global);
 380
 381    active_call_a
 382        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 383        .await
 384        .unwrap();
 385    join_channel_call(cx_a).await.unwrap();
 386
 387    // Give everyone a chance to observe user A joining
 388    executor.run_until_parked();
 389    let room_a =
 390        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 391    cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected())));
 392
 393    cx_a.read(|cx| {
 394        client_a.channel_store().read_with(cx, |channels, _| {
 395            assert_participants_eq(
 396                channels.channel_participants(zed_id),
 397                &[client_a.user_id().unwrap()],
 398            );
 399        })
 400    });
 401
 402    assert_channels(
 403        client_b.channel_store(),
 404        cx_b,
 405        &[ExpectedChannel {
 406            id: zed_id,
 407            name: "zed".into(),
 408            depth: 0,
 409        }],
 410    );
 411    cx_b.read(|cx| {
 412        client_b.channel_store().read_with(cx, |channels, _| {
 413            assert_participants_eq(
 414                channels.channel_participants(zed_id),
 415                &[client_a.user_id().unwrap()],
 416            );
 417        })
 418    });
 419
 420    cx_c.read(|cx| {
 421        client_c.channel_store().read_with(cx, |channels, _| {
 422            assert_participants_eq(
 423                channels.channel_participants(zed_id),
 424                &[client_a.user_id().unwrap()],
 425            );
 426        })
 427    });
 428
 429    active_call_b
 430        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 431        .await
 432        .unwrap();
 433    join_channel_call(cx_b).await.unwrap();
 434    executor.run_until_parked();
 435
 436    cx_a.read(|cx| {
 437        client_a.channel_store().read_with(cx, |channels, _| {
 438            assert_participants_eq(
 439                channels.channel_participants(zed_id),
 440                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 441            );
 442        })
 443    });
 444
 445    cx_b.read(|cx| {
 446        client_b.channel_store().read_with(cx, |channels, _| {
 447            assert_participants_eq(
 448                channels.channel_participants(zed_id),
 449                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 450            );
 451        })
 452    });
 453
 454    cx_c.read(|cx| {
 455        client_c.channel_store().read_with(cx, |channels, _| {
 456            assert_participants_eq(
 457                channels.channel_participants(zed_id),
 458                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 459            );
 460        })
 461    });
 462
 463    let room_a =
 464        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 465    cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected())));
 466    assert_eq!(
 467        room_participants(&room_a, cx_a),
 468        RoomParticipants {
 469            remote: vec!["user_b".to_string()],
 470            pending: vec![]
 471        }
 472    );
 473
 474    let room_b =
 475        cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone()));
 476    cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected())));
 477    assert_eq!(
 478        room_participants(&room_b, cx_b),
 479        RoomParticipants {
 480            remote: vec!["user_a".to_string()],
 481            pending: vec![]
 482        }
 483    );
 484
 485    // Make sure that leaving and rejoining works
 486
 487    active_call_a
 488        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 489        .await
 490        .unwrap();
 491
 492    executor.run_until_parked();
 493
 494    cx_a.read(|cx| {
 495        client_a.channel_store().read_with(cx, |channels, _| {
 496            assert_participants_eq(
 497                channels.channel_participants(zed_id),
 498                &[client_b.user_id().unwrap()],
 499            );
 500        })
 501    });
 502
 503    cx_b.read(|cx| {
 504        client_b.channel_store().read_with(cx, |channels, _| {
 505            assert_participants_eq(
 506                channels.channel_participants(zed_id),
 507                &[client_b.user_id().unwrap()],
 508            );
 509        })
 510    });
 511
 512    cx_c.read(|cx| {
 513        client_c.channel_store().read_with(cx, |channels, _| {
 514            assert_participants_eq(
 515                channels.channel_participants(zed_id),
 516                &[client_b.user_id().unwrap()],
 517            );
 518        })
 519    });
 520
 521    active_call_b
 522        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 523        .await
 524        .unwrap();
 525
 526    executor.run_until_parked();
 527
 528    cx_a.read(|cx| {
 529        client_a.channel_store().read_with(cx, |channels, _| {
 530            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 531        })
 532    });
 533
 534    cx_b.read(|cx| {
 535        client_b.channel_store().read_with(cx, |channels, _| {
 536            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 537        })
 538    });
 539
 540    cx_c.read(|cx| {
 541        client_c.channel_store().read_with(cx, |channels, _| {
 542            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 543        })
 544    });
 545
 546    active_call_a
 547        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 548        .await
 549        .unwrap();
 550
 551    active_call_b
 552        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 553        .await
 554        .unwrap();
 555
 556    join_channel_call(cx_a).await.unwrap();
 557    join_channel_call(cx_b).await.unwrap();
 558
 559    executor.run_until_parked();
 560
 561    let room_a =
 562        cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone()));
 563    cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected())));
 564    assert_eq!(
 565        room_participants(&room_a, cx_a),
 566        RoomParticipants {
 567            remote: vec!["user_b".to_string()],
 568            pending: vec![]
 569        }
 570    );
 571
 572    let room_b =
 573        cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone()));
 574    cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected())));
 575    assert_eq!(
 576        room_participants(&room_b, cx_b),
 577        RoomParticipants {
 578            remote: vec!["user_a".to_string()],
 579            pending: vec![]
 580        }
 581    );
 582}
 583
 584#[gpui::test]
 585async fn test_channel_jumping(executor: BackgroundExecutor, cx_a: &mut TestAppContext) {
 586    let mut server = TestServer::start(executor.clone()).await;
 587    let client_a = server.create_client(cx_a, "user_a").await;
 588
 589    let zed_id = server
 590        .make_channel("zed", None, (&client_a, cx_a), &mut [])
 591        .await;
 592    let rust_id = server
 593        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 594        .await;
 595
 596    let active_call_a = cx_a.read(ActiveCall::global);
 597
 598    active_call_a
 599        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 600        .await
 601        .unwrap();
 602
 603    // Give everything a chance to observe user A joining
 604    executor.run_until_parked();
 605
 606    cx_a.read(|cx| {
 607        client_a.channel_store().read_with(cx, |channels, _| {
 608            assert_participants_eq(
 609                channels.channel_participants(zed_id),
 610                &[client_a.user_id().unwrap()],
 611            );
 612            assert_participants_eq(channels.channel_participants(rust_id), &[]);
 613        })
 614    });
 615
 616    active_call_a
 617        .update(cx_a, |active_call, cx| {
 618            active_call.join_channel(rust_id, cx)
 619        })
 620        .await
 621        .unwrap();
 622
 623    executor.run_until_parked();
 624
 625    cx_a.read(|cx| {
 626        client_a.channel_store().read_with(cx, |channels, _| {
 627            assert_participants_eq(channels.channel_participants(zed_id), &[]);
 628            assert_participants_eq(
 629                channels.channel_participants(rust_id),
 630                &[client_a.user_id().unwrap()],
 631            );
 632        })
 633    });
 634}
 635
 636#[gpui::test]
 637async fn test_permissions_update_while_invited(
 638    executor: BackgroundExecutor,
 639    cx_a: &mut TestAppContext,
 640    cx_b: &mut TestAppContext,
 641) {
 642    let mut server = TestServer::start(executor.clone()).await;
 643    let client_a = server.create_client(cx_a, "user_a").await;
 644    let client_b = server.create_client(cx_b, "user_b").await;
 645
 646    let rust_id = server
 647        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 648        .await;
 649
 650    client_a
 651        .channel_store()
 652        .update(cx_a, |channel_store, cx| {
 653            channel_store.invite_member(
 654                rust_id,
 655                client_b.user_id().unwrap(),
 656                proto::ChannelRole::Member,
 657                cx,
 658            )
 659        })
 660        .await
 661        .unwrap();
 662
 663    executor.run_until_parked();
 664
 665    assert_channel_invitations(
 666        client_b.channel_store(),
 667        cx_b,
 668        &[ExpectedChannel {
 669            depth: 0,
 670            id: rust_id,
 671            name: "rust".into(),
 672        }],
 673    );
 674    assert_channels(client_b.channel_store(), cx_b, &[]);
 675
 676    // Update B's invite before they've accepted it
 677    client_a
 678        .channel_store()
 679        .update(cx_a, |channel_store, cx| {
 680            channel_store.set_member_role(
 681                rust_id,
 682                client_b.user_id().unwrap(),
 683                proto::ChannelRole::Admin,
 684                cx,
 685            )
 686        })
 687        .await
 688        .unwrap();
 689
 690    executor.run_until_parked();
 691
 692    assert_channel_invitations(
 693        client_b.channel_store(),
 694        cx_b,
 695        &[ExpectedChannel {
 696            depth: 0,
 697            id: rust_id,
 698            name: "rust".into(),
 699        }],
 700    );
 701    assert_channels(client_b.channel_store(), cx_b, &[]);
 702}
 703
 704#[gpui::test]
 705async fn test_channel_rename(
 706    executor: BackgroundExecutor,
 707    cx_a: &mut TestAppContext,
 708    cx_b: &mut TestAppContext,
 709) {
 710    let mut server = TestServer::start(executor.clone()).await;
 711    let client_a = server.create_client(cx_a, "user_a").await;
 712    let client_b = server.create_client(cx_b, "user_b").await;
 713
 714    let rust_id = server
 715        .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 716        .await;
 717
 718    // Rename the channel
 719    client_a
 720        .channel_store()
 721        .update(cx_a, |channel_store, cx| {
 722            channel_store.rename(rust_id, "#rust-archive", cx)
 723        })
 724        .await
 725        .unwrap();
 726
 727    executor.run_until_parked();
 728
 729    // Client A sees the channel with its new name.
 730    assert_channels(
 731        client_a.channel_store(),
 732        cx_a,
 733        &[ExpectedChannel {
 734            depth: 0,
 735            id: rust_id,
 736            name: "rust-archive".into(),
 737        }],
 738    );
 739
 740    // Client B sees the channel with its new name.
 741    assert_channels(
 742        client_b.channel_store(),
 743        cx_b,
 744        &[ExpectedChannel {
 745            depth: 0,
 746            id: rust_id,
 747            name: "rust-archive".into(),
 748        }],
 749    );
 750}
 751
 752#[gpui::test]
 753async fn test_call_from_channel(
 754    executor: BackgroundExecutor,
 755    cx_a: &mut TestAppContext,
 756    cx_b: &mut TestAppContext,
 757    cx_c: &mut TestAppContext,
 758) {
 759    let mut server = TestServer::start(executor.clone()).await;
 760    let client_a = server.create_client(cx_a, "user_a").await;
 761    let client_b = server.create_client(cx_b, "user_b").await;
 762    let client_c = server.create_client(cx_c, "user_c").await;
 763    server
 764        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 765        .await;
 766
 767    let channel_id = server
 768        .make_channel(
 769            "x",
 770            None,
 771            (&client_a, cx_a),
 772            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 773        )
 774        .await;
 775
 776    let active_call_a = cx_a.read(ActiveCall::global);
 777    let active_call_b = cx_b.read(ActiveCall::global);
 778
 779    active_call_a
 780        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 781        .await
 782        .unwrap();
 783
 784    // Client A calls client B while in the channel.
 785    active_call_a
 786        .update(cx_a, |call, cx| {
 787            call.invite(client_b.user_id().unwrap(), None, cx)
 788        })
 789        .await
 790        .unwrap();
 791
 792    // Client B accepts the call.
 793    executor.run_until_parked();
 794    active_call_b
 795        .update(cx_b, |call, cx| call.accept_incoming(cx))
 796        .await
 797        .unwrap();
 798
 799    // Client B sees that they are now in the channel
 800    executor.run_until_parked();
 801    cx_b.read(|cx| {
 802        active_call_b.read_with(cx, |call, cx| {
 803            assert_eq!(call.channel_id(cx), Some(channel_id));
 804        })
 805    });
 806    cx_b.read(|cx| {
 807        client_b.channel_store().read_with(cx, |channels, _| {
 808            assert_participants_eq(
 809                channels.channel_participants(channel_id),
 810                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 811            );
 812        })
 813    });
 814
 815    // Clients A and C also see that client B is in the channel.
 816    cx_a.read(|cx| {
 817        client_a.channel_store().read_with(cx, |channels, _| {
 818            assert_participants_eq(
 819                channels.channel_participants(channel_id),
 820                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 821            );
 822        })
 823    });
 824    cx_c.read(|cx| {
 825        client_c.channel_store().read_with(cx, |channels, _| {
 826            assert_participants_eq(
 827                channels.channel_participants(channel_id),
 828                &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 829            );
 830        })
 831    });
 832}
 833
 834#[gpui::test]
 835async fn test_lost_channel_creation(
 836    executor: BackgroundExecutor,
 837    cx_a: &mut TestAppContext,
 838    cx_b: &mut TestAppContext,
 839) {
 840    let mut server = TestServer::start(executor.clone()).await;
 841    let client_a = server.create_client(cx_a, "user_a").await;
 842    let client_b = server.create_client(cx_b, "user_b").await;
 843
 844    server
 845        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 846        .await;
 847
 848    let channel_id = server
 849        .make_channel("x", None, (&client_a, cx_a), &mut [])
 850        .await;
 851
 852    // Invite a member
 853    client_a
 854        .channel_store()
 855        .update(cx_a, |channel_store, cx| {
 856            channel_store.invite_member(
 857                channel_id,
 858                client_b.user_id().unwrap(),
 859                proto::ChannelRole::Member,
 860                cx,
 861            )
 862        })
 863        .await
 864        .unwrap();
 865
 866    executor.run_until_parked();
 867
 868    // Sanity check, B has the invitation
 869    assert_channel_invitations(
 870        client_b.channel_store(),
 871        cx_b,
 872        &[ExpectedChannel {
 873            depth: 0,
 874            id: channel_id,
 875            name: "x".into(),
 876        }],
 877    );
 878
 879    // A creates a subchannel while the invite is still pending.
 880    let subchannel_id = client_a
 881        .channel_store()
 882        .update(cx_a, |channel_store, cx| {
 883            channel_store.create_channel("subchannel", Some(channel_id), cx)
 884        })
 885        .await
 886        .unwrap();
 887
 888    executor.run_until_parked();
 889
 890    // Make sure A sees their new channel
 891    assert_channels(
 892        client_a.channel_store(),
 893        cx_a,
 894        &[
 895            ExpectedChannel {
 896                depth: 0,
 897                id: channel_id,
 898                name: "x".into(),
 899            },
 900            ExpectedChannel {
 901                depth: 1,
 902                id: subchannel_id,
 903                name: "subchannel".into(),
 904            },
 905        ],
 906    );
 907
 908    // Client B accepts the invite
 909    client_b
 910        .channel_store()
 911        .update(cx_b, |channel_store, cx| {
 912            channel_store.respond_to_channel_invite(channel_id, true, cx)
 913        })
 914        .await
 915        .unwrap();
 916
 917    executor.run_until_parked();
 918
 919    // Client B should now see the channel
 920    assert_channels(
 921        client_b.channel_store(),
 922        cx_b,
 923        &[
 924            ExpectedChannel {
 925                depth: 0,
 926                id: channel_id,
 927                name: "x".into(),
 928            },
 929            ExpectedChannel {
 930                depth: 1,
 931                id: subchannel_id,
 932                name: "subchannel".into(),
 933            },
 934        ],
 935    );
 936}
 937
 938#[gpui::test]
 939async fn test_channel_link_notifications(
 940    executor: BackgroundExecutor,
 941    cx_a: &mut TestAppContext,
 942    cx_b: &mut TestAppContext,
 943    cx_c: &mut TestAppContext,
 944) {
 945    let mut server = TestServer::start(executor.clone()).await;
 946    let client_a = server.create_client(cx_a, "user_a").await;
 947    let client_b = server.create_client(cx_b, "user_b").await;
 948    let client_c = server.create_client(cx_c, "user_c").await;
 949
 950    let user_b = client_b.user_id().unwrap();
 951    let user_c = client_c.user_id().unwrap();
 952
 953    let channels = server
 954        .make_channel_tree(&[("zed", None)], (&client_a, cx_a))
 955        .await;
 956    let zed_channel = channels[0];
 957
 958    try_join_all(client_a.channel_store().update(cx_a, |channel_store, cx| {
 959        [
 960            channel_store.set_channel_visibility(zed_channel, proto::ChannelVisibility::Public, cx),
 961            channel_store.invite_member(zed_channel, user_b, proto::ChannelRole::Member, cx),
 962            channel_store.invite_member(zed_channel, user_c, proto::ChannelRole::Guest, cx),
 963        ]
 964    }))
 965    .await
 966    .unwrap();
 967
 968    executor.run_until_parked();
 969
 970    client_b
 971        .channel_store()
 972        .update(cx_b, |channel_store, cx| {
 973            channel_store.respond_to_channel_invite(zed_channel, true, cx)
 974        })
 975        .await
 976        .unwrap();
 977
 978    client_c
 979        .channel_store()
 980        .update(cx_c, |channel_store, cx| {
 981            channel_store.respond_to_channel_invite(zed_channel, true, cx)
 982        })
 983        .await
 984        .unwrap();
 985
 986    executor.run_until_parked();
 987
 988    // we have an admin (a), member (b) and guest (c) all part of the zed channel.
 989
 990    // create a new private channel, make it public, and move it under the previous one, and verify it shows for b and not c
 991    let active_channel = client_a
 992        .channel_store()
 993        .update(cx_a, |channel_store, cx| {
 994            channel_store.create_channel("active", Some(zed_channel), cx)
 995        })
 996        .await
 997        .unwrap();
 998
 999    executor.run_until_parked();
1000
1001    // the new channel shows for b and not c
1002    assert_channels_list_shape(
1003        client_a.channel_store(),
1004        cx_a,
1005        &[(zed_channel, 0), (active_channel, 1)],
1006    );
1007    assert_channels_list_shape(
1008        client_b.channel_store(),
1009        cx_b,
1010        &[(zed_channel, 0), (active_channel, 1)],
1011    );
1012    assert_channels_list_shape(client_c.channel_store(), cx_c, &[(zed_channel, 0)]);
1013
1014    let vim_channel = client_a
1015        .channel_store()
1016        .update(cx_a, |channel_store, cx| {
1017            channel_store.create_channel("vim", Some(zed_channel), cx)
1018        })
1019        .await
1020        .unwrap();
1021
1022    client_a
1023        .channel_store()
1024        .update(cx_a, |channel_store, cx| {
1025            channel_store.set_channel_visibility(vim_channel, proto::ChannelVisibility::Public, cx)
1026        })
1027        .await
1028        .unwrap();
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!(active_call_b
1209        .update(cx_b, |call, cx| call.join_channel(channel_a, cx))
1210        .await
1211        .is_err());
1212
1213    // Make channels A and B public
1214    client_a
1215        .channel_store()
1216        .update(cx_a, |channel_store, cx| {
1217            channel_store.set_channel_visibility(channel_a, proto::ChannelVisibility::Public, cx)
1218        })
1219        .await
1220        .unwrap();
1221    client_a
1222        .channel_store()
1223        .update(cx_a, |channel_store, cx| {
1224            channel_store.set_channel_visibility(channel_b, proto::ChannelVisibility::Public, cx)
1225        })
1226        .await
1227        .unwrap();
1228
1229    // Client B joins channel A as a guest
1230    active_call_b
1231        .update(cx_b, |call, cx| call.join_channel(channel_a, cx))
1232        .await
1233        .unwrap();
1234
1235    executor.run_until_parked();
1236    assert_channels_list_shape(
1237        client_a.channel_store(),
1238        cx_a,
1239        &[(channel_a, 0), (channel_b, 1)],
1240    );
1241    assert_channels_list_shape(
1242        client_b.channel_store(),
1243        cx_b,
1244        &[(channel_a, 0), (channel_b, 1)],
1245    );
1246
1247    client_a.channel_store().update(cx_a, |channel_store, _| {
1248        let participants = channel_store.channel_participants(channel_a);
1249        assert_eq!(participants.len(), 1);
1250        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1251    });
1252}
1253
1254#[gpui::test]
1255async fn test_invite_access(
1256    executor: BackgroundExecutor,
1257    cx_a: &mut TestAppContext,
1258    cx_b: &mut TestAppContext,
1259) {
1260    let mut server = TestServer::start(executor.clone()).await;
1261    let client_a = server.create_client(cx_a, "user_a").await;
1262    let client_b = server.create_client(cx_b, "user_b").await;
1263
1264    let channels = server
1265        .make_channel_tree(
1266            &[("channel-a", None), ("channel-b", Some("channel-a"))],
1267            (&client_a, cx_a),
1268        )
1269        .await;
1270    let channel_a_id = channels[0];
1271    let channel_b_id = channels[0];
1272
1273    let active_call_b = cx_b.read(ActiveCall::global);
1274
1275    // should not be allowed to join
1276    assert!(active_call_b
1277        .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1278        .await
1279        .is_err());
1280
1281    client_a
1282        .channel_store()
1283        .update(cx_a, |channel_store, cx| {
1284            channel_store.invite_member(
1285                channel_a_id,
1286                client_b.user_id().unwrap(),
1287                ChannelRole::Member,
1288                cx,
1289            )
1290        })
1291        .await
1292        .unwrap();
1293
1294    active_call_b
1295        .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1296        .await
1297        .unwrap();
1298
1299    executor.run_until_parked();
1300
1301    client_b.channel_store().update(cx_b, |channel_store, _| {
1302        assert!(channel_store.channel_for_id(channel_b_id).is_some());
1303        assert!(channel_store.channel_for_id(channel_a_id).is_some());
1304    });
1305
1306    client_a.channel_store().update(cx_a, |channel_store, _| {
1307        let participants = channel_store.channel_participants(channel_b_id);
1308        assert_eq!(participants.len(), 1);
1309        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1310    })
1311}
1312
1313#[gpui::test]
1314async fn test_leave_channel(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1315    let (_server, _client_a, client_b, channel_id) = TestServer::start2(cx_a, cx_b).await;
1316
1317    client_b
1318        .channel_store()
1319        .update(cx_b, |channel_store, cx| {
1320            channel_store.remove_member(channel_id, client_b.user_id().unwrap(), cx)
1321        })
1322        .await
1323        .unwrap();
1324
1325    cx_a.run_until_parked();
1326
1327    assert_eq!(
1328        client_b
1329            .channel_store()
1330            .read_with(cx_b, |store, _| store.channels().count()),
1331        0
1332    );
1333}
1334
1335#[gpui::test]
1336async fn test_channel_moving(
1337    executor: BackgroundExecutor,
1338    cx_a: &mut TestAppContext,
1339    _cx_b: &mut TestAppContext,
1340    _cx_c: &mut TestAppContext,
1341) {
1342    let mut server = TestServer::start(executor.clone()).await;
1343    let client_a = server.create_client(cx_a, "user_a").await;
1344
1345    let channels = server
1346        .make_channel_tree(
1347            &[
1348                ("channel-a", None),
1349                ("channel-b", Some("channel-a")),
1350                ("channel-c", Some("channel-b")),
1351                ("channel-d", Some("channel-c")),
1352            ],
1353            (&client_a, cx_a),
1354        )
1355        .await;
1356    let channel_a_id = channels[0];
1357    let channel_b_id = channels[1];
1358    let channel_c_id = channels[2];
1359    let channel_d_id = channels[3];
1360
1361    // Current shape:
1362    // a - b - c - d
1363    assert_channels_list_shape(
1364        client_a.channel_store(),
1365        cx_a,
1366        &[
1367            (channel_a_id, 0),
1368            (channel_b_id, 1),
1369            (channel_c_id, 2),
1370            (channel_d_id, 3),
1371        ],
1372    );
1373
1374    client_a
1375        .channel_store()
1376        .update(cx_a, |channel_store, cx| {
1377            channel_store.move_channel(channel_d_id, channel_b_id, cx)
1378        })
1379        .await
1380        .unwrap();
1381
1382    // Current shape:
1383    //       /- d
1384    // a - b -- c
1385    assert_channels_list_shape(
1386        client_a.channel_store(),
1387        cx_a,
1388        &[
1389            (channel_a_id, 0),
1390            (channel_b_id, 1),
1391            (channel_c_id, 2),
1392            (channel_d_id, 2),
1393        ],
1394    );
1395}
1396
1397#[derive(Debug, PartialEq)]
1398struct ExpectedChannel {
1399    depth: usize,
1400    id: ChannelId,
1401    name: SharedString,
1402}
1403
1404#[track_caller]
1405fn assert_channel_invitations(
1406    channel_store: &Model<ChannelStore>,
1407    cx: &TestAppContext,
1408    expected_channels: &[ExpectedChannel],
1409) {
1410    let actual = cx.read(|cx| {
1411        channel_store.read_with(cx, |store, _| {
1412            store
1413                .channel_invitations()
1414                .iter()
1415                .map(|channel| ExpectedChannel {
1416                    depth: 0,
1417                    name: channel.name.clone(),
1418                    id: channel.id,
1419                })
1420                .collect::<Vec<_>>()
1421        })
1422    });
1423    assert_eq!(actual, expected_channels);
1424}
1425
1426#[track_caller]
1427fn assert_channels(
1428    channel_store: &Model<ChannelStore>,
1429    cx: &TestAppContext,
1430    expected_channels: &[ExpectedChannel],
1431) {
1432    let actual = cx.read(|cx| {
1433        channel_store.read_with(cx, |store, _| {
1434            store
1435                .ordered_channels()
1436                .map(|(depth, channel)| ExpectedChannel {
1437                    depth,
1438                    name: channel.name.clone().into(),
1439                    id: channel.id,
1440                })
1441                .collect::<Vec<_>>()
1442        })
1443    });
1444    pretty_assertions::assert_eq!(actual, expected_channels);
1445}
1446
1447#[track_caller]
1448fn assert_channels_list_shape(
1449    channel_store: &Model<ChannelStore>,
1450    cx: &TestAppContext,
1451    expected_channels: &[(u64, usize)],
1452) {
1453    let actual = cx.read(|cx| {
1454        channel_store.read_with(cx, |store, _| {
1455            store
1456                .ordered_channels()
1457                .map(|(depth, channel)| (channel.id, depth))
1458                .collect::<Vec<_>>()
1459        })
1460    });
1461    pretty_assertions::assert_eq!(actual, expected_channels);
1462}