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
 384    client_a.channel_store().read_with(cx_a, |channels, _| {
 385        assert_participants_eq(
 386            channels.channel_participants(zed_id),
 387            &[client_a.user_id().unwrap()],
 388        );
 389    });
 390
 391    assert_channels(
 392        client_b.channel_store(),
 393        cx_b,
 394        &[ExpectedChannel {
 395            id: zed_id,
 396            name: "zed".to_string(),
 397            depth: 0,
 398            user_is_admin: false,
 399        }],
 400    );
 401    client_b.channel_store().read_with(cx_b, |channels, _| {
 402        assert_participants_eq(
 403            channels.channel_participants(zed_id),
 404            &[client_a.user_id().unwrap()],
 405        );
 406    });
 407
 408    client_c.channel_store().read_with(cx_c, |channels, _| {
 409        assert_participants_eq(
 410            channels.channel_participants(zed_id),
 411            &[client_a.user_id().unwrap()],
 412        );
 413    });
 414
 415    active_call_b
 416        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 417        .await
 418        .unwrap();
 419
 420    deterministic.run_until_parked();
 421
 422    client_a.channel_store().read_with(cx_a, |channels, _| {
 423        assert_participants_eq(
 424            channels.channel_participants(zed_id),
 425            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 426        );
 427    });
 428
 429    client_b.channel_store().read_with(cx_b, |channels, _| {
 430        assert_participants_eq(
 431            channels.channel_participants(zed_id),
 432            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 433        );
 434    });
 435
 436    client_c.channel_store().read_with(cx_c, |channels, _| {
 437        assert_participants_eq(
 438            channels.channel_participants(zed_id),
 439            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 440        );
 441    });
 442
 443    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 444    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 445    assert_eq!(
 446        room_participants(&room_a, cx_a),
 447        RoomParticipants {
 448            remote: vec!["user_b".to_string()],
 449            pending: vec![]
 450        }
 451    );
 452
 453    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 454    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 455    assert_eq!(
 456        room_participants(&room_b, cx_b),
 457        RoomParticipants {
 458            remote: vec!["user_a".to_string()],
 459            pending: vec![]
 460        }
 461    );
 462
 463    // Make sure that leaving and rejoining works
 464
 465    active_call_a
 466        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 467        .await
 468        .unwrap();
 469
 470    deterministic.run_until_parked();
 471
 472    client_a.channel_store().read_with(cx_a, |channels, _| {
 473        assert_participants_eq(
 474            channels.channel_participants(zed_id),
 475            &[client_b.user_id().unwrap()],
 476        );
 477    });
 478
 479    client_b.channel_store().read_with(cx_b, |channels, _| {
 480        assert_participants_eq(
 481            channels.channel_participants(zed_id),
 482            &[client_b.user_id().unwrap()],
 483        );
 484    });
 485
 486    client_c.channel_store().read_with(cx_c, |channels, _| {
 487        assert_participants_eq(
 488            channels.channel_participants(zed_id),
 489            &[client_b.user_id().unwrap()],
 490        );
 491    });
 492
 493    active_call_b
 494        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 495        .await
 496        .unwrap();
 497
 498    deterministic.run_until_parked();
 499
 500    client_a.channel_store().read_with(cx_a, |channels, _| {
 501        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 502    });
 503
 504    client_b.channel_store().read_with(cx_b, |channels, _| {
 505        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 506    });
 507
 508    client_c.channel_store().read_with(cx_c, |channels, _| {
 509        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 510    });
 511
 512    active_call_a
 513        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 514        .await
 515        .unwrap();
 516
 517    active_call_b
 518        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 519        .await
 520        .unwrap();
 521
 522    deterministic.run_until_parked();
 523
 524    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 525    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 526    assert_eq!(
 527        room_participants(&room_a, cx_a),
 528        RoomParticipants {
 529            remote: vec!["user_b".to_string()],
 530            pending: vec![]
 531        }
 532    );
 533
 534    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 535    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 536    assert_eq!(
 537        room_participants(&room_b, cx_b),
 538        RoomParticipants {
 539            remote: vec!["user_a".to_string()],
 540            pending: vec![]
 541        }
 542    );
 543}
 544
 545#[gpui::test]
 546async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
 547    deterministic.forbid_parking();
 548    let mut server = TestServer::start(&deterministic).await;
 549    let client_a = server.create_client(cx_a, "user_a").await;
 550
 551    let zed_id = server
 552        .make_channel("zed", None, (&client_a, cx_a), &mut [])
 553        .await;
 554    let rust_id = server
 555        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 556        .await;
 557
 558    let active_call_a = cx_a.read(ActiveCall::global);
 559
 560    active_call_a
 561        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 562        .await
 563        .unwrap();
 564
 565    // Give everything a chance to observe user A joining
 566    deterministic.run_until_parked();
 567
 568    client_a.channel_store().read_with(cx_a, |channels, _| {
 569        assert_participants_eq(
 570            channels.channel_participants(zed_id),
 571            &[client_a.user_id().unwrap()],
 572        );
 573        assert_participants_eq(channels.channel_participants(rust_id), &[]);
 574    });
 575
 576    active_call_a
 577        .update(cx_a, |active_call, cx| {
 578            active_call.join_channel(rust_id, cx)
 579        })
 580        .await
 581        .unwrap();
 582
 583    deterministic.run_until_parked();
 584
 585    client_a.channel_store().read_with(cx_a, |channels, _| {
 586        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 587        assert_participants_eq(
 588            channels.channel_participants(rust_id),
 589            &[client_a.user_id().unwrap()],
 590        );
 591    });
 592}
 593
 594#[gpui::test]
 595async fn test_permissions_update_while_invited(
 596    deterministic: Arc<Deterministic>,
 597    cx_a: &mut TestAppContext,
 598    cx_b: &mut TestAppContext,
 599) {
 600    deterministic.forbid_parking();
 601    let mut server = TestServer::start(&deterministic).await;
 602    let client_a = server.create_client(cx_a, "user_a").await;
 603    let client_b = server.create_client(cx_b, "user_b").await;
 604
 605    let rust_id = server
 606        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 607        .await;
 608
 609    client_a
 610        .channel_store()
 611        .update(cx_a, |channel_store, cx| {
 612            channel_store.invite_member(rust_id, client_b.user_id().unwrap(), false, cx)
 613        })
 614        .await
 615        .unwrap();
 616
 617    deterministic.run_until_parked();
 618
 619    assert_channel_invitations(
 620        client_b.channel_store(),
 621        cx_b,
 622        &[ExpectedChannel {
 623            depth: 0,
 624            id: rust_id,
 625            name: "rust".to_string(),
 626            user_is_admin: false,
 627        }],
 628    );
 629    assert_channels(client_b.channel_store(), cx_b, &[]);
 630
 631    // Update B's invite before they've accepted it
 632    client_a
 633        .channel_store()
 634        .update(cx_a, |channel_store, cx| {
 635            channel_store.set_member_admin(rust_id, client_b.user_id().unwrap(), true, cx)
 636        })
 637        .await
 638        .unwrap();
 639
 640    deterministic.run_until_parked();
 641
 642    assert_channel_invitations(
 643        client_b.channel_store(),
 644        cx_b,
 645        &[ExpectedChannel {
 646            depth: 0,
 647            id: rust_id,
 648            name: "rust".to_string(),
 649            user_is_admin: false,
 650        }],
 651    );
 652    assert_channels(client_b.channel_store(), cx_b, &[]);
 653}
 654
 655#[gpui::test]
 656async fn test_channel_rename(
 657    deterministic: Arc<Deterministic>,
 658    cx_a: &mut TestAppContext,
 659    cx_b: &mut TestAppContext,
 660) {
 661    deterministic.forbid_parking();
 662    let mut server = TestServer::start(&deterministic).await;
 663    let client_a = server.create_client(cx_a, "user_a").await;
 664    let client_b = server.create_client(cx_b, "user_b").await;
 665
 666    let rust_id = server
 667        .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 668        .await;
 669
 670    // Rename the channel
 671    client_a
 672        .channel_store()
 673        .update(cx_a, |channel_store, cx| {
 674            channel_store.rename(rust_id, "#rust-archive", cx)
 675        })
 676        .await
 677        .unwrap();
 678
 679    deterministic.run_until_parked();
 680
 681    // Client A sees the channel with its new name.
 682    assert_channels(
 683        client_a.channel_store(),
 684        cx_a,
 685        &[ExpectedChannel {
 686            depth: 0,
 687            id: rust_id,
 688            name: "rust-archive".to_string(),
 689            user_is_admin: true,
 690        }],
 691    );
 692
 693    // Client B sees the channel with its new name.
 694    assert_channels(
 695        client_b.channel_store(),
 696        cx_b,
 697        &[ExpectedChannel {
 698            depth: 0,
 699            id: rust_id,
 700            name: "rust-archive".to_string(),
 701            user_is_admin: false,
 702        }],
 703    );
 704}
 705
 706#[gpui::test]
 707async fn test_call_from_channel(
 708    deterministic: Arc<Deterministic>,
 709    cx_a: &mut TestAppContext,
 710    cx_b: &mut TestAppContext,
 711    cx_c: &mut TestAppContext,
 712) {
 713    deterministic.forbid_parking();
 714    let mut server = TestServer::start(&deterministic).await;
 715    let client_a = server.create_client(cx_a, "user_a").await;
 716    let client_b = server.create_client(cx_b, "user_b").await;
 717    let client_c = server.create_client(cx_c, "user_c").await;
 718    server
 719        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 720        .await;
 721
 722    let channel_id = server
 723        .make_channel(
 724            "x",
 725            None,
 726            (&client_a, cx_a),
 727            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 728        )
 729        .await;
 730
 731    let active_call_a = cx_a.read(ActiveCall::global);
 732    let active_call_b = cx_b.read(ActiveCall::global);
 733
 734    active_call_a
 735        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 736        .await
 737        .unwrap();
 738
 739    // Client A calls client B while in the channel.
 740    active_call_a
 741        .update(cx_a, |call, cx| {
 742            call.invite(client_b.user_id().unwrap(), None, cx)
 743        })
 744        .await
 745        .unwrap();
 746
 747    // Client B accepts the call.
 748    deterministic.run_until_parked();
 749    active_call_b
 750        .update(cx_b, |call, cx| call.accept_incoming(cx))
 751        .await
 752        .unwrap();
 753
 754    // Client B sees that they are now in the channel
 755    deterministic.run_until_parked();
 756    active_call_b.read_with(cx_b, |call, cx| {
 757        assert_eq!(call.channel_id(cx), Some(channel_id));
 758    });
 759    client_b.channel_store().read_with(cx_b, |channels, _| {
 760        assert_participants_eq(
 761            channels.channel_participants(channel_id),
 762            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 763        );
 764    });
 765
 766    // Clients A and C also see that client B is in the channel.
 767    client_a.channel_store().read_with(cx_a, |channels, _| {
 768        assert_participants_eq(
 769            channels.channel_participants(channel_id),
 770            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 771        );
 772    });
 773    client_c.channel_store().read_with(cx_c, |channels, _| {
 774        assert_participants_eq(
 775            channels.channel_participants(channel_id),
 776            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 777        );
 778    });
 779}
 780
 781#[gpui::test]
 782async fn test_lost_channel_creation(
 783    deterministic: Arc<Deterministic>,
 784    cx_a: &mut TestAppContext,
 785    cx_b: &mut TestAppContext,
 786) {
 787    deterministic.forbid_parking();
 788    let mut server = TestServer::start(&deterministic).await;
 789    let client_a = server.create_client(cx_a, "user_a").await;
 790    let client_b = server.create_client(cx_b, "user_b").await;
 791
 792    server
 793        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 794        .await;
 795
 796    let channel_id = server
 797        .make_channel("x", None, (&client_a, cx_a), &mut [])
 798        .await;
 799
 800    // Invite a member
 801    client_a
 802        .channel_store()
 803        .update(cx_a, |channel_store, cx| {
 804            channel_store.invite_member(channel_id, client_b.user_id().unwrap(), false, cx)
 805        })
 806        .await
 807        .unwrap();
 808
 809    deterministic.run_until_parked();
 810
 811    // Sanity check, B has the invitation
 812    assert_channel_invitations(
 813        client_b.channel_store(),
 814        cx_b,
 815        &[ExpectedChannel {
 816            depth: 0,
 817            id: channel_id,
 818            name: "x".to_string(),
 819            user_is_admin: false,
 820        }],
 821    );
 822
 823    // A creates a subchannel while the invite is still pending.
 824    let subchannel_id = client_a
 825        .channel_store()
 826        .update(cx_a, |channel_store, cx| {
 827            channel_store.create_channel("subchannel", Some(channel_id), cx)
 828        })
 829        .await
 830        .unwrap();
 831
 832    deterministic.run_until_parked();
 833
 834    // Make sure A sees their new channel
 835    assert_channels(
 836        client_a.channel_store(),
 837        cx_a,
 838        &[
 839            ExpectedChannel {
 840                depth: 0,
 841                id: channel_id,
 842                name: "x".to_string(),
 843                user_is_admin: true,
 844            },
 845            ExpectedChannel {
 846                depth: 1,
 847                id: subchannel_id,
 848                name: "subchannel".to_string(),
 849                user_is_admin: true,
 850            },
 851        ],
 852    );
 853
 854    // Client B accepts the invite
 855    client_b
 856        .channel_store()
 857        .update(cx_b, |channel_store, _| {
 858            channel_store.respond_to_channel_invite(channel_id, true)
 859        })
 860        .await
 861        .unwrap();
 862
 863    deterministic.run_until_parked();
 864
 865    // Client B should now see the channel
 866    assert_channels(
 867        client_b.channel_store(),
 868        cx_b,
 869        &[
 870            ExpectedChannel {
 871                depth: 0,
 872                id: channel_id,
 873                name: "x".to_string(),
 874                user_is_admin: false,
 875            },
 876            ExpectedChannel {
 877                depth: 1,
 878                id: subchannel_id,
 879                name: "subchannel".to_string(),
 880                user_is_admin: false,
 881            },
 882        ],
 883    );
 884}
 885
 886#[gpui::test]
 887async fn test_channel_moving(
 888    deterministic: Arc<Deterministic>,
 889    cx_a: &mut TestAppContext,
 890    cx_b: &mut TestAppContext,
 891    cx_c: &mut TestAppContext,
 892) {
 893    deterministic.forbid_parking();
 894    let mut server = TestServer::start(&deterministic).await;
 895    let client_a = server.create_client(cx_a, "user_a").await;
 896    let client_b = server.create_client(cx_b, "user_b").await;
 897    let client_c = server.create_client(cx_c, "user_c").await;
 898
 899    let channels = server
 900        .make_channel_tree(
 901            &[
 902                ("channel-a", None),
 903                ("channel-b", Some("channel-a")),
 904                ("channel-c", Some("channel-b")),
 905                ("channel-d", Some("channel-c")),
 906            ],
 907            (&client_a, cx_a),
 908        )
 909        .await;
 910    let channel_a_id = channels[0];
 911    let channel_b_id = channels[1];
 912    let channel_c_id = channels[2];
 913    let channel_d_id = channels[3];
 914
 915    // Current shape:
 916    // a - b - c - d
 917    assert_channels_list_shape(
 918        client_a.channel_store(),
 919        cx_a,
 920        &[
 921            (channel_a_id, 0),
 922            (channel_b_id, 1),
 923            (channel_c_id, 2),
 924            (channel_d_id, 3),
 925        ],
 926    );
 927
 928    client_a
 929        .channel_store()
 930        .update(cx_a, |channel_store, cx| {
 931            channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
 932        })
 933        .await
 934        .unwrap();
 935
 936    // Current shape:
 937    //       /- d
 938    // a - b -- c
 939    assert_channels_list_shape(
 940        client_a.channel_store(),
 941        cx_a,
 942        &[
 943            (channel_a_id, 0),
 944            (channel_b_id, 1),
 945            (channel_c_id, 2),
 946            (channel_d_id, 2),
 947        ],
 948    );
 949
 950    client_a
 951        .channel_store()
 952        .update(cx_a, |channel_store, cx| {
 953            channel_store.link_channel(channel_d_id, channel_c_id, cx)
 954        })
 955        .await
 956        .unwrap();
 957
 958    // Current shape for A:
 959    //      /------\
 960    // a - b -- c -- d
 961    assert_channels_list_shape(
 962        client_a.channel_store(),
 963        cx_a,
 964        &[
 965            (channel_a_id, 0),
 966            (channel_b_id, 1),
 967            (channel_c_id, 2),
 968            (channel_d_id, 3),
 969            (channel_d_id, 2),
 970        ],
 971    );
 972
 973    let b_channels = server
 974        .make_channel_tree(
 975            &[
 976                ("channel-mu", None),
 977                ("channel-gamma", Some("channel-mu")),
 978                ("channel-epsilon", Some("channel-mu")),
 979            ],
 980            (&client_b, cx_b),
 981        )
 982        .await;
 983    let channel_mu_id = b_channels[0];
 984    let channel_ga_id = b_channels[1];
 985    let channel_ep_id = b_channels[2];
 986
 987    // Current shape for B:
 988    //    /- ep
 989    // mu -- ga
 990    assert_channels_list_shape(
 991        client_b.channel_store(),
 992        cx_b,
 993        &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
 994    );
 995
 996    client_a
 997        .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
 998        .await;
 999
1000    // Current shape for B:
1001    //    /- ep
1002    // mu -- ga
1003    //  /---------\
1004    // b  -- c  -- d
1005    assert_channels_list_shape(
1006        client_b.channel_store(),
1007        cx_b,
1008        &[
1009            // New channels from a
1010            (channel_b_id, 0),
1011            (channel_c_id, 1),
1012            (channel_d_id, 2),
1013            (channel_d_id, 1),
1014            // B's old channels
1015            (channel_mu_id, 0),
1016            (channel_ep_id, 1),
1017            (channel_ga_id, 1),
1018        ],
1019    );
1020
1021    client_b
1022        .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
1023        .await;
1024
1025    // Current shape for C:
1026    // - ep
1027    assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
1028
1029    client_b
1030        .channel_store()
1031        .update(cx_b, |channel_store, cx| {
1032            channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1033        })
1034        .await
1035        .unwrap();
1036
1037    // Current shape for B:
1038    //              /---------\
1039    //    /- ep -- b  -- c  -- d
1040    // mu -- ga
1041    assert_channels_list_shape(
1042        client_b.channel_store(),
1043        cx_b,
1044        &[
1045            (channel_mu_id, 0),
1046            (channel_ep_id, 1),
1047            (channel_b_id, 2),
1048            (channel_c_id, 3),
1049            (channel_d_id, 4),
1050            (channel_d_id, 3),
1051            (channel_ga_id, 1),
1052        ],
1053    );
1054
1055    // Current shape for C:
1056    //        /---------\
1057    // ep -- b  -- c  -- d
1058    assert_channels_list_shape(
1059        client_c.channel_store(),
1060        cx_c,
1061        &[
1062            (channel_ep_id, 0),
1063            (channel_b_id, 1),
1064            (channel_c_id, 2),
1065            (channel_d_id, 3),
1066            (channel_d_id, 2),
1067        ],
1068    );
1069
1070    client_b
1071        .channel_store()
1072        .update(cx_b, |channel_store, cx| {
1073            channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1074        })
1075        .await
1076        .unwrap();
1077
1078    // Current shape for B:
1079    //              /---------\
1080    //    /- ep -- b  -- c  -- d
1081    //   /          \
1082    // mu ---------- ga
1083    assert_channels_list_shape(
1084        client_b.channel_store(),
1085        cx_b,
1086        &[
1087            (channel_mu_id, 0),
1088            (channel_ep_id, 1),
1089            (channel_b_id, 2),
1090            (channel_c_id, 3),
1091            (channel_d_id, 4),
1092            (channel_d_id, 3),
1093            (channel_ga_id, 3),
1094            (channel_ga_id, 1),
1095        ],
1096    );
1097
1098    // Current shape for A:
1099    //      /------\
1100    // a - b -- c -- d
1101    //      \-- ga
1102    assert_channels_list_shape(
1103        client_a.channel_store(),
1104        cx_a,
1105        &[
1106            (channel_a_id, 0),
1107            (channel_b_id, 1),
1108            (channel_c_id, 2),
1109            (channel_d_id, 3),
1110            (channel_d_id, 2),
1111            (channel_ga_id, 2),
1112        ],
1113    );
1114
1115    // Current shape for C:
1116    //        /-------\
1117    // ep -- b -- c -- d
1118    //        \-- ga
1119    assert_channels_list_shape(
1120        client_c.channel_store(),
1121        cx_c,
1122        &[
1123            (channel_ep_id, 0),
1124            (channel_b_id, 1),
1125            (channel_c_id, 2),
1126            (channel_d_id, 3),
1127            (channel_d_id, 2),
1128            (channel_ga_id, 2),
1129        ],
1130    );
1131}
1132
1133#[derive(Debug, PartialEq)]
1134struct ExpectedChannel {
1135    depth: usize,
1136    id: ChannelId,
1137    name: String,
1138    user_is_admin: bool,
1139}
1140
1141#[track_caller]
1142fn assert_channel_invitations(
1143    channel_store: &ModelHandle<ChannelStore>,
1144    cx: &TestAppContext,
1145    expected_channels: &[ExpectedChannel],
1146) {
1147    let actual = channel_store.read_with(cx, |store, _| {
1148        store
1149            .channel_invitations()
1150            .iter()
1151            .map(|channel| ExpectedChannel {
1152                depth: 0,
1153                name: channel.name.clone(),
1154                id: channel.id,
1155                user_is_admin: store.is_user_admin(channel.id),
1156            })
1157            .collect::<Vec<_>>()
1158    });
1159    assert_eq!(actual, expected_channels);
1160}
1161
1162#[track_caller]
1163fn assert_channels(
1164    channel_store: &ModelHandle<ChannelStore>,
1165    cx: &TestAppContext,
1166    expected_channels: &[ExpectedChannel],
1167) {
1168    let actual = channel_store.read_with(cx, |store, _| {
1169        store
1170            .channel_dag_entries()
1171            .map(|(depth, channel)| ExpectedChannel {
1172                depth,
1173                name: channel.name.clone(),
1174                id: channel.id,
1175                user_is_admin: store.is_user_admin(channel.id),
1176            })
1177            .collect::<Vec<_>>()
1178    });
1179    pretty_assertions::assert_eq!(actual, expected_channels);
1180}
1181
1182#[track_caller]
1183fn assert_channels_list_shape(
1184    channel_store: &ModelHandle<ChannelStore>,
1185    cx: &TestAppContext,
1186    expected_channels: &[(u64, usize)],
1187) {
1188    cx.foreground().run_until_parked();
1189
1190    let actual = channel_store.read_with(cx, |store, _| {
1191        store
1192            .channel_dag_entries()
1193            .map(|(depth, channel)| (channel.id, depth))
1194            .collect::<Vec<_>>()
1195    });
1196    pretty_assertions::assert_eq!(actual, expected_channels);
1197}