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 futures::future::try_join_all;
   9use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
  10use rpc::{
  11    proto::{self, ChannelRole},
  12    RECEIVE_TIMEOUT,
  13};
  14use std::sync::Arc;
  15
  16#[gpui::test]
  17async fn test_core_channels(
  18    deterministic: Arc<Deterministic>,
  19    cx_a: &mut TestAppContext,
  20    cx_b: &mut TestAppContext,
  21) {
  22    deterministic.forbid_parking();
  23    let mut server = TestServer::start(&deterministic).await;
  24    let client_a = server.create_client(cx_a, "user_a").await;
  25    let client_b = server.create_client(cx_b, "user_b").await;
  26
  27    let channel_a_id = client_a
  28        .channel_store()
  29        .update(cx_a, |channel_store, cx| {
  30            channel_store.create_channel("channel-a", None, cx)
  31        })
  32        .await
  33        .unwrap();
  34    let channel_b_id = client_a
  35        .channel_store()
  36        .update(cx_a, |channel_store, cx| {
  37            channel_store.create_channel("channel-b", Some(channel_a_id), cx)
  38        })
  39        .await
  40        .unwrap();
  41
  42    deterministic.run_until_parked();
  43    assert_channels(
  44        client_a.channel_store(),
  45        cx_a,
  46        &[
  47            ExpectedChannel {
  48                id: channel_a_id,
  49                name: "channel-a".to_string(),
  50                depth: 0,
  51                user_is_admin: true,
  52            },
  53            ExpectedChannel {
  54                id: channel_b_id,
  55                name: "channel-b".to_string(),
  56                depth: 1,
  57                user_is_admin: true,
  58            },
  59        ],
  60    );
  61
  62    client_b.channel_store().read_with(cx_b, |channels, _| {
  63        assert!(channels
  64            .channel_dag_entries()
  65            .collect::<Vec<_>>()
  66            .is_empty())
  67    });
  68
  69    // Invite client B to channel A as client A.
  70    client_a
  71        .channel_store()
  72        .update(cx_a, |store, cx| {
  73            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  74
  75            let invite = store.invite_member(
  76                channel_a_id,
  77                client_b.user_id().unwrap(),
  78                proto::ChannelRole::Member,
  79                cx,
  80            );
  81
  82            // Make sure we're synchronously storing the pending invite
  83            assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
  84            invite
  85        })
  86        .await
  87        .unwrap();
  88
  89    // Client A sees that B has been invited.
  90    deterministic.run_until_parked();
  91    assert_channel_invitations(
  92        client_b.channel_store(),
  93        cx_b,
  94        &[ExpectedChannel {
  95            id: channel_a_id,
  96            name: "channel-a".to_string(),
  97            depth: 0,
  98            user_is_admin: false,
  99        }],
 100    );
 101
 102    let members = client_a
 103        .channel_store()
 104        .update(cx_a, |store, cx| {
 105            assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
 106            store.get_channel_member_details(channel_a_id, cx)
 107        })
 108        .await
 109        .unwrap();
 110    assert_members_eq(
 111        &members,
 112        &[
 113            (
 114                client_a.user_id().unwrap(),
 115                proto::ChannelRole::Admin,
 116                proto::channel_member::Kind::Member,
 117            ),
 118            (
 119                client_b.user_id().unwrap(),
 120                proto::ChannelRole::Member,
 121                proto::channel_member::Kind::Invitee,
 122            ),
 123        ],
 124    );
 125
 126    // Client B accepts the invitation.
 127    client_b
 128        .channel_store()
 129        .update(cx_b, |channels, _| {
 130            channels.respond_to_channel_invite(channel_a_id, true)
 131        })
 132        .await
 133        .unwrap();
 134    deterministic.run_until_parked();
 135
 136    // Client B now sees that they are a member of channel A and its existing subchannels.
 137    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 138    assert_channels(
 139        client_b.channel_store(),
 140        cx_b,
 141        &[
 142            ExpectedChannel {
 143                id: channel_a_id,
 144                name: "channel-a".to_string(),
 145                user_is_admin: false,
 146                depth: 0,
 147            },
 148            ExpectedChannel {
 149                id: channel_b_id,
 150                name: "channel-b".to_string(),
 151                user_is_admin: false,
 152                depth: 1,
 153            },
 154        ],
 155    );
 156    dbg!("-------");
 157
 158    let channel_c_id = client_a
 159        .channel_store()
 160        .update(cx_a, |channel_store, cx| {
 161            channel_store.create_channel("channel-c", Some(channel_b_id), cx)
 162        })
 163        .await
 164        .unwrap();
 165
 166    deterministic.run_until_parked();
 167    assert_channels(
 168        client_b.channel_store(),
 169        cx_b,
 170        &[
 171            ExpectedChannel {
 172                id: channel_a_id,
 173                name: "channel-a".to_string(),
 174                user_is_admin: false,
 175                depth: 0,
 176            },
 177            ExpectedChannel {
 178                id: channel_b_id,
 179                name: "channel-b".to_string(),
 180                user_is_admin: false,
 181                depth: 1,
 182            },
 183            ExpectedChannel {
 184                id: channel_c_id,
 185                name: "channel-c".to_string(),
 186                user_is_admin: false,
 187                depth: 2,
 188            },
 189        ],
 190    );
 191
 192    // Update client B's membership to channel A to be an admin.
 193    client_a
 194        .channel_store()
 195        .update(cx_a, |store, cx| {
 196            store.set_member_role(
 197                channel_a_id,
 198                client_b.user_id().unwrap(),
 199                proto::ChannelRole::Admin,
 200                cx,
 201            )
 202        })
 203        .await
 204        .unwrap();
 205    deterministic.run_until_parked();
 206
 207    // Observe that client B is now an admin of channel A, and that
 208    // their admin priveleges extend to subchannels of channel A.
 209    assert_channel_invitations(client_b.channel_store(), cx_b, &[]);
 210    assert_channels(
 211        client_b.channel_store(),
 212        cx_b,
 213        &[
 214            ExpectedChannel {
 215                id: channel_a_id,
 216                name: "channel-a".to_string(),
 217                depth: 0,
 218                user_is_admin: true,
 219            },
 220            ExpectedChannel {
 221                id: channel_b_id,
 222                name: "channel-b".to_string(),
 223                depth: 1,
 224                user_is_admin: true,
 225            },
 226            ExpectedChannel {
 227                id: channel_c_id,
 228                name: "channel-c".to_string(),
 229                depth: 2,
 230                user_is_admin: true,
 231            },
 232        ],
 233    );
 234
 235    // Client A deletes the channel, deletion also deletes subchannels.
 236    client_a
 237        .channel_store()
 238        .update(cx_a, |channel_store, _| {
 239            channel_store.remove_channel(channel_b_id)
 240        })
 241        .await
 242        .unwrap();
 243
 244    deterministic.run_until_parked();
 245    assert_channels(
 246        client_a.channel_store(),
 247        cx_a,
 248        &[ExpectedChannel {
 249            id: channel_a_id,
 250            name: "channel-a".to_string(),
 251            depth: 0,
 252            user_is_admin: true,
 253        }],
 254    );
 255    assert_channels(
 256        client_b.channel_store(),
 257        cx_b,
 258        &[ExpectedChannel {
 259            id: channel_a_id,
 260            name: "channel-a".to_string(),
 261            depth: 0,
 262            user_is_admin: true,
 263        }],
 264    );
 265
 266    // Remove client B
 267    client_a
 268        .channel_store()
 269        .update(cx_a, |channel_store, cx| {
 270            channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx)
 271        })
 272        .await
 273        .unwrap();
 274
 275    deterministic.run_until_parked();
 276
 277    // Client A still has their channel
 278    assert_channels(
 279        client_a.channel_store(),
 280        cx_a,
 281        &[ExpectedChannel {
 282            id: channel_a_id,
 283            name: "channel-a".to_string(),
 284            depth: 0,
 285            user_is_admin: true,
 286        }],
 287    );
 288
 289    // Client B no longer has access to the channel
 290    assert_channels(client_b.channel_store(), cx_b, &[]);
 291
 292    // When disconnected, client A sees no channels.
 293    server.forbid_connections();
 294    server.disconnect_client(client_a.peer_id().unwrap());
 295    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 296    assert_channels(client_a.channel_store(), cx_a, &[]);
 297
 298    server.allow_connections();
 299    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
 300    assert_channels(
 301        client_a.channel_store(),
 302        cx_a,
 303        &[ExpectedChannel {
 304            id: channel_a_id,
 305            name: "channel-a".to_string(),
 306            depth: 0,
 307            user_is_admin: true,
 308        }],
 309    );
 310}
 311
 312#[track_caller]
 313fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) {
 314    assert_eq!(
 315        participants.iter().map(|p| p.id).collect::<Vec<_>>(),
 316        expected_partitipants
 317    );
 318}
 319
 320#[track_caller]
 321fn assert_members_eq(
 322    members: &[ChannelMembership],
 323    expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],
 324) {
 325    assert_eq!(
 326        members
 327            .iter()
 328            .map(|member| (member.user.id, member.role, member.kind))
 329            .collect::<Vec<_>>(),
 330        expected_members
 331    );
 332}
 333
 334#[gpui::test]
 335async fn test_joining_channel_ancestor_member(
 336    deterministic: Arc<Deterministic>,
 337    cx_a: &mut TestAppContext,
 338    cx_b: &mut TestAppContext,
 339) {
 340    deterministic.forbid_parking();
 341    let mut server = TestServer::start(&deterministic).await;
 342
 343    let client_a = server.create_client(cx_a, "user_a").await;
 344    let client_b = server.create_client(cx_b, "user_b").await;
 345
 346    let parent_id = server
 347        .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 348        .await;
 349
 350    let sub_id = client_a
 351        .channel_store()
 352        .update(cx_a, |channel_store, cx| {
 353            channel_store.create_channel("sub_channel", Some(parent_id), cx)
 354        })
 355        .await
 356        .unwrap();
 357
 358    let active_call_b = cx_b.read(ActiveCall::global);
 359
 360    assert!(active_call_b
 361        .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
 362        .await
 363        .is_ok());
 364}
 365
 366#[gpui::test]
 367async fn test_channel_room(
 368    deterministic: Arc<Deterministic>,
 369    cx_a: &mut TestAppContext,
 370    cx_b: &mut TestAppContext,
 371    cx_c: &mut TestAppContext,
 372) {
 373    deterministic.forbid_parking();
 374    let mut server = TestServer::start(&deterministic).await;
 375    let client_a = server.create_client(cx_a, "user_a").await;
 376    let client_b = server.create_client(cx_b, "user_b").await;
 377    let client_c = server.create_client(cx_c, "user_c").await;
 378
 379    let zed_id = server
 380        .make_channel(
 381            "zed",
 382            None,
 383            (&client_a, cx_a),
 384            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 385        )
 386        .await;
 387
 388    let active_call_a = cx_a.read(ActiveCall::global);
 389    let active_call_b = cx_b.read(ActiveCall::global);
 390
 391    active_call_a
 392        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 393        .await
 394        .unwrap();
 395
 396    // Give everyone a chance to observe user A joining
 397    deterministic.run_until_parked();
 398    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 399    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 400
 401    client_a.channel_store().read_with(cx_a, |channels, _| {
 402        assert_participants_eq(
 403            channels.channel_participants(zed_id),
 404            &[client_a.user_id().unwrap()],
 405        );
 406    });
 407
 408    assert_channels(
 409        client_b.channel_store(),
 410        cx_b,
 411        &[ExpectedChannel {
 412            id: zed_id,
 413            name: "zed".to_string(),
 414            depth: 0,
 415            user_is_admin: false,
 416        }],
 417    );
 418    client_b.channel_store().read_with(cx_b, |channels, _| {
 419        assert_participants_eq(
 420            channels.channel_participants(zed_id),
 421            &[client_a.user_id().unwrap()],
 422        );
 423    });
 424
 425    client_c.channel_store().read_with(cx_c, |channels, _| {
 426        assert_participants_eq(
 427            channels.channel_participants(zed_id),
 428            &[client_a.user_id().unwrap()],
 429        );
 430    });
 431
 432    active_call_b
 433        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 434        .await
 435        .unwrap();
 436
 437    deterministic.run_until_parked();
 438
 439    client_a.channel_store().read_with(cx_a, |channels, _| {
 440        assert_participants_eq(
 441            channels.channel_participants(zed_id),
 442            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 443        );
 444    });
 445
 446    client_b.channel_store().read_with(cx_b, |channels, _| {
 447        assert_participants_eq(
 448            channels.channel_participants(zed_id),
 449            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 450        );
 451    });
 452
 453    client_c.channel_store().read_with(cx_c, |channels, _| {
 454        assert_participants_eq(
 455            channels.channel_participants(zed_id),
 456            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 457        );
 458    });
 459
 460    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 461    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 462    assert_eq!(
 463        room_participants(&room_a, cx_a),
 464        RoomParticipants {
 465            remote: vec!["user_b".to_string()],
 466            pending: vec![]
 467        }
 468    );
 469
 470    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 471    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 472    assert_eq!(
 473        room_participants(&room_b, cx_b),
 474        RoomParticipants {
 475            remote: vec!["user_a".to_string()],
 476            pending: vec![]
 477        }
 478    );
 479
 480    // Make sure that leaving and rejoining works
 481
 482    active_call_a
 483        .update(cx_a, |active_call, cx| active_call.hang_up(cx))
 484        .await
 485        .unwrap();
 486
 487    deterministic.run_until_parked();
 488
 489    client_a.channel_store().read_with(cx_a, |channels, _| {
 490        assert_participants_eq(
 491            channels.channel_participants(zed_id),
 492            &[client_b.user_id().unwrap()],
 493        );
 494    });
 495
 496    client_b.channel_store().read_with(cx_b, |channels, _| {
 497        assert_participants_eq(
 498            channels.channel_participants(zed_id),
 499            &[client_b.user_id().unwrap()],
 500        );
 501    });
 502
 503    client_c.channel_store().read_with(cx_c, |channels, _| {
 504        assert_participants_eq(
 505            channels.channel_participants(zed_id),
 506            &[client_b.user_id().unwrap()],
 507        );
 508    });
 509
 510    active_call_b
 511        .update(cx_b, |active_call, cx| active_call.hang_up(cx))
 512        .await
 513        .unwrap();
 514
 515    deterministic.run_until_parked();
 516
 517    client_a.channel_store().read_with(cx_a, |channels, _| {
 518        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 519    });
 520
 521    client_b.channel_store().read_with(cx_b, |channels, _| {
 522        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 523    });
 524
 525    client_c.channel_store().read_with(cx_c, |channels, _| {
 526        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 527    });
 528
 529    active_call_a
 530        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 531        .await
 532        .unwrap();
 533
 534    active_call_b
 535        .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
 536        .await
 537        .unwrap();
 538
 539    deterministic.run_until_parked();
 540
 541    let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
 542    room_a.read_with(cx_a, |room, _| assert!(room.is_connected()));
 543    assert_eq!(
 544        room_participants(&room_a, cx_a),
 545        RoomParticipants {
 546            remote: vec!["user_b".to_string()],
 547            pending: vec![]
 548        }
 549    );
 550
 551    let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
 552    room_b.read_with(cx_b, |room, _| assert!(room.is_connected()));
 553    assert_eq!(
 554        room_participants(&room_b, cx_b),
 555        RoomParticipants {
 556            remote: vec!["user_a".to_string()],
 557            pending: vec![]
 558        }
 559    );
 560}
 561
 562#[gpui::test]
 563async fn test_channel_jumping(deterministic: Arc<Deterministic>, cx_a: &mut TestAppContext) {
 564    deterministic.forbid_parking();
 565    let mut server = TestServer::start(&deterministic).await;
 566    let client_a = server.create_client(cx_a, "user_a").await;
 567
 568    let zed_id = server
 569        .make_channel("zed", None, (&client_a, cx_a), &mut [])
 570        .await;
 571    let rust_id = server
 572        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 573        .await;
 574
 575    let active_call_a = cx_a.read(ActiveCall::global);
 576
 577    active_call_a
 578        .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
 579        .await
 580        .unwrap();
 581
 582    // Give everything a chance to observe user A joining
 583    deterministic.run_until_parked();
 584
 585    client_a.channel_store().read_with(cx_a, |channels, _| {
 586        assert_participants_eq(
 587            channels.channel_participants(zed_id),
 588            &[client_a.user_id().unwrap()],
 589        );
 590        assert_participants_eq(channels.channel_participants(rust_id), &[]);
 591    });
 592
 593    active_call_a
 594        .update(cx_a, |active_call, cx| {
 595            active_call.join_channel(rust_id, cx)
 596        })
 597        .await
 598        .unwrap();
 599
 600    deterministic.run_until_parked();
 601
 602    client_a.channel_store().read_with(cx_a, |channels, _| {
 603        assert_participants_eq(channels.channel_participants(zed_id), &[]);
 604        assert_participants_eq(
 605            channels.channel_participants(rust_id),
 606            &[client_a.user_id().unwrap()],
 607        );
 608    });
 609}
 610
 611#[gpui::test]
 612async fn test_permissions_update_while_invited(
 613    deterministic: Arc<Deterministic>,
 614    cx_a: &mut TestAppContext,
 615    cx_b: &mut TestAppContext,
 616) {
 617    deterministic.forbid_parking();
 618    let mut server = TestServer::start(&deterministic).await;
 619    let client_a = server.create_client(cx_a, "user_a").await;
 620    let client_b = server.create_client(cx_b, "user_b").await;
 621
 622    let rust_id = server
 623        .make_channel("rust", None, (&client_a, cx_a), &mut [])
 624        .await;
 625
 626    client_a
 627        .channel_store()
 628        .update(cx_a, |channel_store, cx| {
 629            channel_store.invite_member(
 630                rust_id,
 631                client_b.user_id().unwrap(),
 632                proto::ChannelRole::Member,
 633                cx,
 634            )
 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    // Update B's invite before they've accepted it
 654    client_a
 655        .channel_store()
 656        .update(cx_a, |channel_store, cx| {
 657            channel_store.set_member_role(
 658                rust_id,
 659                client_b.user_id().unwrap(),
 660                proto::ChannelRole::Admin,
 661                cx,
 662            )
 663        })
 664        .await
 665        .unwrap();
 666
 667    deterministic.run_until_parked();
 668
 669    assert_channel_invitations(
 670        client_b.channel_store(),
 671        cx_b,
 672        &[ExpectedChannel {
 673            depth: 0,
 674            id: rust_id,
 675            name: "rust".to_string(),
 676            user_is_admin: false,
 677        }],
 678    );
 679    assert_channels(client_b.channel_store(), cx_b, &[]);
 680}
 681
 682#[gpui::test]
 683async fn test_channel_rename(
 684    deterministic: Arc<Deterministic>,
 685    cx_a: &mut TestAppContext,
 686    cx_b: &mut TestAppContext,
 687) {
 688    deterministic.forbid_parking();
 689    let mut server = TestServer::start(&deterministic).await;
 690    let client_a = server.create_client(cx_a, "user_a").await;
 691    let client_b = server.create_client(cx_b, "user_b").await;
 692
 693    let rust_id = server
 694        .make_channel("rust", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 695        .await;
 696
 697    // Rename the channel
 698    client_a
 699        .channel_store()
 700        .update(cx_a, |channel_store, cx| {
 701            channel_store.rename(rust_id, "#rust-archive", cx)
 702        })
 703        .await
 704        .unwrap();
 705
 706    deterministic.run_until_parked();
 707
 708    // Client A sees the channel with its new name.
 709    assert_channels(
 710        client_a.channel_store(),
 711        cx_a,
 712        &[ExpectedChannel {
 713            depth: 0,
 714            id: rust_id,
 715            name: "rust-archive".to_string(),
 716            user_is_admin: true,
 717        }],
 718    );
 719
 720    // Client B sees the channel with its new name.
 721    assert_channels(
 722        client_b.channel_store(),
 723        cx_b,
 724        &[ExpectedChannel {
 725            depth: 0,
 726            id: rust_id,
 727            name: "rust-archive".to_string(),
 728            user_is_admin: false,
 729        }],
 730    );
 731}
 732
 733#[gpui::test]
 734async fn test_call_from_channel(
 735    deterministic: Arc<Deterministic>,
 736    cx_a: &mut TestAppContext,
 737    cx_b: &mut TestAppContext,
 738    cx_c: &mut TestAppContext,
 739) {
 740    deterministic.forbid_parking();
 741    let mut server = TestServer::start(&deterministic).await;
 742    let client_a = server.create_client(cx_a, "user_a").await;
 743    let client_b = server.create_client(cx_b, "user_b").await;
 744    let client_c = server.create_client(cx_c, "user_c").await;
 745    server
 746        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 747        .await;
 748
 749    let channel_id = server
 750        .make_channel(
 751            "x",
 752            None,
 753            (&client_a, cx_a),
 754            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 755        )
 756        .await;
 757
 758    let active_call_a = cx_a.read(ActiveCall::global);
 759    let active_call_b = cx_b.read(ActiveCall::global);
 760
 761    active_call_a
 762        .update(cx_a, |call, cx| call.join_channel(channel_id, cx))
 763        .await
 764        .unwrap();
 765
 766    // Client A calls client B while in the channel.
 767    active_call_a
 768        .update(cx_a, |call, cx| {
 769            call.invite(client_b.user_id().unwrap(), None, cx)
 770        })
 771        .await
 772        .unwrap();
 773
 774    // Client B accepts the call.
 775    deterministic.run_until_parked();
 776    active_call_b
 777        .update(cx_b, |call, cx| call.accept_incoming(cx))
 778        .await
 779        .unwrap();
 780
 781    // Client B sees that they are now in the channel
 782    deterministic.run_until_parked();
 783    active_call_b.read_with(cx_b, |call, cx| {
 784        assert_eq!(call.channel_id(cx), Some(channel_id));
 785    });
 786    client_b.channel_store().read_with(cx_b, |channels, _| {
 787        assert_participants_eq(
 788            channels.channel_participants(channel_id),
 789            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 790        );
 791    });
 792
 793    // Clients A and C also see that client B is in the channel.
 794    client_a.channel_store().read_with(cx_a, |channels, _| {
 795        assert_participants_eq(
 796            channels.channel_participants(channel_id),
 797            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 798        );
 799    });
 800    client_c.channel_store().read_with(cx_c, |channels, _| {
 801        assert_participants_eq(
 802            channels.channel_participants(channel_id),
 803            &[client_a.user_id().unwrap(), client_b.user_id().unwrap()],
 804        );
 805    });
 806}
 807
 808#[gpui::test]
 809async fn test_lost_channel_creation(
 810    deterministic: Arc<Deterministic>,
 811    cx_a: &mut TestAppContext,
 812    cx_b: &mut TestAppContext,
 813) {
 814    deterministic.forbid_parking();
 815    let mut server = TestServer::start(&deterministic).await;
 816    let client_a = server.create_client(cx_a, "user_a").await;
 817    let client_b = server.create_client(cx_b, "user_b").await;
 818
 819    server
 820        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 821        .await;
 822
 823    let channel_id = server
 824        .make_channel("x", None, (&client_a, cx_a), &mut [])
 825        .await;
 826
 827    // Invite a member
 828    client_a
 829        .channel_store()
 830        .update(cx_a, |channel_store, cx| {
 831            channel_store.invite_member(
 832                channel_id,
 833                client_b.user_id().unwrap(),
 834                proto::ChannelRole::Member,
 835                cx,
 836            )
 837        })
 838        .await
 839        .unwrap();
 840
 841    deterministic.run_until_parked();
 842
 843    // Sanity check, B has the invitation
 844    assert_channel_invitations(
 845        client_b.channel_store(),
 846        cx_b,
 847        &[ExpectedChannel {
 848            depth: 0,
 849            id: channel_id,
 850            name: "x".to_string(),
 851            user_is_admin: false,
 852        }],
 853    );
 854
 855    // A creates a subchannel while the invite is still pending.
 856    let subchannel_id = client_a
 857        .channel_store()
 858        .update(cx_a, |channel_store, cx| {
 859            channel_store.create_channel("subchannel", Some(channel_id), cx)
 860        })
 861        .await
 862        .unwrap();
 863
 864    deterministic.run_until_parked();
 865
 866    // Make sure A sees their new channel
 867    assert_channels(
 868        client_a.channel_store(),
 869        cx_a,
 870        &[
 871            ExpectedChannel {
 872                depth: 0,
 873                id: channel_id,
 874                name: "x".to_string(),
 875                user_is_admin: true,
 876            },
 877            ExpectedChannel {
 878                depth: 1,
 879                id: subchannel_id,
 880                name: "subchannel".to_string(),
 881                user_is_admin: true,
 882            },
 883        ],
 884    );
 885
 886    // Client B accepts the invite
 887    client_b
 888        .channel_store()
 889        .update(cx_b, |channel_store, _| {
 890            channel_store.respond_to_channel_invite(channel_id, true)
 891        })
 892        .await
 893        .unwrap();
 894
 895    deterministic.run_until_parked();
 896
 897    // Client B should now see the channel
 898    assert_channels(
 899        client_b.channel_store(),
 900        cx_b,
 901        &[
 902            ExpectedChannel {
 903                depth: 0,
 904                id: channel_id,
 905                name: "x".to_string(),
 906                user_is_admin: false,
 907            },
 908            ExpectedChannel {
 909                depth: 1,
 910                id: subchannel_id,
 911                name: "subchannel".to_string(),
 912                user_is_admin: false,
 913            },
 914        ],
 915    );
 916}
 917
 918#[gpui::test]
 919async fn test_channel_link_notifications(
 920    deterministic: Arc<Deterministic>,
 921    cx_a: &mut TestAppContext,
 922    cx_b: &mut TestAppContext,
 923    cx_c: &mut TestAppContext,
 924) {
 925    deterministic.forbid_parking();
 926
 927    let mut server = TestServer::start(&deterministic).await;
 928    let client_a = server.create_client(cx_a, "user_a").await;
 929    let client_b = server.create_client(cx_b, "user_b").await;
 930    let client_c = server.create_client(cx_c, "user_c").await;
 931
 932    let user_b = client_b.user_id().unwrap();
 933    let user_c = client_c.user_id().unwrap();
 934
 935    let channels = server
 936        .make_channel_tree(&[("zed", None)], (&client_a, cx_a))
 937        .await;
 938    let zed_channel = channels[0];
 939
 940    try_join_all(client_a.channel_store().update(cx_a, |channel_store, cx| {
 941        [
 942            channel_store.set_channel_visibility(zed_channel, proto::ChannelVisibility::Public, cx),
 943            channel_store.invite_member(zed_channel, user_b, proto::ChannelRole::Member, cx),
 944            channel_store.invite_member(zed_channel, user_c, proto::ChannelRole::Guest, cx),
 945        ]
 946    }))
 947    .await
 948    .unwrap();
 949
 950    deterministic.run_until_parked();
 951
 952    client_b
 953        .channel_store()
 954        .update(cx_b, |channel_store, _| {
 955            channel_store.respond_to_channel_invite(zed_channel, true)
 956        })
 957        .await
 958        .unwrap();
 959
 960    client_c
 961        .channel_store()
 962        .update(cx_c, |channel_store, _| {
 963            channel_store.respond_to_channel_invite(zed_channel, true)
 964        })
 965        .await
 966        .unwrap();
 967
 968    deterministic.run_until_parked();
 969
 970    // we have an admin (a), member (b) and guest (c) all part of the zed channel.
 971
 972    // create a new private sub-channel
 973    // create a new priate channel, make it public, and move it under the previous one, and verify it shows for b and c
 974    let active_channel = client_a
 975        .channel_store()
 976        .update(cx_a, |channel_store, cx| {
 977            channel_store.create_channel("active", Some(zed_channel), cx)
 978        })
 979        .await
 980        .unwrap();
 981
 982    // the new channel shows for b and not c
 983    assert_channels_list_shape(
 984        client_a.channel_store(),
 985        cx_a,
 986        &[(zed_channel, 0), (active_channel, 1)],
 987    );
 988    assert_channels_list_shape(
 989        client_b.channel_store(),
 990        cx_b,
 991        &[(zed_channel, 0), (active_channel, 1)],
 992    );
 993    assert_channels_list_shape(client_c.channel_store(), cx_c, &[(zed_channel, 0)]);
 994
 995    let vim_channel = client_a
 996        .channel_store()
 997        .update(cx_a, |channel_store, cx| {
 998            channel_store.create_channel("vim", None, cx)
 999        })
1000        .await
1001        .unwrap();
1002
1003    client_a
1004        .channel_store()
1005        .update(cx_a, |channel_store, cx| {
1006            channel_store.set_channel_visibility(vim_channel, proto::ChannelVisibility::Public, cx)
1007        })
1008        .await
1009        .unwrap();
1010
1011    client_a
1012        .channel_store()
1013        .update(cx_a, |channel_store, cx| {
1014            channel_store.link_channel(vim_channel, active_channel, cx)
1015        })
1016        .await
1017        .unwrap();
1018
1019    deterministic.run_until_parked();
1020
1021    // the new channel shows for b and c
1022    assert_channels_list_shape(
1023        client_a.channel_store(),
1024        cx_a,
1025        &[(zed_channel, 0), (active_channel, 1), (vim_channel, 2)],
1026    );
1027    assert_channels_list_shape(
1028        client_b.channel_store(),
1029        cx_b,
1030        &[(zed_channel, 0), (active_channel, 1), (vim_channel, 2)],
1031    );
1032    assert_channels_list_shape(
1033        client_c.channel_store(),
1034        cx_c,
1035        &[(zed_channel, 0), (vim_channel, 1)],
1036    );
1037
1038    let helix_channel = client_a
1039        .channel_store()
1040        .update(cx_a, |channel_store, cx| {
1041            channel_store.create_channel("helix", None, cx)
1042        })
1043        .await
1044        .unwrap();
1045
1046    client_a
1047        .channel_store()
1048        .update(cx_a, |channel_store, cx| {
1049            channel_store.link_channel(helix_channel, vim_channel, cx)
1050        })
1051        .await
1052        .unwrap();
1053
1054    client_a
1055        .channel_store()
1056        .update(cx_a, |channel_store, cx| {
1057            channel_store.set_channel_visibility(
1058                helix_channel,
1059                proto::ChannelVisibility::Public,
1060                cx,
1061            )
1062        })
1063        .await
1064        .unwrap();
1065
1066    // the new channel shows for b and c
1067    assert_channels_list_shape(
1068        client_b.channel_store(),
1069        cx_b,
1070        &[
1071            (zed_channel, 0),
1072            (active_channel, 1),
1073            (vim_channel, 2),
1074            (helix_channel, 3),
1075        ],
1076    );
1077    assert_channels_list_shape(
1078        client_c.channel_store(),
1079        cx_c,
1080        &[(zed_channel, 0), (vim_channel, 1), (helix_channel, 2)],
1081    );
1082
1083    client_a
1084        .channel_store()
1085        .update(cx_a, |channel_store, cx| {
1086            channel_store.set_channel_visibility(vim_channel, proto::ChannelVisibility::Members, cx)
1087        })
1088        .await
1089        .unwrap();
1090
1091    // the members-only channel is still shown for c, but hidden for b
1092    assert_channels_list_shape(
1093        client_b.channel_store(),
1094        cx_b,
1095        &[
1096            (zed_channel, 0),
1097            (active_channel, 1),
1098            (vim_channel, 2),
1099            (helix_channel, 3),
1100        ],
1101    );
1102    client_b
1103        .channel_store()
1104        .read_with(cx_b, |channel_store, _| {
1105            assert_eq!(
1106                channel_store
1107                    .channel_for_id(vim_channel)
1108                    .unwrap()
1109                    .visibility,
1110                proto::ChannelVisibility::Members
1111            )
1112        });
1113
1114    assert_channels_list_shape(
1115        client_c.channel_store(),
1116        cx_c,
1117        &[(zed_channel, 0), (helix_channel, 1)],
1118    );
1119}
1120
1121#[gpui::test]
1122async fn test_guest_access(
1123    deterministic: Arc<Deterministic>,
1124    cx_a: &mut TestAppContext,
1125    cx_b: &mut TestAppContext,
1126) {
1127    deterministic.forbid_parking();
1128
1129    let mut server = TestServer::start(&deterministic).await;
1130    let client_a = server.create_client(cx_a, "user_a").await;
1131    let client_b = server.create_client(cx_b, "user_b").await;
1132
1133    let channels = server
1134        .make_channel_tree(&[("channel-a", None)], (&client_a, cx_a))
1135        .await;
1136    let channel_a_id = channels[0];
1137
1138    let active_call_b = cx_b.read(ActiveCall::global);
1139
1140    // should not be allowed to join
1141    assert!(active_call_b
1142        .update(cx_b, |call, cx| call.join_channel(channel_a_id, cx))
1143        .await
1144        .is_err());
1145
1146    client_a
1147        .channel_store()
1148        .update(cx_a, |channel_store, cx| {
1149            channel_store.set_channel_visibility(channel_a_id, proto::ChannelVisibility::Public, cx)
1150        })
1151        .await
1152        .unwrap();
1153
1154    active_call_b
1155        .update(cx_b, |call, cx| call.join_channel(channel_a_id, cx))
1156        .await
1157        .unwrap();
1158
1159    deterministic.run_until_parked();
1160
1161    assert!(client_b
1162        .channel_store()
1163        .update(cx_b, |channel_store, _| channel_store
1164            .channel_for_id(channel_a_id)
1165            .is_some()));
1166
1167    client_a.channel_store().update(cx_a, |channel_store, _| {
1168        let participants = channel_store.channel_participants(channel_a_id);
1169        assert_eq!(participants.len(), 1);
1170        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1171    })
1172}
1173
1174#[gpui::test]
1175async fn test_invite_access(
1176    deterministic: Arc<Deterministic>,
1177    cx_a: &mut TestAppContext,
1178    cx_b: &mut TestAppContext,
1179) {
1180    deterministic.forbid_parking();
1181
1182    let mut server = TestServer::start(&deterministic).await;
1183    let client_a = server.create_client(cx_a, "user_a").await;
1184    let client_b = server.create_client(cx_b, "user_b").await;
1185
1186    let channels = server
1187        .make_channel_tree(
1188            &[("channel-a", None), ("channel-b", Some("channel-a"))],
1189            (&client_a, cx_a),
1190        )
1191        .await;
1192    let channel_a_id = channels[0];
1193    let channel_b_id = channels[0];
1194
1195    let active_call_b = cx_b.read(ActiveCall::global);
1196
1197    // should not be allowed to join
1198    assert!(active_call_b
1199        .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1200        .await
1201        .is_err());
1202
1203    client_a
1204        .channel_store()
1205        .update(cx_a, |channel_store, cx| {
1206            channel_store.invite_member(
1207                channel_a_id,
1208                client_b.user_id().unwrap(),
1209                ChannelRole::Member,
1210                cx,
1211            )
1212        })
1213        .await
1214        .unwrap();
1215
1216    active_call_b
1217        .update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
1218        .await
1219        .unwrap();
1220
1221    deterministic.run_until_parked();
1222
1223    client_b.channel_store().update(cx_b, |channel_store, _| {
1224        assert!(channel_store.channel_for_id(channel_b_id).is_some());
1225        assert!(channel_store.channel_for_id(channel_a_id).is_some());
1226    });
1227
1228    client_a.channel_store().update(cx_a, |channel_store, _| {
1229        let participants = channel_store.channel_participants(channel_b_id);
1230        assert_eq!(participants.len(), 1);
1231        assert_eq!(participants[0].id, client_b.user_id().unwrap());
1232    })
1233}
1234
1235#[gpui::test]
1236async fn test_channel_moving(
1237    deterministic: Arc<Deterministic>,
1238    cx_a: &mut TestAppContext,
1239    _cx_b: &mut TestAppContext,
1240    _cx_c: &mut TestAppContext,
1241) {
1242    deterministic.forbid_parking();
1243    let mut server = TestServer::start(&deterministic).await;
1244    let client_a = server.create_client(cx_a, "user_a").await;
1245    // let client_b = server.create_client(cx_b, "user_b").await;
1246    // let client_c = server.create_client(cx_c, "user_c").await;
1247
1248    let channels = server
1249        .make_channel_tree(
1250            &[
1251                ("channel-a", None),
1252                ("channel-b", Some("channel-a")),
1253                ("channel-c", Some("channel-b")),
1254                ("channel-d", Some("channel-c")),
1255            ],
1256            (&client_a, cx_a),
1257        )
1258        .await;
1259    let channel_a_id = channels[0];
1260    let channel_b_id = channels[1];
1261    let channel_c_id = channels[2];
1262    let channel_d_id = channels[3];
1263
1264    // Current shape:
1265    // a - b - c - d
1266    assert_channels_list_shape(
1267        client_a.channel_store(),
1268        cx_a,
1269        &[
1270            (channel_a_id, 0),
1271            (channel_b_id, 1),
1272            (channel_c_id, 2),
1273            (channel_d_id, 3),
1274        ],
1275    );
1276
1277    client_a
1278        .channel_store()
1279        .update(cx_a, |channel_store, cx| {
1280            channel_store.move_channel(channel_d_id, channel_c_id, channel_b_id, cx)
1281        })
1282        .await
1283        .unwrap();
1284
1285    // Current shape:
1286    //       /- d
1287    // a - b -- c
1288    assert_channels_list_shape(
1289        client_a.channel_store(),
1290        cx_a,
1291        &[
1292            (channel_a_id, 0),
1293            (channel_b_id, 1),
1294            (channel_c_id, 2),
1295            (channel_d_id, 2),
1296        ],
1297    );
1298
1299    // TODO: Restore this test once we have a way to make channel symlinks
1300    // client_a
1301    //     .channel_store()
1302    //     .update(cx_a, |channel_store, cx| {
1303    //         channel_store.link_channel(channel_d_id, channel_c_id, cx)
1304    //     })
1305    //     .await
1306    //     .unwrap();
1307
1308    // // Current shape for A:
1309    // //      /------\
1310    // // a - b -- c -- d
1311    // assert_channels_list_shape(
1312    //     client_a.channel_store(),
1313    //     cx_a,
1314    //     &[
1315    //         (channel_a_id, 0),
1316    //         (channel_b_id, 1),
1317    //         (channel_c_id, 2),
1318    //         (channel_d_id, 3),
1319    //         (channel_d_id, 2),
1320    //     ],
1321    // );
1322    //
1323    // let b_channels = server
1324    //     .make_channel_tree(
1325    //         &[
1326    //             ("channel-mu", None),
1327    //             ("channel-gamma", Some("channel-mu")),
1328    //             ("channel-epsilon", Some("channel-mu")),
1329    //         ],
1330    //         (&client_b, cx_b),
1331    //     )
1332    //     .await;
1333    // let channel_mu_id = b_channels[0];
1334    // let channel_ga_id = b_channels[1];
1335    // let channel_ep_id = b_channels[2];
1336
1337    // // Current shape for B:
1338    // //    /- ep
1339    // // mu -- ga
1340    // assert_channels_list_shape(
1341    //     client_b.channel_store(),
1342    //     cx_b,
1343    //     &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)],
1344    // );
1345
1346    // client_a
1347    //     .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a)
1348    //     .await;
1349
1350    // // Current shape for B:
1351    // //    /- ep
1352    // // mu -- ga
1353    // //  /---------\
1354    // // b  -- c  -- d
1355    // assert_channels_list_shape(
1356    //     client_b.channel_store(),
1357    //     cx_b,
1358    //     &[
1359    //         // New channels from a
1360    //         (channel_b_id, 0),
1361    //         (channel_c_id, 1),
1362    //         (channel_d_id, 2),
1363    //         (channel_d_id, 1),
1364    //         // B's old channels
1365    //         (channel_mu_id, 0),
1366    //         (channel_ep_id, 1),
1367    //         (channel_ga_id, 1),
1368    //     ],
1369    // );
1370
1371    // client_b
1372    //     .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b)
1373    //     .await;
1374
1375    // // Current shape for C:
1376    // // - ep
1377    // assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]);
1378
1379    // client_b
1380    //     .channel_store()
1381    //     .update(cx_b, |channel_store, cx| {
1382    //         channel_store.link_channel(channel_b_id, channel_ep_id, cx)
1383    //     })
1384    //     .await
1385    //     .unwrap();
1386
1387    // // Current shape for B:
1388    // //              /---------\
1389    // //    /- ep -- b  -- c  -- d
1390    // // mu -- ga
1391    // assert_channels_list_shape(
1392    //     client_b.channel_store(),
1393    //     cx_b,
1394    //     &[
1395    //         (channel_mu_id, 0),
1396    //         (channel_ep_id, 1),
1397    //         (channel_b_id, 2),
1398    //         (channel_c_id, 3),
1399    //         (channel_d_id, 4),
1400    //         (channel_d_id, 3),
1401    //         (channel_ga_id, 1),
1402    //     ],
1403    // );
1404
1405    // // Current shape for C:
1406    // //        /---------\
1407    // // ep -- b  -- c  -- d
1408    // assert_channels_list_shape(
1409    //     client_c.channel_store(),
1410    //     cx_c,
1411    //     &[
1412    //         (channel_ep_id, 0),
1413    //         (channel_b_id, 1),
1414    //         (channel_c_id, 2),
1415    //         (channel_d_id, 3),
1416    //         (channel_d_id, 2),
1417    //     ],
1418    // );
1419
1420    // client_b
1421    //     .channel_store()
1422    //     .update(cx_b, |channel_store, cx| {
1423    //         channel_store.link_channel(channel_ga_id, channel_b_id, cx)
1424    //     })
1425    //     .await
1426    //     .unwrap();
1427
1428    // // Current shape for B:
1429    // //              /---------\
1430    // //    /- ep -- b  -- c  -- d
1431    // //   /          \
1432    // // mu ---------- ga
1433    // assert_channels_list_shape(
1434    //     client_b.channel_store(),
1435    //     cx_b,
1436    //     &[
1437    //         (channel_mu_id, 0),
1438    //         (channel_ep_id, 1),
1439    //         (channel_b_id, 2),
1440    //         (channel_c_id, 3),
1441    //         (channel_d_id, 4),
1442    //         (channel_d_id, 3),
1443    //         (channel_ga_id, 3),
1444    //         (channel_ga_id, 1),
1445    //     ],
1446    // );
1447
1448    // // Current shape for A:
1449    // //      /------\
1450    // // a - b -- c -- d
1451    // //      \-- ga
1452    // assert_channels_list_shape(
1453    //     client_a.channel_store(),
1454    //     cx_a,
1455    //     &[
1456    //         (channel_a_id, 0),
1457    //         (channel_b_id, 1),
1458    //         (channel_c_id, 2),
1459    //         (channel_d_id, 3),
1460    //         (channel_d_id, 2),
1461    //         (channel_ga_id, 2),
1462    //     ],
1463    // );
1464
1465    // // Current shape for C:
1466    // //        /-------\
1467    // // ep -- b -- c -- d
1468    // //        \-- ga
1469    // assert_channels_list_shape(
1470    //     client_c.channel_store(),
1471    //     cx_c,
1472    //     &[
1473    //         (channel_ep_id, 0),
1474    //         (channel_b_id, 1),
1475    //         (channel_c_id, 2),
1476    //         (channel_d_id, 3),
1477    //         (channel_d_id, 2),
1478    //         (channel_ga_id, 2),
1479    //     ],
1480    // );
1481}
1482
1483#[derive(Debug, PartialEq)]
1484struct ExpectedChannel {
1485    depth: usize,
1486    id: ChannelId,
1487    name: String,
1488    user_is_admin: bool,
1489}
1490
1491#[track_caller]
1492fn assert_channel_invitations(
1493    channel_store: &ModelHandle<ChannelStore>,
1494    cx: &TestAppContext,
1495    expected_channels: &[ExpectedChannel],
1496) {
1497    let actual = channel_store.read_with(cx, |store, _| {
1498        store
1499            .channel_invitations()
1500            .iter()
1501            .map(|channel| ExpectedChannel {
1502                depth: 0,
1503                name: channel.name.clone(),
1504                id: channel.id,
1505                user_is_admin: store.is_channel_admin(channel.id),
1506            })
1507            .collect::<Vec<_>>()
1508    });
1509    assert_eq!(actual, expected_channels);
1510}
1511
1512#[track_caller]
1513fn assert_channels(
1514    channel_store: &ModelHandle<ChannelStore>,
1515    cx: &TestAppContext,
1516    expected_channels: &[ExpectedChannel],
1517) {
1518    let actual = channel_store.read_with(cx, |store, _| {
1519        store
1520            .channel_dag_entries()
1521            .map(|(depth, channel)| ExpectedChannel {
1522                depth,
1523                name: channel.name.clone(),
1524                id: channel.id,
1525                user_is_admin: store.is_channel_admin(channel.id),
1526            })
1527            .collect::<Vec<_>>()
1528    });
1529    pretty_assertions::assert_eq!(actual, expected_channels);
1530}
1531
1532#[track_caller]
1533fn assert_channels_list_shape(
1534    channel_store: &ModelHandle<ChannelStore>,
1535    cx: &TestAppContext,
1536    expected_channels: &[(u64, usize)],
1537) {
1538    cx.foreground().run_until_parked();
1539
1540    let actual = channel_store.read_with(cx, |store, _| {
1541        store
1542            .channel_dag_entries()
1543            .map(|(depth, channel)| (channel.id, depth))
1544            .collect::<Vec<_>>()
1545    });
1546    pretty_assertions::assert_eq!(actual, expected_channels);
1547}