channel_tests.rs

   1use crate::{
   2    rpc::RECONNECT_TIMEOUT,
   3    tests::{room_participants, RoomParticipants, TestServer},
   4};
   5use call::ActiveCall;
   6use channel::{ChannelId, ChannelMembership, ChannelStore};
   7use client::User;
   8use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
   9use rpc::{
  10    proto::{self},
  11    RECEIVE_TIMEOUT,
  12};
  13use std::sync::Arc;
  14
  15#[gpui::test]
  16async fn test_core_channels(
  17    deterministic: Arc<Deterministic>,
  18    cx_a: &mut TestAppContext,
  19    cx_b: &mut TestAppContext,
  20) {
  21    deterministic.forbid_parking();
  22    let mut server = TestServer::start(&deterministic).await;
  23    let client_a = server.create_client(cx_a, "user_a").await;
  24    let client_b = server.create_client(cx_b, "user_b").await;
  25
  26    let channel_a_id = client_a
  27        .channel_store()
  28        .update(cx_a, |channel_store, cx| {
  29            channel_store.create_channel("channel-a", None, cx)
  30        })
  31        .await
  32        .unwrap();
  33    let channel_b_id = client_a
  34        .channel_store()
  35        .update(cx_a, |channel_store, cx| {
  36            channel_store.create_channel("channel-b", Some(channel_a_id), cx)
  37        })
  38        .await
  39        .unwrap();
  40
  41    deterministic.run_until_parked();
  42    assert_channels(
  43        client_a.channel_store(),
  44        cx_a,
  45        &[
  46            ExpectedChannel {
  47                id: channel_a_id,
  48                name: "channel-a".to_string(),
  49                depth: 0,
  50                user_is_admin: true,
  51            },
  52            ExpectedChannel {
  53                id: channel_b_id,
  54                name: "channel-b".to_string(),
  55                depth: 1,
  56                user_is_admin: true,
  57            },
  58        ],
  59    );
  60
  61    client_b.channel_store().read_with(cx_b, |channels, _| {
  62        assert!(channels
  63            .channel_dag_entries()
  64            .collect::<Vec<_>>()
  65            .is_empty())
  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    deterministic.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            user_is_admin: false,
  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, _| {
 129            channels.respond_to_channel_invite(channel_a_id, true)
 130        })
 131        .await
 132        .unwrap();
 133    deterministic.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                user_is_admin: false,
 145                depth: 0,
 146            },
 147            ExpectedChannel {
 148                id: channel_b_id,
 149                name: "channel-b".to_string(),
 150                user_is_admin: false,
 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    deterministic.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                user_is_admin: false,
 173                depth: 0,
 174            },
 175            ExpectedChannel {
 176                id: channel_b_id,
 177                name: "channel-b".to_string(),
 178                user_is_admin: false,
 179                depth: 1,
 180            },
 181            ExpectedChannel {
 182                id: channel_c_id,
 183                name: "channel-c".to_string(),
 184                user_is_admin: false,
 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    deterministic.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                user_is_admin: true,
 217            },
 218            ExpectedChannel {
 219                id: channel_b_id,
 220                name: "channel-b".to_string(),
 221                depth: 1,
 222                user_is_admin: true,
 223            },
 224            ExpectedChannel {
 225                id: channel_c_id,
 226                name: "channel-c".to_string(),
 227                depth: 2,
 228                user_is_admin: true,
 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    deterministic.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            user_is_admin: true,
 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            user_is_admin: true,
 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    deterministic.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            user_is_admin: true,
 284        }],
 285    );
 286
 287    // Client B no longer has access to the channel
 288    assert_channels(client_b.channel_store(), cx_b, &[]);
 289
 290    // When disconnected, client A sees no channels.
 291    server.forbid_connections();
 292    server.disconnect_client(client_a.peer_id().unwrap());
 293    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 294    assert_channels(client_a.channel_store(), cx_a, &[]);
 295
 296    server.allow_connections();
 297    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 298    assert_channels(
 299        client_a.channel_store(),
 300        cx_a,
 301        &[ExpectedChannel {
 302            id: channel_a_id,
 303            name: "channel-a".to_string(),
 304            depth: 0,
 305            user_is_admin: true,
 306        }],
 307    );
 308}
 309
 310#[track_caller]
 311fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
 312    assert_eq!(
 313        participants.iter().map(|p| p.id).collect::<Vec<_>>(),
 314        expected_partitipants
 315    );
 316}
 317
 318#[track_caller]
 319fn assert_members_eq(
 320    members: &[ChannelMembership],
 321    expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],
 322) {
 323    assert_eq!(
 324        members
 325            .iter()
 326            .map(|member| (member.user.id, member.role, member.kind))
 327            .collect::<Vec<_>>(),
 328        expected_members
 329    );
 330}
 331
 332#[gpui::test]
 333async fn test_joining_channel_ancestor_member(
 334    deterministic: Arc<Deterministic>,
 335    cx_a: &mut TestAppContext,
 336    cx_b: &mut TestAppContext,
 337) {
 338    deterministic.forbid_parking();
 339    let mut server = TestServer::start(&deterministic).await;
 340
 341    let client_a = server.create_client(cx_a, "user_a").await;
 342    let client_b = server.create_client(cx_b, "user_b").await;
 343
 344    let parent_id = server
 345        .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 346        .await;
 347
 348    let sub_id = client_a
 349        .channel_store()
 350        .update(cx_a, |channel_store, cx| {
 351            channel_store.create_channel("sub_channel", Some(parent_id), cx)
 352        })
 353        .await
 354        .unwrap();
 355
 356    let active_call_b = cx_b.read(ActiveCall::global);
 357
 358    assert!(active_call_b
 359        .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
 360        .await
 361        .is_ok());
 362}
 363
 364#[gpui::test]
 365async fn test_channel_room(
 366    deterministic: Arc<Deterministic>,
 367    cx_a: &mut TestAppContext,
 368    cx_b: &mut TestAppContext,
 369    cx_c: &mut TestAppContext,
 370) {
 371    deterministic.forbid_parking();
 372    let mut server = TestServer::start(&deterministic).await;
 373    let client_a = server.create_client(cx_a, "user_a").await;
 374    let client_b = server.create_client(cx_b, "user_b").await;
 375    let client_c = server.create_client(cx_c, "user_c").await;
 376
 377    let zed_id = server
 378        .make_channel(
 379            "zed",
 380            None,
 381            (&client_a, cx_a),
 382            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 383        )
 384        .await;
 385
 386    let active_call_a = cx_a.read(ActiveCall::global);
 387    let active_call_b = cx_b.read(ActiveCall::global);
 388
 389    active_call_a
 390        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 391        .await
 392        .unwrap();
 393
 394    // Give everyone a chance to observe user A joining
 395    deterministic.run_until_parked();
 396    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 397    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 398
 399    client_a.channel_store().read_with(cx_a, |channels, _| {
 400        assert_participants_eq(
 401            channels.channel_participants(zed_id),
 402            &[client_a.user_id().unwrap()],
 403        );
 404    });
 405
 406    assert_channels(
 407        client_b.channel_store(),
 408        cx_b,
 409        &[ExpectedChannel {
 410            id: zed_id,
 411            name: "zed".to_string(),
 412            depth: 0,
 413            user_is_admin: false,
 414        }],
 415    );
 416    client_b.channel_store().read_with(cx_b, |channels, _| {
 417        assert_participants_eq(
 418            channels.channel_participants(zed_id),
 419            &[client_a.user_id().unwrap()],
 420        );
 421    });
 422
 423    client_c.channel_store().read_with(cx_c, |channels, _| {
 424        assert_participants_eq(
 425            channels.channel_participants(zed_id),
 426            &[client_a.user_id().unwrap()],
 427        );
 428    });
 429
 430    active_call_b
 431        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 432        .await
 433        .unwrap();
 434
 435    deterministic.run_until_parked();
 436
 437    client_a.channel_store().read_with(cx_a, |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    client_b.channel_store().read_with(cx_b, |channels, _| {
 445        assert_participants_eq(
 446            channels.channel_participants(zed_id),
 447            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 448        );
 449    });
 450
 451    client_c.channel_store().read_with(cx_c, |channels, _| {
 452        assert_participants_eq(
 453            channels.channel_participants(zed_id),
 454            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 455        );
 456    });
 457
 458    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 459    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 460    assert_eq!(
 461        room_participants(&room_a, cx_a),
 462        RoomParticipants {
 463            remote: vec!["user_b".to_string()],
 464            pending: vec![]
 465        }
 466    );
 467
 468    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 469    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 470    assert_eq!(
 471        room_participants(&room_b, cx_b),
 472        RoomParticipants {
 473            remote: vec!["user_a".to_string()],
 474            pending: vec![]
 475        }
 476    );
 477
 478    // Make sure that leaving and rejoining works
 479
 480    active_call_a
 481        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 482        .await
 483        .unwrap();
 484
 485    deterministic.run_until_parked();
 486
 487    client_a.channel_store().read_with(cx_a, |channels, _| {
 488        assert_participants_eq(
 489            channels.channel_participants(zed_id),
 490            &[client_b.user_id().unwrap()],
 491        );
 492    });
 493
 494    client_b.channel_store().read_with(cx_b, |channels, _| {
 495        assert_participants_eq(
 496            channels.channel_participants(zed_id),
 497            &[client_b.user_id().unwrap()],
 498        );
 499    });
 500
 501    client_c.channel_store().read_with(cx_c, |channels, _| {
 502        assert_participants_eq(
 503            channels.channel_participants(zed_id),
 504            &[client_b.user_id().unwrap()],
 505        );
 506    });
 507
 508    active_call_b
 509        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 510        .await
 511        .unwrap();
 512
 513    deterministic.run_until_parked();
 514
 515    client_a.channel_store().read_with(cx_a, |channels, _| {
 516        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 517    });
 518
 519    client_b.channel_store().read_with(cx_b, |channels, _| {
 520        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 521    });
 522
 523    client_c.channel_store().read_with(cx_c, |channels, _| {
 524        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 525    });
 526
 527    active_call_a
 528        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 529        .await
 530        .unwrap();
 531
 532    active_call_b
 533        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 534        .await
 535        .unwrap();
 536
 537    deterministic.run_until_parked();
 538
 539    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 540    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 541    assert_eq!(
 542        room_participants(&room_a, cx_a),
 543        RoomParticipants {
 544            remote: vec!["user_b".to_string()],
 545            pending: vec![]
 546        }
 547    );
 548
 549    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 550    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 551    assert_eq!(
 552        room_participants(&room_b, cx_b),
 553        RoomParticipants {
 554            remote: vec!["user_a".to_string()],
 555            pending: vec![]
 556        }
 557    );
 558}
 559
 560#[gpui::test]
 561async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
 562    deterministic.forbid_parking();
 563    let mut server = TestServer::start(&deterministic).await;
 564    let client_a = server.create_client(cx_a, "user_a").await;
 565
 566    let zed_id = server
 567        .make_channel("zed", None, (&client_a, cx_a), &mut [])
 568        .await;
 569    let rust_id = server
 570        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 571        .await;
 572
 573    let active_call_a = cx_a.read(ActiveCall::global);
 574
 575    active_call_a
 576        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 577        .await
 578        .unwrap();
 579
 580    // Give everything a chance to observe user A joining
 581    deterministic.run_until_parked();
 582
 583    client_a.channel_store().read_with(cx_a, |channels, _| {
 584        assert_participants_eq(
 585            channels.channel_participants(zed_id),
 586            &[client_a.user_id().unwrap()],
 587        );
 588        assert_participants_eq(channels.channel_participants(rust_id), &[]);
 589    });
 590
 591    active_call_a
 592        .update(cx_a, |active_call, cx| {
 593            active_call.join_channel(rust_id, cx)
 594        })
 595        .await
 596        .unwrap();
 597
 598    deterministic.run_until_parked();
 599
 600    client_a.channel_store().read_with(cx_a, |channels, _| {
 601        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 602        assert_participants_eq(
 603            channels.channel_participants(rust_id),
 604            &[client_a.user_id().unwrap()],
 605        );
 606    });
 607}
 608
 609#[gpui::test]
 610async fn test_permissions_update_while_invited(
 611    deterministic: Arc<Deterministic>,
 612    cx_a: &mut TestAppContext,
 613    cx_b: &mut TestAppContext,
 614) {
 615    deterministic.forbid_parking();
 616    let mut server = TestServer::start(&deterministic).await;
 617    let client_a = server.create_client(cx_a, "user_a").await;
 618    let client_b = server.create_client(cx_b, "user_b").await;
 619
 620    let rust_id = server
 621        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 622        .await;
 623
 624    client_a
 625        .channel_store()
 626        .update(cx_a, |channel_store, cx| {
 627            channel_store.invite_member(
 628                rust_id,
 629                client_b.user_id().unwrap(),
 630                proto::ChannelRole::Member,
 631                cx,
 632            )
 633        })
 634        .await
 635        .unwrap();
 636
 637    deterministic.run_until_parked();
 638
 639    assert_channel_invitations(
 640        client_b.channel_store(),
 641        cx_b,
 642        &[ExpectedChannel {
 643            depth: 0,
 644            id: rust_id,
 645            name: "rust".to_string(),
 646            user_is_admin: false,
 647        }],
 648    );
 649    assert_channels(client_b.channel_store(), cx_b, &[]);
 650
 651    // Update B's invite before they've accepted it
 652    client_a
 653        .channel_store()
 654        .update(cx_a, |channel_store, cx| {
 655            channel_store.set_member_role(
 656                rust_id,
 657                client_b.user_id().unwrap(),
 658                proto::ChannelRole::Admin,
 659                cx,
 660            )
 661        })
 662        .await
 663        .unwrap();
 664
 665    deterministic.run_until_parked();
 666
 667    assert_channel_invitations(
 668        client_b.channel_store(),
 669        cx_b,
 670        &[ExpectedChannel {
 671            depth: 0,
 672            id: rust_id,
 673            name: "rust".to_string(),
 674            user_is_admin: false,
 675        }],
 676    );
 677    assert_channels(client_b.channel_store(), cx_b, &[]);
 678}
 679
 680#[gpui::test]
 681async fn test_channel_rename(
 682    deterministic: Arc<Deterministic>,
 683    cx_a: &mut TestAppContext,
 684    cx_b: &mut TestAppContext,
 685) {
 686    deterministic.forbid_parking();
 687    let mut server = TestServer::start(&deterministic).await;
 688    let client_a = server.create_client(cx_a, "user_a").await;
 689    let client_b = server.create_client(cx_b, "user_b").await;
 690
 691    let rust_id = server
 692        .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 693        .await;
 694
 695    // Rename the channel
 696    client_a
 697        .channel_store()
 698        .update(cx_a, |channel_store, cx| {
 699            channel_store.rename(rust_id, "#rust-archive", cx)
 700        })
 701        .await
 702        .unwrap();
 703
 704    deterministic.run_until_parked();
 705
 706    // Client A sees the channel with its new name.
 707    assert_channels(
 708        client_a.channel_store(),
 709        cx_a,
 710        &[ExpectedChannel {
 711            depth: 0,
 712            id: rust_id,
 713            name: "rust-archive".to_string(),
 714            user_is_admin: true,
 715        }],
 716    );
 717
 718    // Client B sees the channel with its new name.
 719    assert_channels(
 720        client_b.channel_store(),
 721        cx_b,
 722        &[ExpectedChannel {
 723            depth: 0,
 724            id: rust_id,
 725            name: "rust-archive".to_string(),
 726            user_is_admin: false,
 727        }],
 728    );
 729}
 730
 731#[gpui::test]
 732async fn test_call_from_channel(
 733    deterministic: Arc<Deterministic>,
 734    cx_a: &mut TestAppContext,
 735    cx_b: &mut TestAppContext,
 736    cx_c: &mut TestAppContext,
 737) {
 738    deterministic.forbid_parking();
 739    let mut server = TestServer::start(&deterministic).await;
 740    let client_a = server.create_client(cx_a, "user_a").await;
 741    let client_b = server.create_client(cx_b, "user_b").await;
 742    let client_c = server.create_client(cx_c, "user_c").await;
 743    server
 744        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 745        .await;
 746
 747    let channel_id = server
 748        .make_channel(
 749            "x",
 750            None,
 751            (&client_a, cx_a),
 752            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 753        )
 754        .await;
 755
 756    let active_call_a = cx_a.read(ActiveCall::global);
 757    let active_call_b = cx_b.read(ActiveCall::global);
 758
 759    active_call_a
 760        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 761        .await
 762        .unwrap();
 763
 764    // Client A calls client B while in the channel.
 765    active_call_a
 766        .update(cx_a, |call, cx| {
 767            call.invite(client_b.user_id().unwrap(), None, cx)
 768        })
 769        .await
 770        .unwrap();
 771
 772    // Client B accepts the call.
 773    deterministic.run_until_parked();
 774    active_call_b
 775        .update(cx_b, |call, cx| call.accept_incoming(cx))
 776        .await
 777        .unwrap();
 778
 779    // Client B sees that they are now in the channel
 780    deterministic.run_until_parked();
 781    active_call_b.read_with(cx_b, |call, cx| {
 782        assert_eq!(call.channel_id(cx), Some(channel_id));
 783    });
 784    client_b.channel_store().read_with(cx_b, |channels, _| {
 785        assert_participants_eq(
 786            channels.channel_participants(channel_id),
 787            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 788        );
 789    });
 790
 791    // Clients A and C also see that client B is in the channel.
 792    client_a.channel_store().read_with(cx_a, |channels, _| {
 793        assert_participants_eq(
 794            channels.channel_participants(channel_id),
 795            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 796        );
 797    });
 798    client_c.channel_store().read_with(cx_c, |channels, _| {
 799        assert_participants_eq(
 800            channels.channel_participants(channel_id),
 801            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 802        );
 803    });
 804}
 805
 806#[gpui::test]
 807async fn test_lost_channel_creation(
 808    deterministic: Arc<Deterministic>,
 809    cx_a: &mut TestAppContext,
 810    cx_b: &mut TestAppContext,
 811) {
 812    deterministic.forbid_parking();
 813    let mut server = TestServer::start(&deterministic).await;
 814    let client_a = server.create_client(cx_a, "user_a").await;
 815    let client_b = server.create_client(cx_b, "user_b").await;
 816
 817    server
 818        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 819        .await;
 820
 821    let channel_id = server
 822        .make_channel("x", None, (&client_a, cx_a), &mut [])
 823        .await;
 824
 825    // Invite a member
 826    client_a
 827        .channel_store()
 828        .update(cx_a, |channel_store, cx| {
 829            channel_store.invite_member(
 830                channel_id,
 831                client_b.user_id().unwrap(),
 832                proto::ChannelRole::Member,
 833                cx,
 834            )
 835        })
 836        .await
 837        .unwrap();
 838
 839    deterministic.run_until_parked();
 840
 841    // Sanity check, B has the invitation
 842    assert_channel_invitations(
 843        client_b.channel_store(),
 844        cx_b,
 845        &[ExpectedChannel {
 846            depth: 0,
 847            id: channel_id,
 848            name: "x".to_string(),
 849            user_is_admin: false,
 850        }],
 851    );
 852
 853    // A creates a subchannel while the invite is still pending.
 854    let subchannel_id = client_a
 855        .channel_store()
 856        .update(cx_a, |channel_store, cx| {
 857            channel_store.create_channel("subchannel", Some(channel_id), cx)
 858        })
 859        .await
 860        .unwrap();
 861
 862    deterministic.run_until_parked();
 863
 864    // Make sure A sees their new channel
 865    assert_channels(
 866        client_a.channel_store(),
 867        cx_a,
 868        &[
 869            ExpectedChannel {
 870                depth: 0,
 871                id: channel_id,
 872                name: "x".to_string(),
 873                user_is_admin: true,
 874            },
 875            ExpectedChannel {
 876                depth: 1,
 877                id: subchannel_id,
 878                name: "subchannel".to_string(),
 879                user_is_admin: true,
 880            },
 881        ],
 882    );
 883
 884    // Client B accepts the invite
 885    client_b
 886        .channel_store()
 887        .update(cx_b, |channel_store, _| {
 888            channel_store.respond_to_channel_invite(channel_id, true)
 889        })
 890        .await
 891        .unwrap();
 892
 893    deterministic.run_until_parked();
 894
 895    // Client B should now see the channel
 896    assert_channels(
 897        client_b.channel_store(),
 898        cx_b,
 899        &[
 900            ExpectedChannel {
 901                depth: 0,
 902                id: channel_id,
 903                name: "x".to_string(),
 904                user_is_admin: false,
 905            },
 906            ExpectedChannel {
 907                depth: 1,
 908                id: subchannel_id,
 909                name: "subchannel".to_string(),
 910                user_is_admin: false,
 911            },
 912        ],
 913    );
 914}
 915
 916#[gpui::test]
 917async fn test_channel_moving(
 918    deterministic: Arc<Deterministic>,
 919    cx_a: &mut TestAppContext,
 920    cx_b: &mut TestAppContext,
 921    cx_c: &mut TestAppContext,
 922) {
 923    deterministic.forbid_parking();
 924    let mut server = TestServer::start(&deterministic).await;
 925    let client_a = server.create_client(cx_a, "user_a").await;
 926    let client_b = server.create_client(cx_b, "user_b").await;
 927    let client_c = server.create_client(cx_c, "user_c").await;
 928
 929    let channels = server
 930        .make_channel_tree(
 931            &[
 932                ("channel-a", None),
 933                ("channel-b", Some("channel-a")),
 934                ("channel-c", Some("channel-b")),
 935                ("channel-d", Some("channel-c")),
 936            ],
 937            (&client_a, cx_a),
 938        )
 939        .await;
 940    let channel_a_id = channels[0];
 941    let channel_b_id = channels[1];
 942    let channel_c_id = channels[2];
 943    let channel_d_id = channels[3];
 944
 945    // Current shape:
 946    // a - b - c - d
 947    assert_channels_list_shape(
 948        client_a.channel_store(),
 949        cx_a,
 950        &[
 951            (channel_a_id, 0),
 952            (channel_b_id, 1),
 953            (channel_c_id, 2),
 954            (channel_d_id, 3),
 955        ],
 956    );
 957
 958    client_a
 959        .channel_store()
 960        .update(cx_a, |channel_store, cx| {
 961            channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
 962        })
 963        .await
 964        .unwrap();
 965
 966    // Current shape:
 967    //       /- d
 968    // a - b -- c
 969    assert_channels_list_shape(
 970        client_a.channel_store(),
 971        cx_a,
 972        &[
 973            (channel_a_id, 0),
 974            (channel_b_id, 1),
 975            (channel_c_id, 2),
 976            (channel_d_id, 2),
 977        ],
 978    );
 979
 980    client_a
 981        .channel_store()
 982        .update(cx_a, |channel_store, cx| {
 983            channel_store.link_channel(channel_d_id, channel_c_id, cx)
 984        })
 985        .await
 986        .unwrap();
 987
 988    // Current shape for A:
 989    //      /------\
 990    // a - b -- c -- d
 991    assert_channels_list_shape(
 992        client_a.channel_store(),
 993        cx_a,
 994        &[
 995            (channel_a_id, 0),
 996            (channel_b_id, 1),
 997            (channel_c_id, 2),
 998            (channel_d_id, 3),
 999            (channel_d_id, 2),
1000        ],
1001    );
1002
1003    let b_channels = server
1004        .make_channel_tree(
1005            &[
1006                ("channel-mu", None),
1007                ("channel-gamma", Some("channel-mu")),
1008                ("channel-epsilon", Some("channel-mu")),
1009            ],
1010            (&client_b, cx_b),
1011        )
1012        .await;
1013    let channel_mu_id = b_channels[0];
1014    let channel_ga_id = b_channels[1];
1015    let channel_ep_id = b_channels[2];
1016
1017    // Current shape for B:
1018    //    /- ep
1019    // mu -- ga
1020    assert_channels_list_shape(
1021        client_b.channel_store(),
1022        cx_b,
1023        &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
1024    );
1025
1026    client_a
1027        .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
1028        .await;
1029
1030    // Current shape for B:
1031    //    /- ep
1032    // mu -- ga
1033    //  /---------\
1034    // b  -- c  -- d
1035    assert_channels_list_shape(
1036        client_b.channel_store(),
1037        cx_b,
1038        &[
1039            // New channels from a
1040            (channel_b_id, 0),
1041            (channel_c_id, 1),
1042            (channel_d_id, 2),
1043            (channel_d_id, 1),
1044            // B's old channels
1045            (channel_mu_id, 0),
1046            (channel_ep_id, 1),
1047            (channel_ga_id, 1),
1048        ],
1049    );
1050
1051    client_b
1052        .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
1053        .await;
1054
1055    // Current shape for C:
1056    // - ep
1057    assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
1058
1059    client_b
1060        .channel_store()
1061        .update(cx_b, |channel_store, cx| {
1062            channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1063        })
1064        .await
1065        .unwrap();
1066
1067    // Current shape for B:
1068    //              /---------\
1069    //    /- ep -- b  -- c  -- d
1070    // mu -- ga
1071    assert_channels_list_shape(
1072        client_b.channel_store(),
1073        cx_b,
1074        &[
1075            (channel_mu_id, 0),
1076            (channel_ep_id, 1),
1077            (channel_b_id, 2),
1078            (channel_c_id, 3),
1079            (channel_d_id, 4),
1080            (channel_d_id, 3),
1081            (channel_ga_id, 1),
1082        ],
1083    );
1084
1085    // Current shape for C:
1086    //        /---------\
1087    // ep -- b  -- c  -- d
1088    assert_channels_list_shape(
1089        client_c.channel_store(),
1090        cx_c,
1091        &[
1092            (channel_ep_id, 0),
1093            (channel_b_id, 1),
1094            (channel_c_id, 2),
1095            (channel_d_id, 3),
1096            (channel_d_id, 2),
1097        ],
1098    );
1099
1100    client_b
1101        .channel_store()
1102        .update(cx_b, |channel_store, cx| {
1103            channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1104        })
1105        .await
1106        .unwrap();
1107
1108    // Current shape for B:
1109    //              /---------\
1110    //    /- ep -- b  -- c  -- d
1111    //   /          \
1112    // mu ---------- ga
1113    assert_channels_list_shape(
1114        client_b.channel_store(),
1115        cx_b,
1116        &[
1117            (channel_mu_id, 0),
1118            (channel_ep_id, 1),
1119            (channel_b_id, 2),
1120            (channel_c_id, 3),
1121            (channel_d_id, 4),
1122            (channel_d_id, 3),
1123            (channel_ga_id, 3),
1124            (channel_ga_id, 1),
1125        ],
1126    );
1127
1128    // Current shape for A:
1129    //      /------\
1130    // a - b -- c -- d
1131    //      \-- ga
1132    assert_channels_list_shape(
1133        client_a.channel_store(),
1134        cx_a,
1135        &[
1136            (channel_a_id, 0),
1137            (channel_b_id, 1),
1138            (channel_c_id, 2),
1139            (channel_d_id, 3),
1140            (channel_d_id, 2),
1141            (channel_ga_id, 2),
1142        ],
1143    );
1144
1145    // Current shape for C:
1146    //        /-------\
1147    // ep -- b -- c -- d
1148    //        \-- ga
1149    assert_channels_list_shape(
1150        client_c.channel_store(),
1151        cx_c,
1152        &[
1153            (channel_ep_id, 0),
1154            (channel_b_id, 1),
1155            (channel_c_id, 2),
1156            (channel_d_id, 3),
1157            (channel_d_id, 2),
1158            (channel_ga_id, 2),
1159        ],
1160    );
1161}
1162
1163#[derive(Debug, PartialEq)]
1164struct ExpectedChannel {
1165    depth: usize,
1166    id: ChannelId,
1167    name: String,
1168    user_is_admin: bool,
1169}
1170
1171#[track_caller]
1172fn assert_channel_invitations(
1173    channel_store: &ModelHandle<ChannelStore>,
1174    cx: &TestAppContext,
1175    expected_channels: &[ExpectedChannel],
1176) {
1177    let actual = channel_store.read_with(cx, |store, _| {
1178        store
1179            .channel_invitations()
1180            .iter()
1181            .map(|channel| ExpectedChannel {
1182                depth: 0,
1183                name: channel.name.clone(),
1184                id: channel.id,
1185                user_is_admin: store.is_user_admin(channel.id),
1186            })
1187            .collect::<Vec<_>>()
1188    });
1189    assert_eq!(actual, expected_channels);
1190}
1191
1192#[track_caller]
1193fn assert_channels(
1194    channel_store: &ModelHandle<ChannelStore>,
1195    cx: &TestAppContext,
1196    expected_channels: &[ExpectedChannel],
1197) {
1198    let actual = channel_store.read_with(cx, |store, _| {
1199        store
1200            .channel_dag_entries()
1201            .map(|(depth, channel)| ExpectedChannel {
1202                depth,
1203                name: channel.name.clone(),
1204                id: channel.id,
1205                user_is_admin: store.is_user_admin(channel.id),
1206            })
1207            .collect::<Vec<_>>()
1208    });
1209    pretty_assertions::assert_eq!(actual, expected_channels);
1210}
1211
1212#[track_caller]
1213fn assert_channels_list_shape(
1214    channel_store: &ModelHandle<ChannelStore>,
1215    cx: &TestAppContext,
1216    expected_channels: &[(u64, usize)],
1217) {
1218    cx.foreground().run_until_parked();
1219
1220    let actual = channel_store.read_with(cx, |store, _| {
1221        store
1222            .channel_dag_entries()
1223            .map(|(depth, channel)| (channel.id, depth))
1224            .collect::<Vec<_>>()
1225    });
1226    pretty_assertions::assert_eq!(actual, expected_channels);
1227}