Be more specific about clearing (leader, follower) row

Previously anyone unfollowing someone would clear all other rows for
other followers leading to an incorrect state, fix and test

Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Julia 2023-02-22 15:29:20 -05:00
parent 36040cd0e1
commit 0324ca3b08
2 changed files with 96 additions and 28 deletions

View File

@ -1759,15 +1759,10 @@ impl Database {
.filter( .filter(
Condition::all() Condition::all()
.add(follower::Column::ProjectId.eq(project_id)) .add(follower::Column::ProjectId.eq(project_id))
.add(
Condition::any()
.add( .add(
follower::Column::LeaderConnectionServerId follower::Column::LeaderConnectionServerId
.eq(leader_connection.owner_id) .eq(leader_connection.owner_id)
.and( .and(follower::Column::LeaderConnectionId.eq(leader_connection.id)),
follower::Column::LeaderConnectionId
.eq(leader_connection.id),
),
) )
.add( .add(
follower::Column::FollowerConnectionServerId follower::Column::FollowerConnectionServerId
@ -1777,7 +1772,6 @@ impl Database {
.eq(follower_connection.id), .eq(follower_connection.id),
), ),
), ),
),
) )
.exec(&*tx) .exec(&*tx)
.await?; .await?;

View File

@ -5786,6 +5786,7 @@ async fn test_following(
deterministic: Arc<Deterministic>, deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext, cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext, cx_b: &mut TestAppContext,
cx_c: &mut TestAppContext,
) { ) {
deterministic.forbid_parking(); deterministic.forbid_parking();
cx_a.update(editor::init); cx_a.update(editor::init);
@ -5794,9 +5795,13 @@ async fn test_following(
let mut server = TestServer::start(&deterministic).await; let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").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_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
server server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await; .await;
server
.make_contacts(&mut [(&client_a, cx_a), (&client_c, cx_c)])
.await;
let active_call_a = cx_a.read(ActiveCall::global); let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global);
@ -5827,8 +5832,10 @@ async fn test_following(
.await .await
.unwrap(); .unwrap();
// Client A opens some editors.
let workspace_a = client_a.build_workspace(&project_a, cx_a); let workspace_a = client_a.build_workspace(&project_a, cx_a);
let workspace_b = client_b.build_workspace(&project_b, cx_b);
// Client A opens some editors.
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone()); let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
let editor_a1 = workspace_a let editor_a1 = workspace_a
.update(cx_a, |workspace, cx| { .update(cx_a, |workspace, cx| {
@ -5848,7 +5855,6 @@ async fn test_following(
.unwrap(); .unwrap();
// Client B opens an editor. // Client B opens an editor.
let workspace_b = client_b.build_workspace(&project_b, cx_b);
let editor_b1 = workspace_b let editor_b1 = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "1.txt"), None, true, cx) workspace.open_path((worktree_id, "1.txt"), None, true, cx)
@ -5858,29 +5864,97 @@ async fn test_following(
.downcast::<Editor>() .downcast::<Editor>()
.unwrap(); .unwrap();
let client_a_id = project_b.read_with(cx_b, |project, _| { let peer_id_a = client_a.peer_id().unwrap();
project.collaborators().values().next().unwrap().peer_id let peer_id_b = client_b.peer_id().unwrap();
}); let peer_id_c = client_c.peer_id().unwrap();
let client_b_id = project_a.read_with(cx_a, |project, _| {
project.collaborators().values().next().unwrap().peer_id
});
// When client B starts following client A, all visible view states are replicated to client B. // Client A updates their selections in those editors
editor_a1.update(cx_a, |editor, cx| { editor_a1.update(cx_a, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges([0..1])) editor.change_selections(None, cx, |s| s.select_ranges([0..1]))
}); });
editor_a2.update(cx_a, |editor, cx| { editor_a2.update(cx_a, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges([2..3])) editor.change_selections(None, cx, |s| s.select_ranges([2..3]))
}); });
// When client B starts following client A, all visible view states are replicated to client B.
workspace_b workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace workspace
.toggle_follow(&ToggleFollow(client_a_id), cx) .toggle_follow(&ToggleFollow(peer_id_a), cx)
.unwrap() .unwrap()
}) })
.await .await
.unwrap(); .unwrap();
// Client A invites client C to the call.
active_call_a
.update(cx_a, |call, cx| {
call.invite(client_c.current_user_id(cx_c).to_proto(), None, cx)
})
.await
.unwrap();
cx_c.foreground().run_until_parked();
let active_call_c = cx_c.read(ActiveCall::global);
active_call_c
.update(cx_c, |call, cx| call.accept_incoming(cx))
.await
.unwrap();
let project_c = client_c.build_remote_project(project_id, cx_c).await;
let workspace_c = client_c.build_workspace(&project_c, cx_c);
active_call_c
.update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
.await
.unwrap();
// Client C also follows client A.
workspace_c
.update(cx_c, |workspace, cx| {
workspace
.toggle_follow(&ToggleFollow(peer_id_a), cx)
.unwrap()
})
.await
.unwrap();
// All clients see that clients B and C are following client A.
cx_c.foreground().run_until_parked();
for (name, active_call, cx) in [
("A", &active_call_a, &cx_a),
("B", &active_call_b, &cx_b),
("C", &active_call_c, &cx_c),
] {
active_call.read_with(*cx, |call, cx| {
let room = call.room().unwrap().read(cx);
assert_eq!(
room.followers_for(peer_id_a),
&[peer_id_b, peer_id_c],
"checking followers for A as {name}"
);
});
}
// Client C unfollows client A.
workspace_c.update(cx_c, |workspace, cx| {
workspace.toggle_follow(&ToggleFollow(peer_id_a), cx);
});
// All clients see that clients B is following client A.
cx_c.foreground().run_until_parked();
for (name, active_call, cx) in [
("A", &active_call_a, &cx_a),
("B", &active_call_b, &cx_b),
("C", &active_call_c, &cx_c),
] {
active_call.read_with(*cx, |call, cx| {
let room = call.room().unwrap().read(cx);
assert_eq!(
room.followers_for(peer_id_a),
&[peer_id_b],
"checking followers for A as {name}"
);
});
}
let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| { let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
workspace workspace
.active_item(cx) .active_item(cx)
@ -6033,14 +6107,14 @@ async fn test_following(
workspace_a workspace_a
.update(cx_a, |workspace, cx| { .update(cx_a, |workspace, cx| {
workspace workspace
.toggle_follow(&ToggleFollow(client_b_id), cx) .toggle_follow(&ToggleFollow(peer_id_b), cx)
.unwrap() .unwrap()
}) })
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)), workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
Some(client_b_id) Some(peer_id_b)
); );
assert_eq!( assert_eq!(
workspace_a.read_with(cx_a, |workspace, cx| workspace workspace_a.read_with(cx_a, |workspace, cx| workspace