use crate::{ db::{self, UserId}, rpc::RECONNECT_TIMEOUT, tests::{room_participants, RoomParticipants, TestServer},};use call::ActiveCall;use channel::{ChannelMembership, ChannelStore};use client::{ChannelId, User};use futures::future::try_join_all;use gpui::{BackgroundExecutor, Entity, SharedString, TestAppContext};use rpc::{ proto::{self, ChannelRole}, RECEIVE_TIMEOUT,};use std::sync::Arc; #[gpui::test]async fn test_core_channels( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext,) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let channel_a_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-a", None, cx) }) .await .unwrap(); let channel_b_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-b", Some(channel_a_id), cx) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_a.channel_store(), cx_a, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ], ); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert!(channels.ordered_channels().collect::<Vec<_>>().is_empty()) }) }); // Invite client B to channel A as client A. client_a .channel_store() .update(cx_a, |store, cx| { assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); let invite = store.invite_member( channel_a_id, client_b.user_id().unwrap(), proto::ChannelRole::Member, cx, ); // Make sure we're synchronously storing the pending invite assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); invite }) .await .unwrap(); // Client A sees that B has been invited. executor.run_until_parked(); assert_channel_invitations( client_b.channel_store(), cx_b, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); let members = client_a .channel_store() .update(cx_a, |store, cx| { assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); store.fuzzy_search_members(channel_a_id, "".to_string(), 10, cx) }) .await .unwrap(); assert_members_eq( &members, &[ ( client_a.user_id().unwrap(), proto::ChannelRole::Admin, proto::channel_member::Kind::Member, ), ( client_b.user_id().unwrap(), proto::ChannelRole::Member, proto::channel_member::Kind::Invitee, ), ], ); // Client B accepts the invitation. client_b .channel_store() .update(cx_b, |channels, cx| { channels.respond_to_channel_invite(channel_a_id, true, cx) }) .await .unwrap(); executor.run_until_parked(); // Client B now sees that they are a member of channel A and its existing subchannels. assert_channel_invitations(client_b.channel_store(), cx_b, &[]); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ], ); let channel_c_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-c", Some(channel_b_id), cx) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ExpectedChannel { id: channel_c_id, name: "channel-c".into(), depth: 2, }, ], ); // Update client B's membership to channel A to be an admin. client_a .channel_store() .update(cx_a, |store, cx| { store.set_member_role( channel_a_id, client_b.user_id().unwrap(), proto::ChannelRole::Admin, cx, ) }) .await .unwrap(); executor.run_until_parked(); // Observe that client B is now an admin of channel A, and that // their admin privileges extend to subchannels of channel A. assert_channel_invitations(client_b.channel_store(), cx_b, &[]); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ExpectedChannel { id: channel_c_id, name: "channel-c".into(), depth: 2, }, ], ); // Client A deletes the channel, deletion also deletes subchannels. client_a .channel_store() .update(cx_a, |channel_store, _| { channel_store.remove_channel(channel_b_id) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); assert_channels( client_b.channel_store(), cx_b, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); // Remove client B client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx) }) .await .unwrap(); executor.run_until_parked(); // Client A still has their channel assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); // Client B no longer has access to the channel assert_channels(client_b.channel_store(), cx_b, &[]); server.forbid_connections(); server.disconnect_client(client_a.peer_id().unwrap()); executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); server .app_state .db .rename_channel( db::ChannelId::from_proto(channel_a_id.0), UserId::from_proto(client_a.id()), "channel-a-renamed", ) .await .unwrap(); server.allow_connections(); executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a-renamed".into(), depth: 0, }], );} #[track_caller]fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) { assert_eq!( participants.iter().map(|p| p.id).collect::<Vec<_>>(), expected_partitipants );} #[track_caller]fn assert_members_eq( members: &[ChannelMembership], expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],) { assert_eq!( members .iter() .map(|member| (member.user.id, member.role, member.kind)) .collect::<Vec<_>>(), expected_members );} #[gpui::test]async fn test_joining_channel_ancestor_member( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext,) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let parent_id = server .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)]) .await; let sub_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("sub_channel", Some(parent_id), cx) }) .await .unwrap(); let active_call_b = cx_b.read(ActiveCall::global); assert!(active_call_b .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx)) .await .is_ok());} #[gpui::test]async fn test_channel_room( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, cx_c: &mut TestAppContext,) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; let zed_id = server .make_channel( "zed", None, (&client_a, cx_a), &mut [(&client_b, cx_b), (&client_c, cx_c)], ) .await; let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); active_call_a .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx)) .await .unwrap(); // Give everyone a chance to observe user A joining executor.run_until_parked(); let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); assert_channels( client_b.channel_store(), cx_b, &[ExpectedChannel { id: zed_id, name: "zed".into(), depth: 0, }], ); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); cx_c.read(|cx| { client_c.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); active_call_b .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx)) .await .unwrap(); executor.run_until_parked(); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); cx_c.read(|cx| { client_c.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: vec![] } ); let room_b = cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone())); cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected()))); assert_eq!( room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: vec![] } ); // Make sure that leaving and rejoining works active_call_a .update(cx_a, |active_call, cx| active_call.hang_up(cx)) .await .unwrap(); executor.run_until_parked(); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_b.user_id().unwrap()], ); }) });Lines 1–500 of 1460. Load more
use crate::{ db::{self, UserId}, rpc::RECONNECT_TIMEOUT, tests::{room_participants, RoomParticipants, TestServer}, }; use call::ActiveCall; use channel::{ChannelMembership, ChannelStore}; use client::{ChannelId, User}; use futures::future::try_join_all; use gpui::{BackgroundExecutor, Entity, SharedString, TestAppContext}; use rpc::{ proto::{self, ChannelRole}, RECEIVE_TIMEOUT, }; use std::sync::Arc; #[gpui::test] async fn test_core_channels( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let channel_a_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-a", None, cx) }) .await .unwrap(); let channel_b_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-b", Some(channel_a_id), cx) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_a.channel_store(), cx_a, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ], ); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert!(channels.ordered_channels().collect::<Vec<_>>().is_empty()) }) }); // Invite client B to channel A as client A. client_a .channel_store() .update(cx_a, |store, cx| { assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); let invite = store.invite_member( channel_a_id, client_b.user_id().unwrap(), proto::ChannelRole::Member, cx, ); // Make sure we're synchronously storing the pending invite assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); invite }) .await .unwrap(); // Client A sees that B has been invited. executor.run_until_parked(); assert_channel_invitations( client_b.channel_store(), cx_b, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); let members = client_a .channel_store() .update(cx_a, |store, cx| { assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); store.fuzzy_search_members(channel_a_id, "".to_string(), 10, cx) }) .await .unwrap(); assert_members_eq( &members, &[ ( client_a.user_id().unwrap(), proto::ChannelRole::Admin, proto::channel_member::Kind::Member, ), ( client_b.user_id().unwrap(), proto::ChannelRole::Member, proto::channel_member::Kind::Invitee, ), ], ); // Client B accepts the invitation. client_b .channel_store() .update(cx_b, |channels, cx| { channels.respond_to_channel_invite(channel_a_id, true, cx) }) .await .unwrap(); executor.run_until_parked(); // Client B now sees that they are a member of channel A and its existing subchannels. assert_channel_invitations(client_b.channel_store(), cx_b, &[]); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ], ); let channel_c_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("channel-c", Some(channel_b_id), cx) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ExpectedChannel { id: channel_c_id, name: "channel-c".into(), depth: 2, }, ], ); // Update client B's membership to channel A to be an admin. client_a .channel_store() .update(cx_a, |store, cx| { store.set_member_role( channel_a_id, client_b.user_id().unwrap(), proto::ChannelRole::Admin, cx, ) }) .await .unwrap(); executor.run_until_parked(); // Observe that client B is now an admin of channel A, and that // their admin privileges extend to subchannels of channel A. assert_channel_invitations(client_b.channel_store(), cx_b, &[]); assert_channels( client_b.channel_store(), cx_b, &[ ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }, ExpectedChannel { id: channel_b_id, name: "channel-b".into(), depth: 1, }, ExpectedChannel { id: channel_c_id, name: "channel-c".into(), depth: 2, }, ], ); // Client A deletes the channel, deletion also deletes subchannels. client_a .channel_store() .update(cx_a, |channel_store, _| { channel_store.remove_channel(channel_b_id) }) .await .unwrap(); executor.run_until_parked(); assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); assert_channels( client_b.channel_store(), cx_b, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); // Remove client B client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.remove_member(channel_a_id, client_b.user_id().unwrap(), cx) }) .await .unwrap(); executor.run_until_parked(); // Client A still has their channel assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a".into(), depth: 0, }], ); // Client B no longer has access to the channel assert_channels(client_b.channel_store(), cx_b, &[]); server.forbid_connections(); server.disconnect_client(client_a.peer_id().unwrap()); executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); server .app_state .db .rename_channel( db::ChannelId::from_proto(channel_a_id.0), UserId::from_proto(client_a.id()), "channel-a-renamed", ) .await .unwrap(); server.allow_connections(); executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); assert_channels( client_a.channel_store(), cx_a, &[ExpectedChannel { id: channel_a_id, name: "channel-a-renamed".into(), depth: 0, }], ); } #[track_caller] fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u64]) { assert_eq!( participants.iter().map(|p| p.id).collect::<Vec<_>>(), expected_partitipants ); } #[track_caller] fn assert_members_eq( members: &[ChannelMembership], expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)], ) { assert_eq!( members .iter() .map(|member| (member.user.id, member.role, member.kind)) .collect::<Vec<_>>(), expected_members ); } #[gpui::test] async fn test_joining_channel_ancestor_member( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let parent_id = server .make_channel("parent", None, (&client_a, cx_a), &mut [(&client_b, cx_b)]) .await; let sub_id = client_a .channel_store() .update(cx_a, |channel_store, cx| { channel_store.create_channel("sub_channel", Some(parent_id), cx) }) .await .unwrap(); let active_call_b = cx_b.read(ActiveCall::global); assert!(active_call_b .update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx)) .await .is_ok()); } #[gpui::test] async fn test_channel_room( executor: BackgroundExecutor, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, cx_c: &mut TestAppContext, ) { let mut server = TestServer::start(executor.clone()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; let zed_id = server .make_channel( "zed", None, (&client_a, cx_a), &mut [(&client_b, cx_b), (&client_c, cx_c)], ) .await; let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); active_call_a .update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx)) .await .unwrap(); // Give everyone a chance to observe user A joining executor.run_until_parked(); let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); assert_channels( client_b.channel_store(), cx_b, &[ExpectedChannel { id: zed_id, name: "zed".into(), depth: 0, }], ); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); cx_c.read(|cx| { client_c.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap()], ); }) }); active_call_b .update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx)) .await .unwrap(); executor.run_until_parked(); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); cx_b.read(|cx| { client_b.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); cx_c.read(|cx| { client_c.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_a.user_id().unwrap(), client_b.user_id().unwrap()], ); }) }); let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: vec![] } ); let room_b = cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone())); cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected()))); assert_eq!( room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: vec![] } ); // Make sure that leaving and rejoining works active_call_a .update(cx_a, |active_call, cx| active_call.hang_up(cx)) .await .unwrap(); executor.run_until_parked(); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { assert_participants_eq( channels.channel_participants(zed_id), &[client_b.user_id().unwrap()], ); }) });