
This PR restricts usage of the LLM service to accounts older than 30 days. We now store the GitHub user's `created_at` timestamp to check the GitHub account age. If this is not set—which it won't be for existing users—then we use the `created_at` timestamp in the Zed database. Release Notes: - N/A --------- Co-authored-by: Max <max@zed.dev>
289 lines
9.6 KiB
Rust
289 lines
9.6 KiB
Rust
use crate::{db::ChannelId, tests::TestServer};
|
|
use call::ActiveCall;
|
|
use chrono::Utc;
|
|
use editor::Editor;
|
|
use gpui::{BackgroundExecutor, TestAppContext};
|
|
use rpc::proto;
|
|
|
|
#[gpui::test]
|
|
async fn test_channel_guests(
|
|
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 active_call_a = cx_a.read(ActiveCall::global);
|
|
|
|
let channel_id = server
|
|
.make_public_channel("the-channel", &client_a, cx_a)
|
|
.await;
|
|
|
|
// Client A shares a project in the channel
|
|
let project_a = client_a.build_test_project(cx_a).await;
|
|
active_call_a
|
|
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
|
|
.await
|
|
.unwrap();
|
|
let project_id = active_call_a
|
|
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
|
.await
|
|
.unwrap();
|
|
cx_a.executor().run_until_parked();
|
|
|
|
// Client B joins channel A as a guest
|
|
cx_b.update(|cx| workspace::join_channel(channel_id, client_b.app_state.clone(), None, cx))
|
|
.await
|
|
.unwrap();
|
|
|
|
// b should be following a in the shared project.
|
|
// B is a guest,
|
|
executor.run_until_parked();
|
|
|
|
let active_call_b = cx_b.read(ActiveCall::global);
|
|
let project_b =
|
|
active_call_b.read_with(cx_b, |call, _| call.location().unwrap().upgrade().unwrap());
|
|
let room_b = active_call_b.update(cx_b, |call, _| call.room().unwrap().clone());
|
|
|
|
assert_eq!(
|
|
project_b.read_with(cx_b, |project, _| project.remote_id()),
|
|
Some(project_id),
|
|
);
|
|
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
|
|
assert!(project_b
|
|
.update(cx_b, |project, cx| {
|
|
let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
|
|
project.create_entry((worktree_id, "b.txt"), false, cx)
|
|
})
|
|
.await
|
|
.is_err());
|
|
assert!(room_b.read_with(cx_b, |room, _| room.is_muted()));
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_channel_guest_promotion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
|
let mut server = TestServer::start(cx_a.executor()).await;
|
|
let client_a = server.create_client(cx_a, "user_a").await;
|
|
let client_b = server.create_client(cx_b, "user_b").await;
|
|
let active_call_a = cx_a.read(ActiveCall::global);
|
|
|
|
let channel_id = server
|
|
.make_public_channel("the-channel", &client_a, cx_a)
|
|
.await;
|
|
|
|
let project_a = client_a.build_test_project(cx_a).await;
|
|
cx_a.update(|cx| workspace::join_channel(channel_id, client_a.app_state.clone(), None, cx))
|
|
.await
|
|
.unwrap();
|
|
|
|
// Client A shares a project in the channel
|
|
active_call_a
|
|
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
|
|
// Client B joins channel A as a guest
|
|
cx_b.update(|cx| workspace::join_channel(channel_id, client_b.app_state.clone(), None, cx))
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
|
|
// client B opens 1.txt as a guest
|
|
let (workspace_b, cx_b) = client_b.active_workspace(cx_b);
|
|
let room_b = cx_b
|
|
.read(ActiveCall::global)
|
|
.update(cx_b, |call, _| call.room().unwrap().clone());
|
|
cx_b.simulate_keystrokes("cmd-p 1 enter");
|
|
|
|
let (project_b, editor_b) = workspace_b.update(cx_b, |workspace, cx| {
|
|
(
|
|
workspace.project().clone(),
|
|
workspace.active_item_as::<Editor>(cx).unwrap(),
|
|
)
|
|
});
|
|
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
|
|
assert!(editor_b.update(cx_b, |e, cx| e.read_only(cx)));
|
|
assert!(room_b.read_with(cx_b, |room, _| !room.can_use_microphone()));
|
|
assert!(room_b
|
|
.update(cx_b, |room, cx| room.share_microphone(cx))
|
|
.await
|
|
.is_err());
|
|
|
|
// B is promoted
|
|
active_call_a
|
|
.update(cx_a, |call, cx| {
|
|
call.room().unwrap().update(cx, |room, cx| {
|
|
room.set_participant_role(
|
|
client_b.user_id().unwrap(),
|
|
proto::ChannelRole::Member,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
|
|
// project and buffers are now editable
|
|
assert!(project_b.read_with(cx_b, |project, _| !project.is_read_only()));
|
|
assert!(editor_b.update(cx_b, |editor, cx| !editor.read_only(cx)));
|
|
|
|
// B sees themselves as muted, and can unmute.
|
|
assert!(room_b.read_with(cx_b, |room, _| room.can_use_microphone()));
|
|
room_b.read_with(cx_b, |room, _| assert!(room.is_muted()));
|
|
room_b.update(cx_b, |room, cx| room.toggle_mute(cx));
|
|
cx_a.run_until_parked();
|
|
room_b.read_with(cx_b, |room, _| assert!(!room.is_muted()));
|
|
|
|
// B is demoted
|
|
active_call_a
|
|
.update(cx_a, |call, cx| {
|
|
call.room().unwrap().update(cx, |room, cx| {
|
|
room.set_participant_role(
|
|
client_b.user_id().unwrap(),
|
|
proto::ChannelRole::Guest,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
|
|
// project and buffers are no longer editable
|
|
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
|
|
assert!(editor_b.update(cx_b, |editor, cx| editor.read_only(cx)));
|
|
assert!(room_b
|
|
.update(cx_b, |room, cx| room.share_microphone(cx))
|
|
.await
|
|
.is_err());
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_channel_requires_zed_cla(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
|
let mut server = TestServer::start(cx_a.executor()).await;
|
|
|
|
server
|
|
.app_state
|
|
.db
|
|
.get_or_create_user_by_github_account("user_b", Some(100), None, Some(Utc::now()), None)
|
|
.await
|
|
.unwrap();
|
|
|
|
let client_a = server.create_client(cx_a, "user_a").await;
|
|
let client_b = server.create_client(cx_b, "user_b").await;
|
|
let active_call_a = cx_a.read(ActiveCall::global);
|
|
let active_call_b = cx_b.read(ActiveCall::global);
|
|
|
|
// Create a parent channel that requires the Zed CLA
|
|
let parent_channel_id = server
|
|
.make_channel("the-channel", None, (&client_a, cx_a), &mut [])
|
|
.await;
|
|
server
|
|
.app_state
|
|
.db
|
|
.set_channel_requires_zed_cla(ChannelId::from_proto(parent_channel_id.0), true)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create a public channel that is a child of the parent channel.
|
|
let channel_id = client_a
|
|
.channel_store()
|
|
.update(cx_a, |store, cx| {
|
|
store.create_channel("the-sub-channel", Some(parent_channel_id), cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
client_a
|
|
.channel_store()
|
|
.update(cx_a, |store, cx| {
|
|
store.set_channel_visibility(parent_channel_id, proto::ChannelVisibility::Public, cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
client_a
|
|
.channel_store()
|
|
.update(cx_a, |store, cx| {
|
|
store.set_channel_visibility(channel_id, proto::ChannelVisibility::Public, cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
// Users A and B join the channel. B is a guest.
|
|
active_call_a
|
|
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
|
|
.await
|
|
.unwrap();
|
|
active_call_b
|
|
.update(cx_b, |call, cx| call.join_channel(channel_id, cx))
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
let room_b = cx_b
|
|
.read(ActiveCall::global)
|
|
.update(cx_b, |call, _| call.room().unwrap().clone());
|
|
assert!(room_b.read_with(cx_b, |room, _| !room.can_use_microphone()));
|
|
|
|
// A tries to grant write access to B, but cannot because B has not
|
|
// yet signed the zed CLA.
|
|
active_call_a
|
|
.update(cx_a, |call, cx| {
|
|
call.room().unwrap().update(cx, |room, cx| {
|
|
room.set_participant_role(
|
|
client_b.user_id().unwrap(),
|
|
proto::ChannelRole::Member,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap_err();
|
|
cx_a.run_until_parked();
|
|
assert!(room_b.read_with(cx_b, |room, _| !room.can_share_projects()));
|
|
assert!(room_b.read_with(cx_b, |room, _| !room.can_use_microphone()));
|
|
|
|
// A tries to grant write access to B, but cannot because B has not
|
|
// yet signed the zed CLA.
|
|
active_call_a
|
|
.update(cx_a, |call, cx| {
|
|
call.room().unwrap().update(cx, |room, cx| {
|
|
room.set_participant_role(
|
|
client_b.user_id().unwrap(),
|
|
proto::ChannelRole::Talker,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
assert!(room_b.read_with(cx_b, |room, _| !room.can_share_projects()));
|
|
assert!(room_b.read_with(cx_b, |room, _| room.can_use_microphone()));
|
|
|
|
// User B signs the zed CLA.
|
|
server
|
|
.app_state
|
|
.db
|
|
.add_contributor("user_b", Some(100), None, Some(Utc::now()), None)
|
|
.await
|
|
.unwrap();
|
|
|
|
// A can now grant write access to B.
|
|
active_call_a
|
|
.update(cx_a, |call, cx| {
|
|
call.room().unwrap().update(cx, |room, cx| {
|
|
room.set_participant_role(
|
|
client_b.user_id().unwrap(),
|
|
proto::ChannelRole::Member,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx_a.run_until_parked();
|
|
assert!(room_b.read_with(cx_b, |room, _| room.can_share_projects()));
|
|
assert!(room_b.read_with(cx_b, |room, _| room.can_use_microphone()));
|
|
}
|