Merge remote-tracking branch 'origin/main' into room
This commit is contained in:
commit
afaacba41f
92 changed files with 10800 additions and 6586 deletions
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
auth,
|
||||
db::{ProjectId, User, UserId},
|
||||
db::{Invite, NewUserParams, ProjectId, Signup, User, UserId, WaitlistSummary},
|
||||
rpc::{self, ResultExt},
|
||||
AppState, Error, Result,
|
||||
};
|
||||
|
@ -24,13 +24,10 @@ use tracing::instrument;
|
|||
|
||||
pub fn routes(rpc_server: &Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body> {
|
||||
Router::new()
|
||||
.route("/user", get(get_authenticated_user))
|
||||
.route("/users", get(get_users).post(create_user))
|
||||
.route(
|
||||
"/users/:id",
|
||||
put(update_user).delete(destroy_user).get(get_user),
|
||||
)
|
||||
.route("/users/:id", put(update_user).delete(destroy_user))
|
||||
.route("/users/:id/access_tokens", post(create_access_token))
|
||||
.route("/bulk_users", post(create_users))
|
||||
.route("/users_with_no_invites", get(get_users_with_no_invites))
|
||||
.route("/invite_codes/:code", get(get_user_for_invite_code))
|
||||
.route("/panic", post(trace_panic))
|
||||
|
@ -45,6 +42,11 @@ pub fn routes(rpc_server: &Arc<rpc::Server>, state: Arc<AppState>) -> Router<Bod
|
|||
)
|
||||
.route("/user_activity/counts", get(get_active_user_counts))
|
||||
.route("/project_metadata", get(get_project_metadata))
|
||||
.route("/signups", post(create_signup))
|
||||
.route("/signups_summary", get(get_waitlist_summary))
|
||||
.route("/user_invites", post(create_invite_from_code))
|
||||
.route("/unsent_invites", get(get_unsent_invites))
|
||||
.route("/sent_invites", post(record_sent_invites))
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
.layer(Extension(state))
|
||||
|
@ -84,6 +86,31 @@ pub async fn validate_api_token<B>(req: Request<B>, next: Next<B>) -> impl IntoR
|
|||
Ok::<_, Error>(next.run(req).await)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AuthenticatedUserParams {
|
||||
github_user_id: i32,
|
||||
github_login: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AuthenticatedUserResponse {
|
||||
user: User,
|
||||
metrics_id: String,
|
||||
}
|
||||
|
||||
async fn get_authenticated_user(
|
||||
Query(params): Query<AuthenticatedUserParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<AuthenticatedUserResponse>> {
|
||||
let user = app
|
||||
.db
|
||||
.get_user_by_github_account(¶ms.github_login, Some(params.github_user_id))
|
||||
.await?
|
||||
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?;
|
||||
let metrics_id = app.db.get_user_metrics_id(user.id).await?;
|
||||
return Ok(Json(AuthenticatedUserResponse { user, metrics_id }));
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GetUsersQueryParams {
|
||||
query: Option<String>,
|
||||
|
@ -108,48 +135,76 @@ async fn get_users(
|
|||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct CreateUserParams {
|
||||
github_user_id: i32,
|
||||
github_login: String,
|
||||
invite_code: Option<String>,
|
||||
email_address: Option<String>,
|
||||
email_address: String,
|
||||
email_confirmation_code: Option<String>,
|
||||
#[serde(default)]
|
||||
admin: bool,
|
||||
#[serde(default)]
|
||||
invite_count: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct CreateUserResponse {
|
||||
user: User,
|
||||
signup_device_id: Option<String>,
|
||||
metrics_id: String,
|
||||
}
|
||||
|
||||
async fn create_user(
|
||||
Json(params): Json<CreateUserParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
Extension(rpc_server): Extension<Arc<rpc::Server>>,
|
||||
) -> Result<Json<User>> {
|
||||
let user_id = if let Some(invite_code) = params.invite_code {
|
||||
let invitee_id = app
|
||||
.db
|
||||
.redeem_invite_code(
|
||||
&invite_code,
|
||||
¶ms.github_login,
|
||||
params.email_address.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
rpc_server
|
||||
.invite_code_redeemed(&invite_code, invitee_id)
|
||||
.await
|
||||
.trace_err();
|
||||
invitee_id
|
||||
} else {
|
||||
) -> Result<Json<CreateUserResponse>> {
|
||||
let user = NewUserParams {
|
||||
github_login: params.github_login,
|
||||
github_user_id: params.github_user_id,
|
||||
invite_count: params.invite_count,
|
||||
};
|
||||
|
||||
// Creating a user via the normal signup process
|
||||
let result = if let Some(email_confirmation_code) = params.email_confirmation_code {
|
||||
app.db
|
||||
.create_user(
|
||||
¶ms.github_login,
|
||||
params.email_address.as_deref(),
|
||||
params.admin,
|
||||
.create_user_from_invite(
|
||||
&Invite {
|
||||
email_address: params.email_address,
|
||||
email_confirmation_code,
|
||||
},
|
||||
user,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
// Creating a user as an admin
|
||||
else if params.admin {
|
||||
app.db
|
||||
.create_user(¶ms.email_address, false, user)
|
||||
.await?
|
||||
} else {
|
||||
Err(Error::Http(
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
"email confirmation code is required".into(),
|
||||
))?
|
||||
};
|
||||
|
||||
if let Some(inviter_id) = result.inviting_user_id {
|
||||
rpc_server
|
||||
.invite_code_redeemed(inviter_id, result.user_id)
|
||||
.await
|
||||
.trace_err();
|
||||
}
|
||||
|
||||
let user = app
|
||||
.db
|
||||
.get_user_by_id(user_id)
|
||||
.get_user_by_id(result.user_id)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("couldn't find the user we just created"))?;
|
||||
|
||||
Ok(Json(user))
|
||||
Ok(Json(CreateUserResponse {
|
||||
user,
|
||||
metrics_id: result.metrics_id,
|
||||
signup_device_id: result.signup_device_id,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -171,7 +226,9 @@ async fn update_user(
|
|||
}
|
||||
|
||||
if let Some(invite_count) = params.invite_count {
|
||||
app.db.set_invite_count(user_id, invite_count).await?;
|
||||
app.db
|
||||
.set_invite_count_for_user(user_id, invite_count)
|
||||
.await?;
|
||||
rpc_server.invite_count_updated(user_id).await.trace_err();
|
||||
}
|
||||
|
||||
|
@ -186,54 +243,6 @@ async fn destroy_user(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_user(
|
||||
Path(login): Path<String>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<User>> {
|
||||
let user = app
|
||||
.db
|
||||
.get_user_by_github_login(&login)
|
||||
.await?
|
||||
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CreateUsersParams {
|
||||
users: Vec<CreateUsersEntry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CreateUsersEntry {
|
||||
github_login: String,
|
||||
email_address: String,
|
||||
invite_count: usize,
|
||||
}
|
||||
|
||||
async fn create_users(
|
||||
Json(params): Json<CreateUsersParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<User>>> {
|
||||
let user_ids = app
|
||||
.db
|
||||
.create_users(
|
||||
params
|
||||
.users
|
||||
.into_iter()
|
||||
.map(|params| {
|
||||
(
|
||||
params.github_login,
|
||||
params.email_address,
|
||||
params.invite_count,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.await?;
|
||||
let users = app.db.get_users_by_ids(user_ids).await?;
|
||||
Ok(Json(users))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GetUsersWithNoInvites {
|
||||
invited_by_another_user: bool,
|
||||
|
@ -368,22 +377,24 @@ struct CreateAccessTokenResponse {
|
|||
}
|
||||
|
||||
async fn create_access_token(
|
||||
Path(login): Path<String>,
|
||||
Path(user_id): Path<UserId>,
|
||||
Query(params): Query<CreateAccessTokenQueryParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<CreateAccessTokenResponse>> {
|
||||
// request.require_token().await?;
|
||||
|
||||
let user = app
|
||||
.db
|
||||
.get_user_by_github_login(&login)
|
||||
.get_user_by_id(user_id)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("user not found"))?;
|
||||
|
||||
let mut user_id = user.id;
|
||||
if let Some(impersonate) = params.impersonate {
|
||||
if user.admin {
|
||||
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
|
||||
if let Some(impersonated_user) = app
|
||||
.db
|
||||
.get_user_by_github_account(&impersonate, None)
|
||||
.await?
|
||||
{
|
||||
user_id = impersonated_user.id;
|
||||
} else {
|
||||
return Err(Error::Http(
|
||||
|
@ -415,3 +426,59 @@ async fn get_user_for_invite_code(
|
|||
) -> Result<Json<User>> {
|
||||
Ok(Json(app.db.get_user_for_invite_code(&code).await?))
|
||||
}
|
||||
|
||||
async fn create_signup(
|
||||
Json(params): Json<Signup>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<()> {
|
||||
app.db.create_signup(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_waitlist_summary(
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<WaitlistSummary>> {
|
||||
Ok(Json(app.db.get_waitlist_summary().await?))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateInviteFromCodeParams {
|
||||
invite_code: String,
|
||||
email_address: String,
|
||||
device_id: Option<String>,
|
||||
}
|
||||
|
||||
async fn create_invite_from_code(
|
||||
Json(params): Json<CreateInviteFromCodeParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<Invite>> {
|
||||
Ok(Json(
|
||||
app.db
|
||||
.create_invite_from_code(
|
||||
¶ms.invite_code,
|
||||
¶ms.email_address,
|
||||
params.device_id.as_deref(),
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetUnsentInvitesParams {
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
async fn get_unsent_invites(
|
||||
Query(params): Query<GetUnsentInvitesParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<Invite>>> {
|
||||
Ok(Json(app.db.get_unsent_invites(params.count).await?))
|
||||
}
|
||||
|
||||
async fn record_sent_invites(
|
||||
Json(params): Json<Vec<Invite>>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<()> {
|
||||
app.db.record_sent_invites(¶ms).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ mod db;
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GitHubUser {
|
||||
id: usize,
|
||||
id: i32,
|
||||
login: String,
|
||||
email: Option<String>,
|
||||
}
|
||||
|
@ -26,8 +26,11 @@ async fn main() {
|
|||
let github_token = std::env::var("GITHUB_TOKEN").expect("missing GITHUB_TOKEN env var");
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let current_user =
|
||||
let mut current_user =
|
||||
fetch_github::<GitHubUser>(&client, &github_token, "https://api.github.com/user").await;
|
||||
current_user
|
||||
.email
|
||||
.get_or_insert_with(|| "placeholder@example.com".to_string());
|
||||
let staff_users = fetch_github::<Vec<GitHubUser>>(
|
||||
&client,
|
||||
&github_token,
|
||||
|
@ -64,16 +67,24 @@ async fn main() {
|
|||
let mut zed_user_ids = Vec::<UserId>::new();
|
||||
for (github_user, admin) in zed_users {
|
||||
if let Some(user) = db
|
||||
.get_user_by_github_login(&github_user.login)
|
||||
.get_user_by_github_account(&github_user.login, Some(github_user.id))
|
||||
.await
|
||||
.expect("failed to fetch user")
|
||||
{
|
||||
zed_user_ids.push(user.id);
|
||||
} else {
|
||||
} else if let Some(email) = &github_user.email {
|
||||
zed_user_ids.push(
|
||||
db.create_user(&github_user.login, github_user.email.as_deref(), admin)
|
||||
.await
|
||||
.expect("failed to insert user"),
|
||||
db.create_user(
|
||||
email,
|
||||
admin,
|
||||
db::NewUserParams {
|
||||
github_login: github_user.login,
|
||||
github_user_id: github_user.id,
|
||||
invite_count: 5,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("failed to insert user"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
1186
crates/collab/src/db_tests.rs
Normal file
1186
crates/collab/src/db_tests.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
db::{tests::TestDb, ProjectId, UserId},
|
||||
db::{NewUserParams, ProjectId, TestDb, UserId},
|
||||
rpc::{Executor, Server, Store},
|
||||
AppState,
|
||||
};
|
||||
|
@ -52,6 +52,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use unindent::Unindent as _;
|
||||
use workspace::{Item, SplitDirection, ToggleFollow, Workspace};
|
||||
|
||||
#[ctor::ctor]
|
||||
|
@ -329,6 +330,7 @@ async fn test_room_uniqueness(
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
deterministic.run_until_parked();
|
||||
let call_b2 = incoming_call_b.next().await.unwrap().unwrap();
|
||||
assert_eq!(call_b2.caller.github_login, "user_c");
|
||||
}
|
||||
|
@ -1174,6 +1176,258 @@ async fn test_propagate_saves_and_fs_changes(
|
|||
.await;
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_git_diff_base_change(
|
||||
executor: Arc<Deterministic>,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
executor.forbid_parking();
|
||||
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
client_a
|
||||
.fs
|
||||
.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
".git": {},
|
||||
"sub": {
|
||||
".git": {},
|
||||
"b.txt": "
|
||||
one
|
||||
two
|
||||
three
|
||||
".unindent(),
|
||||
},
|
||||
"a.txt": "
|
||||
one
|
||||
two
|
||||
three
|
||||
".unindent(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| {
|
||||
call.share_project(project_local.clone(), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let project_remote = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
let diff_base = "
|
||||
one
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_diff_base = "
|
||||
one
|
||||
two
|
||||
"
|
||||
.unindent();
|
||||
|
||||
client_a
|
||||
.fs
|
||||
.as_fake()
|
||||
.set_index_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[(Path::new("a.txt"), diff_base.clone())],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Create the buffer
|
||||
let buffer_local_a = project_local
|
||||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for it to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test diffing
|
||||
buffer_local_a.read_with(cx_a, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Create remote buffer
|
||||
let buffer_remote_a = project_remote
|
||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait remote buffer to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test diffing
|
||||
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
client_a
|
||||
.fs
|
||||
.as_fake()
|
||||
.set_index_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[(Path::new("a.txt"), new_diff_base.clone())],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Wait for buffer_local_a to receive it
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test new diffing
|
||||
buffer_local_a.read_with(cx_a, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Smoke test B
|
||||
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
//Nested git dir
|
||||
|
||||
let diff_base = "
|
||||
one
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_diff_base = "
|
||||
one
|
||||
two
|
||||
"
|
||||
.unindent();
|
||||
|
||||
client_a
|
||||
.fs
|
||||
.as_fake()
|
||||
.set_index_for_repo(
|
||||
Path::new("/dir/sub/.git"),
|
||||
&[(Path::new("b.txt"), diff_base.clone())],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Create the buffer
|
||||
let buffer_local_b = project_local
|
||||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for it to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test diffing
|
||||
buffer_local_b.read_with(cx_a, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Create remote buffer
|
||||
let buffer_remote_b = project_remote
|
||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait remote buffer to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test diffing
|
||||
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
client_a
|
||||
.fs
|
||||
.as_fake()
|
||||
.set_index_for_repo(
|
||||
Path::new("/dir/sub/.git"),
|
||||
&[(Path::new("b.txt"), new_diff_base.clone())],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Wait for buffer_local_b to receive it
|
||||
executor.run_until_parked();
|
||||
|
||||
// Smoke test new diffing
|
||||
buffer_local_b.read_with(cx_a, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||
println!("{:?}", buffer.as_rope().to_string());
|
||||
println!("{:?}", buffer.diff_base());
|
||||
println!(
|
||||
"{:?}",
|
||||
buffer
|
||||
.snapshot()
|
||||
.git_diff_hunks_in_range(0..4)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Smoke test B
|
||||
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||
git::diff::assert_hunks(
|
||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
||||
&buffer,
|
||||
&diff_base,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_fs_operations(
|
||||
executor: Arc<Deterministic>,
|
||||
|
@ -5092,7 +5346,19 @@ async fn test_random_collaboration(
|
|||
let mut server = TestServer::start(cx.foreground(), cx.background()).await;
|
||||
let db = server.app_state.db.clone();
|
||||
|
||||
let room_creator_user_id = db.create_user("room-creator", None, false).await.unwrap();
|
||||
let room_creator_user_id = db
|
||||
.create_user(
|
||||
"room-creator@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "room-creator".into(),
|
||||
github_user_id: 0,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let mut available_guests = vec![
|
||||
"guest-1".to_string(),
|
||||
"guest-2".to_string(),
|
||||
|
@ -5100,11 +5366,24 @@ async fn test_random_collaboration(
|
|||
"guest-4".to_string(),
|
||||
];
|
||||
|
||||
for username in Some(&"host".to_string())
|
||||
for (ix, username) in Some(&"host".to_string())
|
||||
.into_iter()
|
||||
.chain(&available_guests)
|
||||
.enumerate()
|
||||
{
|
||||
let user_id = db.create_user(username, None, false).await.unwrap();
|
||||
let user_id = db
|
||||
.create_user(
|
||||
&format!("{username}@example.com"),
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: username.into(),
|
||||
github_user_id: (ix + 1) as i32,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
server
|
||||
.app_state
|
||||
.db
|
||||
|
@ -5632,18 +5911,31 @@ impl TestServer {
|
|||
});
|
||||
|
||||
let http = FakeHttpClient::with_404_response();
|
||||
let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await
|
||||
let user_id = if let Ok(Some(user)) = self
|
||||
.app_state
|
||||
.db
|
||||
.get_user_by_github_account(name, None)
|
||||
.await
|
||||
{
|
||||
user.id
|
||||
} else {
|
||||
self.app_state
|
||||
.db
|
||||
.create_user(name, None, false)
|
||||
.create_user(
|
||||
&format!("{name}@example.com"),
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: name.into(),
|
||||
github_user_id: 0,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id
|
||||
};
|
||||
let client_name = name.to_string();
|
||||
let mut client = Client::new(http.clone());
|
||||
let mut client = cx.read(|cx| Client::new(http.clone(), cx));
|
||||
let server = self.server.clone();
|
||||
let db = self.app_state.db.clone();
|
||||
let connection_killers = self.connection_killers.clone();
|
||||
|
|
|
@ -4,6 +4,8 @@ mod db;
|
|||
mod env;
|
||||
mod rpc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod db_tests;
|
||||
#[cfg(test)]
|
||||
mod integration_tests;
|
||||
|
||||
|
|
|
@ -206,7 +206,9 @@ impl Server {
|
|||
.add_request_handler(Server::follow)
|
||||
.add_message_handler(Server::unfollow)
|
||||
.add_message_handler(Server::update_followers)
|
||||
.add_request_handler(Server::get_channel_messages);
|
||||
.add_request_handler(Server::get_channel_messages)
|
||||
.add_message_handler(Server::update_diff_base)
|
||||
.add_request_handler(Server::get_private_user_info);
|
||||
|
||||
Arc::new(server)
|
||||
}
|
||||
|
@ -528,27 +530,30 @@ impl Server {
|
|||
|
||||
pub async fn invite_code_redeemed(
|
||||
self: &Arc<Self>,
|
||||
code: &str,
|
||||
inviter_id: UserId,
|
||||
invitee_id: UserId,
|
||||
) -> Result<()> {
|
||||
let user = self.app_state.db.get_user_for_invite_code(code).await?;
|
||||
let store = self.store().await;
|
||||
let invitee_contact = store.contact_for_user(invitee_id, true);
|
||||
for connection_id in store.connection_ids_for_user(user.id) {
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateContacts {
|
||||
contacts: vec![invitee_contact.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateInviteInfo {
|
||||
url: format!("{}{}", self.app_state.invite_link_prefix, code),
|
||||
count: user.invite_count as u32,
|
||||
},
|
||||
)?;
|
||||
if let Some(user) = self.app_state.db.get_user_by_id(inviter_id).await? {
|
||||
if let Some(code) = &user.invite_code {
|
||||
let store = self.store().await;
|
||||
let invitee_contact = store.contact_for_user(invitee_id, true);
|
||||
for connection_id in store.connection_ids_for_user(inviter_id) {
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateContacts {
|
||||
contacts: vec![invitee_contact.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateInviteInfo {
|
||||
url: format!("{}{}", self.app_state.invite_link_prefix, &code),
|
||||
count: user.invite_count as u32,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1427,7 +1432,7 @@ impl Server {
|
|||
let users = match query.len() {
|
||||
0 => vec![],
|
||||
1 | 2 => db
|
||||
.get_user_by_github_login(&query)
|
||||
.get_user_by_github_account(&query, None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|
@ -1750,6 +1755,44 @@ impl Server {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_diff_base(
|
||||
self: Arc<Server>,
|
||||
request: TypedEnvelope<proto::UpdateDiffBase>,
|
||||
) -> Result<()> {
|
||||
let receiver_ids = self.store().await.project_connection_ids(
|
||||
ProjectId::from_proto(request.payload.project_id),
|
||||
request.sender_id,
|
||||
)?;
|
||||
broadcast(request.sender_id, receiver_ids, |connection_id| {
|
||||
self.peer
|
||||
.forward_send(request.sender_id, connection_id, request.payload.clone())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_private_user_info(
|
||||
self: Arc<Self>,
|
||||
request: TypedEnvelope<proto::GetPrivateUserInfo>,
|
||||
response: Response<proto::GetPrivateUserInfo>,
|
||||
) -> Result<()> {
|
||||
let user_id = self
|
||||
.store()
|
||||
.await
|
||||
.user_id_for_connection(request.sender_id)?;
|
||||
let metrics_id = self.app_state.db.get_user_metrics_id(user_id).await?;
|
||||
let user = self
|
||||
.app_state
|
||||
.db
|
||||
.get_user_by_id(user_id)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("user not found"))?;
|
||||
response.send(proto::GetPrivateUserInfoResponse {
|
||||
metrics_id,
|
||||
staff: user.admin,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn store(&self) -> StoreGuard<'_> {
|
||||
#[cfg(test)]
|
||||
tokio::task::yield_now().await;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue