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::{proto, RECEIVE_TIMEOUT};
  10use std::sync::Arc;
  11
  12#[gpui::test]
  13async fn test_core_channels(
  14    deterministic: Arc<Deterministic>,
  15    cx_a: &mut TestAppContext,
  16    cx_b: &mut TestAppContext,
  17) {
  18    deterministic.forbid_parking();
  19    let mut server = TestServer::start(&deterministic).await;
  20    let client_a = server.create_client(cx_a, "user_a").await;
  21    let client_b = server.create_client(cx_b, "user_b").await;
  22
  23    let channel_a_id = client_a
  24        .channel_store()
  25        .update(cx_a, |channel_store, cx| {
  26            channel_store.create_channel("channel-a", None, cx)
  27        })
  28        .await
  29        .unwrap();
  30    let channel_b_id = client_a
  31        .channel_store()
  32        .update(cx_a, |channel_store, cx| {
  33            channel_store.create_channel("channel-b", Some(channel_a_id), cx)
  34        })
  35        .await
  36        .unwrap();
  37
  38    deterministic.run_until_parked();
  39    assert_channels(
  40        client_a.channel_store(),
  41        cx_a,
  42        &[
  43            ExpectedChannel {
  44                id: channel_a_id,
  45                name: "channel-a".to_string(),
  46                depth: 0,
  47                user_is_admin: true,
  48            },
  49            ExpectedChannel {
  50                id: channel_b_id,
  51                name: "channel-b".to_string(),
  52                depth: 1,
  53                user_is_admin: true,
  54            },
  55        ],
  56    );
  57
  58    client_b.channel_store().read_with(cx_b, |channels, _| {
  59        assert!(channels.channels().collect::<Vec<_>>().is_empty())
  60    });
  61
  62    // Invite client B to channel A as client A.
  63    client_a
  64        .channel_store()
  65        .update(cx_a, |store, cx| {
  66            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  67
  68            let invite = store.invite_member(channel_a_id, client_b.user_id().unwrap(), false, cx);
  69
  70            // Make sure we're synchronously storing the pending invite
  71            assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  72            invite
  73        })
  74        .await
  75        .unwrap();
  76
  77    // Client A sees that B has been invited.
  78    deterministic.run_until_parked();
  79    assert_channel_invitations(
  80        client_b.channel_store(),
  81        cx_b,
  82        &[ExpectedChannel {
  83            id: channel_a_id,
  84            name: "channel-a".to_string(),
  85            depth: 0,
  86            user_is_admin: false,
  87        }],
  88    );
  89
  90    let members = client_a
  91        .channel_store()
  92        .update(cx_a, |store, cx| {
  93            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  94            store.get_channel_member_details(channel_a_id, cx)
  95        })
  96        .await
  97        .unwrap();
  98    assert_members_eq(
  99        &members,
 100        &[
 101            (
 102                client_a.user_id().unwrap(),
 103                true,
 104                proto::channel_member::Kind::Member,
 105            ),
 106            (
 107                client_b.user_id().unwrap(),
 108                false,
 109                proto::channel_member::Kind::Invitee,
 110            ),
 111        ],
 112    );
 113
 114    // Client B accepts the invitation.
 115    client_b
 116        .channel_store()
 117        .update(cx_b, |channels, _| {
 118            channels.respond_to_channel_invite(channel_a_id, true)
 119        })
 120        .await
 121        .unwrap();
 122    deterministic.run_until_parked();
 123
 124    // Client B now sees that they are a member of channel A and its existing subchannels.
 125    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 126    assert_channels(
 127        client_b.channel_store(),
 128        cx_b,
 129        &[
 130            ExpectedChannel {
 131                id: channel_a_id,
 132                name: "channel-a".to_string(),
 133                user_is_admin: false,
 134                depth: 0,
 135            },
 136            ExpectedChannel {
 137                id: channel_b_id,
 138                name: "channel-b".to_string(),
 139                user_is_admin: false,
 140                depth: 1,
 141            },
 142        ],
 143    );
 144
 145    let channel_c_id = client_a
 146        .channel_store()
 147        .update(cx_a, |channel_store, cx| {
 148            channel_store.create_channel("channel-c", Some(channel_b_id), cx)
 149        })
 150        .await
 151        .unwrap();
 152
 153    deterministic.run_until_parked();
 154    assert_channels(
 155        client_b.channel_store(),
 156        cx_b,
 157        &[
 158            ExpectedChannel {
 159                id: channel_a_id,
 160                name: "channel-a".to_string(),
 161                user_is_admin: false,
 162                depth: 0,
 163            },
 164            ExpectedChannel {
 165                id: channel_b_id,
 166                name: "channel-b".to_string(),
 167                user_is_admin: false,
 168                depth: 1,
 169            },
 170            ExpectedChannel {
 171                id: channel_c_id,
 172                name: "channel-c".to_string(),
 173                user_is_admin: false,
 174                depth: 2,
 175            },
 176        ],
 177    );
 178
 179    // Update client B's membership to channel A to be an admin.
 180    client_a
 181        .channel_store()
 182        .update(cx_a, |store, cx| {
 183            store.set_member_admin(channel_a_id, client_b.user_id().unwrap(), true, cx)
 184        })
 185        .await
 186        .unwrap();
 187    deterministic.run_until_parked();
 188
 189    // Observe that client B is now an admin of channel A, and that
 190    // their admin priveleges extend to subchannels of channel A.
 191    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 192    assert_channels(
 193        client_b.channel_store(),
 194        cx_b,
 195        &[
 196            ExpectedChannel {
 197                id: channel_a_id,
 198                name: "channel-a".to_string(),
 199                depth: 0,
 200                user_is_admin: true,
 201            },
 202            ExpectedChannel {
 203                id: channel_b_id,
 204                name: "channel-b".to_string(),
 205                depth: 1,
 206                user_is_admin: true,
 207            },
 208            ExpectedChannel {
 209                id: channel_c_id,
 210                name: "channel-c".to_string(),
 211                depth: 2,
 212                user_is_admin: true,
 213            },
 214        ],
 215    );
 216
 217    // Client A deletes the channel, deletion also deletes subchannels.
 218    client_a
 219        .channel_store()
 220        .update(cx_a, |channel_store, _| {
 221            channel_store.remove_channel(channel_b_id)
 222        })
 223        .await
 224        .unwrap();
 225
 226    deterministic.run_until_parked();
 227    assert_channels(
 228        client_a.channel_store(),
 229        cx_a,
 230        &[ExpectedChannel {
 231            id: channel_a_id,
 232            name: "channel-a".to_string(),
 233            depth: 0,
 234            user_is_admin: true,
 235        }],
 236    );
 237    assert_channels(
 238        client_b.channel_store(),
 239        cx_b,
 240        &[ExpectedChannel {
 241            id: channel_a_id,
 242            name: "channel-a".to_string(),
 243            depth: 0,
 244            user_is_admin: true,
 245        }],
 246    );
 247
 248    // Remove client B
 249    client_a
 250        .channel_store()
 251        .update(cx_a, |channel_store, cx| {
 252            channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
 253        })
 254        .await
 255        .unwrap();
 256
 257    deterministic.run_until_parked();
 258
 259    // Client A still has their channel
 260    assert_channels(
 261        client_a.channel_store(),
 262        cx_a,
 263        &[ExpectedChannel {
 264            id: channel_a_id,
 265            name: "channel-a".to_string(),
 266            depth: 0,
 267            user_is_admin: true,
 268        }],
 269    );
 270
 271    // Client B no longer has access to the channel
 272    assert_channels(client_b.channel_store(), cx_b, &[]);
 273
 274    // When disconnected, client A sees no channels.
 275    server.forbid_connections();
 276    server.disconnect_client(client_a.peer_id().unwrap());
 277    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 278    assert_channels(client_a.channel_store(), cx_a, &[]);
 279
 280    server.allow_connections();
 281    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 282    assert_channels(
 283        client_a.channel_store(),
 284        cx_a,
 285        &[ExpectedChannel {
 286            id: channel_a_id,
 287            name: "channel-a".to_string(),
 288            depth: 0,
 289            user_is_admin: true,
 290        }],
 291    );
 292}
 293
 294#[track_caller]
 295fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
 296    assert_eq!(
 297        participants.iter().map(|p| p.id).collect::<Vec<_>>(),
 298        expected_partitipants
 299    );
 300}
 301
 302#[track_caller]
 303fn assert_members_eq(
 304    members: &[ChannelMembership],
 305    expected_members: &[(u64, bool, proto::channel_member::Kind)],
 306) {
 307    assert_eq!(
 308        members
 309            .iter()
 310            .map(|member| (member.user.id, member.admin, member.kind))
 311            .collect::<Vec<_>>(),
 312        expected_members
 313    );
 314}
 315
 316#[gpui::test]
 317async fn test_joining_channel_ancestor_member(
 318    deterministic: Arc<Deterministic>,
 319    cx_a: &mut TestAppContext,
 320    cx_b: &mut TestAppContext,
 321) {
 322    deterministic.forbid_parking();
 323    let mut server = TestServer::start(&deterministic).await;
 324
 325    let client_a = server.create_client(cx_a, "user_a").await;
 326    let client_b = server.create_client(cx_b, "user_b").await;
 327
 328    let parent_id = server
 329        .make_channel("parent", (&client_a, cx_a), &mut [(&client_b, cx_b)])
 330        .await;
 331
 332    let sub_id = client_a
 333        .channel_store()
 334        .update(cx_a, |channel_store, cx| {
 335            channel_store.create_channel("sub_channel", Some(parent_id), cx)
 336        })
 337        .await
 338        .unwrap();
 339
 340    let active_call_b = cx_b.read(ActiveCall::global);
 341
 342    assert!(active_call_b
 343        .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
 344        .await
 345        .is_ok());
 346}
 347
 348#[gpui::test]
 349async fn test_channel_room(
 350    deterministic: Arc<Deterministic>,
 351    cx_a: &mut TestAppContext,
 352    cx_b: &mut TestAppContext,
 353    cx_c: &mut TestAppContext,
 354) {
 355    deterministic.forbid_parking();
 356    let mut server = TestServer::start(&deterministic).await;
 357    let client_a = server.create_client(cx_a, "user_a").await;
 358    let client_b = server.create_client(cx_b, "user_b").await;
 359    let client_c = server.create_client(cx_c, "user_c").await;
 360
 361    let zed_id = server
 362        .make_channel(
 363            "zed",
 364            (&client_a, cx_a),
 365            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 366        )
 367        .await;
 368
 369    let active_call_a = cx_a.read(ActiveCall::global);
 370    let active_call_b = cx_b.read(ActiveCall::global);
 371
 372    active_call_a
 373        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 374        .await
 375        .unwrap();
 376
 377    // Give everyone a chance to observe user A joining
 378    deterministic.run_until_parked();
 379
 380    client_a.channel_store().read_with(cx_a, |channels, _| {
 381        assert_participants_eq(
 382            channels.channel_participants(zed_id),
 383            &[client_a.user_id().unwrap()],
 384        );
 385    });
 386
 387    assert_channels(
 388        client_b.channel_store(),
 389        cx_b,
 390        &[ExpectedChannel {
 391            id: zed_id,
 392            name: "zed".to_string(),
 393            depth: 0,
 394            user_is_admin: false,
 395        }],
 396    );
 397    client_b.channel_store().read_with(cx_b, |channels, _| {
 398        assert_participants_eq(
 399            channels.channel_participants(zed_id),
 400            &[client_a.user_id().unwrap()],
 401        );
 402    });
 403
 404    client_c.channel_store().read_with(cx_c, |channels, _| {
 405        assert_participants_eq(
 406            channels.channel_participants(zed_id),
 407            &[client_a.user_id().unwrap()],
 408        );
 409    });
 410
 411    active_call_b
 412        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 413        .await
 414        .unwrap();
 415
 416    deterministic.run_until_parked();
 417
 418    client_a.channel_store().read_with(cx_a, |channels, _| {
 419        assert_participants_eq(
 420            channels.channel_participants(zed_id),
 421            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 422        );
 423    });
 424
 425    client_b.channel_store().read_with(cx_b, |channels, _| {
 426        assert_participants_eq(
 427            channels.channel_participants(zed_id),
 428            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 429        );
 430    });
 431
 432    client_c.channel_store().read_with(cx_c, |channels, _| {
 433        assert_participants_eq(
 434            channels.channel_participants(zed_id),
 435            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 436        );
 437    });
 438
 439    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 440    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 441    assert_eq!(
 442        room_participants(&room_a, cx_a),
 443        RoomParticipants {
 444            remote: vec!["user_b".to_string()],
 445            pending: vec![]
 446        }
 447    );
 448
 449    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 450    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 451    assert_eq!(
 452        room_participants(&room_b, cx_b),
 453        RoomParticipants {
 454            remote: vec!["user_a".to_string()],
 455            pending: vec![]
 456        }
 457    );
 458
 459    // Make sure that leaving and rejoining works
 460
 461    active_call_a
 462        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 463        .await
 464        .unwrap();
 465
 466    deterministic.run_until_parked();
 467
 468    client_a.channel_store().read_with(cx_a, |channels, _| {
 469        assert_participants_eq(
 470            channels.channel_participants(zed_id),
 471            &[client_b.user_id().unwrap()],
 472        );
 473    });
 474
 475    client_b.channel_store().read_with(cx_b, |channels, _| {
 476        assert_participants_eq(
 477            channels.channel_participants(zed_id),
 478            &[client_b.user_id().unwrap()],
 479        );
 480    });
 481
 482    client_c.channel_store().read_with(cx_c, |channels, _| {
 483        assert_participants_eq(
 484            channels.channel_participants(zed_id),
 485            &[client_b.user_id().unwrap()],
 486        );
 487    });
 488
 489    active_call_b
 490        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 491        .await
 492        .unwrap();
 493
 494    deterministic.run_until_parked();
 495
 496    client_a.channel_store().read_with(cx_a, |channels, _| {
 497        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 498    });
 499
 500    client_b.channel_store().read_with(cx_b, |channels, _| {
 501        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 502    });
 503
 504    client_c.channel_store().read_with(cx_c, |channels, _| {
 505        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 506    });
 507
 508    active_call_a
 509        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 510        .await
 511        .unwrap();
 512
 513    active_call_b
 514        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 515        .await
 516        .unwrap();
 517
 518    deterministic.run_until_parked();
 519
 520    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 521    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 522    assert_eq!(
 523        room_participants(&room_a, cx_a),
 524        RoomParticipants {
 525            remote: vec!["user_b".to_string()],
 526            pending: vec![]
 527        }
 528    );
 529
 530    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 531    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 532    assert_eq!(
 533        room_participants(&room_b, cx_b),
 534        RoomParticipants {
 535            remote: vec!["user_a".to_string()],
 536            pending: vec![]
 537        }
 538    );
 539}
 540
 541#[gpui::test]
 542async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
 543    deterministic.forbid_parking();
 544    let mut server = TestServer::start(&deterministic).await;
 545    let client_a = server.create_client(cx_a, "user_a").await;
 546
 547    let zed_id = server.make_channel("zed", (&client_a, cx_a), &mut []).await;
 548    let rust_id = server
 549        .make_channel("rust", (&client_a, cx_a), &mut [])
 550        .await;
 551
 552    let active_call_a = cx_a.read(ActiveCall::global);
 553
 554    active_call_a
 555        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 556        .await
 557        .unwrap();
 558
 559    // Give everything a chance to observe user A joining
 560    deterministic.run_until_parked();
 561
 562    client_a.channel_store().read_with(cx_a, |channels, _| {
 563        assert_participants_eq(
 564            channels.channel_participants(zed_id),
 565            &[client_a.user_id().unwrap()],
 566        );
 567        assert_participants_eq(channels.channel_participants(rust_id), &[]);
 568    });
 569
 570    active_call_a
 571        .update(cx_a, |active_call, cx| {
 572            active_call.join_channel(rust_id, cx)
 573        })
 574        .await
 575        .unwrap();
 576
 577    deterministic.run_until_parked();
 578
 579    client_a.channel_store().read_with(cx_a, |channels, _| {
 580        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 581        assert_participants_eq(
 582            channels.channel_participants(rust_id),
 583            &[client_a.user_id().unwrap()],
 584        );
 585    });
 586}
 587
 588#[gpui::test]
 589async fn test_permissions_update_while_invited(
 590    deterministic: Arc<Deterministic>,
 591    cx_a: &mut TestAppContext,
 592    cx_b: &mut TestAppContext,
 593) {
 594    deterministic.forbid_parking();
 595    let mut server = TestServer::start(&deterministic).await;
 596    let client_a = server.create_client(cx_a, "user_a").await;
 597    let client_b = server.create_client(cx_b, "user_b").await;
 598
 599    let rust_id = server
 600        .make_channel("rust", (&client_a, cx_a), &mut [])
 601        .await;
 602
 603    client_a
 604        .channel_store()
 605        .update(cx_a, |channel_store, cx| {
 606            channel_store.invite_member(rust_id, client_b.user_id().unwrap(), false, cx)
 607        })
 608        .await
 609        .unwrap();
 610
 611    deterministic.run_until_parked();
 612
 613    assert_channel_invitations(
 614        client_b.channel_store(),
 615        cx_b,
 616        &[ExpectedChannel {
 617            depth: 0,
 618            id: rust_id,
 619            name: "rust".to_string(),
 620            user_is_admin: false,
 621        }],
 622    );
 623    assert_channels(client_b.channel_store(), cx_b, &[]);
 624
 625    // Update B's invite before they've accepted it
 626    client_a
 627        .channel_store()
 628        .update(cx_a, |channel_store, cx| {
 629            channel_store.set_member_admin(rust_id, client_b.user_id().unwrap(), true, cx)
 630        })
 631        .await
 632        .unwrap();
 633
 634    deterministic.run_until_parked();
 635
 636    assert_channel_invitations(
 637        client_b.channel_store(),
 638        cx_b,
 639        &[ExpectedChannel {
 640            depth: 0,
 641            id: rust_id,
 642            name: "rust".to_string(),
 643            user_is_admin: false,
 644        }],
 645    );
 646    assert_channels(client_b.channel_store(), cx_b, &[]);
 647}
 648
 649#[gpui::test]
 650async fn test_channel_rename(
 651    deterministic: Arc<Deterministic>,
 652    cx_a: &mut TestAppContext,
 653    cx_b: &mut TestAppContext,
 654) {
 655    deterministic.forbid_parking();
 656    let mut server = TestServer::start(&deterministic).await;
 657    let client_a = server.create_client(cx_a, "user_a").await;
 658    let client_b = server.create_client(cx_b, "user_b").await;
 659
 660    let rust_id = server
 661        .make_channel("rust", (&client_a, cx_a), &mut [(&client_b, cx_b)])
 662        .await;
 663
 664    // Rename the channel
 665    client_a
 666        .channel_store()
 667        .update(cx_a, |channel_store, cx| {
 668            channel_store.rename(rust_id, "#rust-archive", cx)
 669        })
 670        .await
 671        .unwrap();
 672
 673    deterministic.run_until_parked();
 674
 675    // Client A sees the channel with its new name.
 676    assert_channels(
 677        client_a.channel_store(),
 678        cx_a,
 679        &[ExpectedChannel {
 680            depth: 0,
 681            id: rust_id,
 682            name: "rust-archive".to_string(),
 683            user_is_admin: true,
 684        }],
 685    );
 686
 687    // Client B sees the channel with its new name.
 688    assert_channels(
 689        client_b.channel_store(),
 690        cx_b,
 691        &[ExpectedChannel {
 692            depth: 0,
 693            id: rust_id,
 694            name: "rust-archive".to_string(),
 695            user_is_admin: false,
 696        }],
 697    );
 698}
 699
 700#[gpui::test]
 701async fn test_call_from_channel(
 702    deterministic: Arc<Deterministic>,
 703    cx_a: &mut TestAppContext,
 704    cx_b: &mut TestAppContext,
 705    cx_c: &mut TestAppContext,
 706) {
 707    deterministic.forbid_parking();
 708    let mut server = TestServer::start(&deterministic).await;
 709    let client_a = server.create_client(cx_a, "user_a").await;
 710    let client_b = server.create_client(cx_b, "user_b").await;
 711    let client_c = server.create_client(cx_c, "user_c").await;
 712    server
 713        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 714        .await;
 715
 716    let channel_id = server
 717        .make_channel(
 718            "x",
 719            (&client_a, cx_a),
 720            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 721        )
 722        .await;
 723
 724    let active_call_a = cx_a.read(ActiveCall::global);
 725    let active_call_b = cx_b.read(ActiveCall::global);
 726
 727    active_call_a
 728        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 729        .await
 730        .unwrap();
 731
 732    // Client A calls client B while in the channel.
 733    active_call_a
 734        .update(cx_a, |call, cx| {
 735            call.invite(client_b.user_id().unwrap(), None, cx)
 736        })
 737        .await
 738        .unwrap();
 739
 740    // Client B accepts the call.
 741    deterministic.run_until_parked();
 742    active_call_b
 743        .update(cx_b, |call, cx| call.accept_incoming(cx))
 744        .await
 745        .unwrap();
 746
 747    // Client B sees that they are now in the channel
 748    deterministic.run_until_parked();
 749    active_call_b.read_with(cx_b, |call, cx| {
 750        assert_eq!(call.channel_id(cx), Some(channel_id));
 751    });
 752    client_b.channel_store().read_with(cx_b, |channels, _| {
 753        assert_participants_eq(
 754            channels.channel_participants(channel_id),
 755            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 756        );
 757    });
 758
 759    // Clients A and C also see that client B is in the channel.
 760    client_a.channel_store().read_with(cx_a, |channels, _| {
 761        assert_participants_eq(
 762            channels.channel_participants(channel_id),
 763            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 764        );
 765    });
 766    client_c.channel_store().read_with(cx_c, |channels, _| {
 767        assert_participants_eq(
 768            channels.channel_participants(channel_id),
 769            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 770        );
 771    });
 772}
 773
 774#[gpui::test]
 775async fn test_lost_channel_creation(
 776    deterministic: Arc<Deterministic>,
 777    cx_a: &mut TestAppContext,
 778    cx_b: &mut TestAppContext,
 779) {
 780    deterministic.forbid_parking();
 781    let mut server = TestServer::start(&deterministic).await;
 782    let client_a = server.create_client(cx_a, "user_a").await;
 783    let client_b = server.create_client(cx_b, "user_b").await;
 784
 785    server
 786        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 787        .await;
 788
 789    let channel_id = server.make_channel("x", (&client_a, cx_a), &mut []).await;
 790
 791    // Invite a member
 792    client_a
 793        .channel_store()
 794        .update(cx_a, |channel_store, cx| {
 795            channel_store.invite_member(channel_id, client_b.user_id().unwrap(), false, cx)
 796        })
 797        .await
 798        .unwrap();
 799
 800    deterministic.run_until_parked();
 801
 802    // Sanity check, B has the invitation
 803    assert_channel_invitations(
 804        client_b.channel_store(),
 805        cx_b,
 806        &[ExpectedChannel {
 807            depth: 0,
 808            id: channel_id,
 809            name: "x".to_string(),
 810            user_is_admin: false,
 811        }],
 812    );
 813
 814    // A creates a subchannel while the invite is still pending.
 815    let subchannel_id = client_a
 816        .channel_store()
 817        .update(cx_a, |channel_store, cx| {
 818            channel_store.create_channel("subchannel", Some(channel_id), cx)
 819        })
 820        .await
 821        .unwrap();
 822
 823    deterministic.run_until_parked();
 824
 825    // Make sure A sees their new channel
 826    assert_channels(
 827        client_a.channel_store(),
 828        cx_a,
 829        &[
 830            ExpectedChannel {
 831                depth: 0,
 832                id: channel_id,
 833                name: "x".to_string(),
 834                user_is_admin: true,
 835            },
 836            ExpectedChannel {
 837                depth: 1,
 838                id: subchannel_id,
 839                name: "subchannel".to_string(),
 840                user_is_admin: true,
 841            },
 842        ],
 843    );
 844
 845    // Client B accepts the invite
 846    client_b
 847        .channel_store()
 848        .update(cx_b, |channel_store, _| {
 849            channel_store.respond_to_channel_invite(channel_id, true)
 850        })
 851        .await
 852        .unwrap();
 853
 854    deterministic.run_until_parked();
 855
 856    // Client B should now see the channel
 857    assert_channels(
 858        client_b.channel_store(),
 859        cx_b,
 860        &[
 861            ExpectedChannel {
 862                depth: 0,
 863                id: channel_id,
 864                name: "x".to_string(),
 865                user_is_admin: false,
 866            },
 867            ExpectedChannel {
 868                depth: 1,
 869                id: subchannel_id,
 870                name: "subchannel".to_string(),
 871                user_is_admin: false,
 872            },
 873        ],
 874    );
 875}
 876
 877#[gpui::test]
 878async fn test_channel_moving(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
 879    deterministic.forbid_parking();
 880    let mut server = TestServer::start(&deterministic).await;
 881    let client_a = server.create_client(cx_a, "user_a").await;
 882
 883    let channel_a_id = client_a
 884        .channel_store()
 885        .update(cx_a, |channel_store, cx| {
 886            channel_store.create_channel("channel-a", None, cx)
 887        })
 888        .await
 889        .unwrap();
 890    let channel_b_id = client_a
 891        .channel_store()
 892        .update(cx_a, |channel_store, cx| {
 893            channel_store.create_channel("channel-b", Some(channel_a_id), cx)
 894        })
 895        .await
 896        .unwrap();
 897    let channel_c_id = client_a
 898        .channel_store()
 899        .update(cx_a, |channel_store, cx| {
 900            channel_store.create_channel("channel-c", Some(channel_b_id), cx)
 901        })
 902        .await
 903        .unwrap();
 904
 905    // Current shape:
 906    // a - b - c
 907    deterministic.run_until_parked();
 908    assert_channels(
 909        client_a.channel_store(),
 910        cx_a,
 911        &[
 912            ExpectedChannel {
 913                id: channel_a_id,
 914                name: "channel-a".to_string(),
 915                depth: 0,
 916                user_is_admin: true,
 917            },
 918            ExpectedChannel {
 919                id: channel_b_id,
 920                name: "channel-b".to_string(),
 921                depth: 1,
 922                user_is_admin: true,
 923            },
 924            ExpectedChannel {
 925                id: channel_c_id,
 926                name: "channel-c".to_string(),
 927                depth: 2,
 928                user_is_admin: true,
 929            },
 930        ],
 931    );
 932
 933    client_a
 934        .channel_store()
 935        .update(cx_a, |channel_store, cx| {
 936            channel_store.move_channel(channel_c_id, Some(channel_b_id), Some(channel_a_id), cx)
 937        })
 938        .await
 939        .unwrap();
 940
 941    // Current shape:
 942    //   /- c
 943    // a -- b
 944    deterministic.run_until_parked();
 945    assert_channels(
 946        client_a.channel_store(),
 947        cx_a,
 948        &[
 949            ExpectedChannel {
 950                id: channel_a_id,
 951                name: "channel-a".to_string(),
 952                depth: 0,
 953                user_is_admin: true,
 954            },
 955            ExpectedChannel {
 956                id: channel_b_id,
 957                name: "channel-b".to_string(),
 958                depth: 1,
 959                user_is_admin: true,
 960            },
 961            ExpectedChannel {
 962                id: channel_c_id,
 963                name: "channel-c".to_string(),
 964                depth: 1,
 965                user_is_admin: true,
 966            },
 967        ],
 968    );
 969
 970    client_a
 971        .channel_store()
 972        .update(cx_a, |channel_store, cx| {
 973            channel_store.move_channel(channel_c_id, None, Some(channel_b_id), cx)
 974        })
 975        .await
 976        .unwrap();
 977
 978    // Current shape:
 979    //   /------\
 980    // a -- b -- c
 981    deterministic.run_until_parked();
 982    assert_channels(
 983        client_a.channel_store(),
 984        cx_a,
 985        &[
 986            ExpectedChannel {
 987                id: channel_a_id,
 988                name: "channel-a".to_string(),
 989                depth: 0,
 990                user_is_admin: true,
 991            },
 992            ExpectedChannel {
 993                id: channel_b_id,
 994                name: "channel-b".to_string(),
 995                depth: 1,
 996                user_is_admin: true,
 997            },
 998            ExpectedChannel {
 999                id: channel_c_id,
1000                name: "channel-c".to_string(),
1001                depth: 2,
1002                user_is_admin: true,
1003            },
1004            ExpectedChannel {
1005                id: channel_c_id,
1006                name: "channel-c".to_string(),
1007                depth: 1,
1008                user_is_admin: true,
1009            },
1010        ],
1011    );
1012}
1013
1014#[derive(Debug, PartialEq)]
1015struct ExpectedChannel {
1016    depth: usize,
1017    id: ChannelId,
1018    name: String,
1019    user_is_admin: bool,
1020}
1021
1022#[track_caller]
1023fn assert_channel_invitations(
1024    channel_store: &ModelHandle<ChannelStore>,
1025    cx: &TestAppContext,
1026    expected_channels: &[ExpectedChannel],
1027) {
1028    let actual = channel_store.read_with(cx, |store, _| {
1029        store
1030            .channel_invitations()
1031            .iter()
1032            .map(|channel| ExpectedChannel {
1033                depth: 0,
1034                name: channel.name.clone(),
1035                id: channel.id,
1036                user_is_admin: store.is_user_admin(channel.id),
1037            })
1038            .collect::<Vec<_>>()
1039    });
1040    assert_eq!(actual, expected_channels);
1041}
1042
1043#[track_caller]
1044fn assert_channels(
1045    channel_store: &ModelHandle<ChannelStore>,
1046    cx: &TestAppContext,
1047    expected_channels: &[ExpectedChannel],
1048) {
1049    let actual = channel_store.read_with(cx, |store, _| {
1050        store
1051            .channels()
1052            .map(|(depth, channel)| ExpectedChannel {
1053                depth,
1054                name: channel.name.clone(),
1055                id: channel.id,
1056                user_is_admin: store.is_user_admin(channel.id),
1057            })
1058            .collect::<Vec<_>>()
1059    });
1060    pretty_assertions::assert_eq!(actual, expected_channels);
1061}