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