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