Only leave room on connections that are associated with the active call

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/collab/src/integration_tests.rs | 12 ++++++++++--
crates/collab/src/rpc.rs               |  4 ++++
crates/collab/src/rpc/store.rs         | 12 +++++++-----
3 files changed, 21 insertions(+), 7 deletions(-)

Detailed changes

crates/collab/src/integration_tests.rs 🔗

@@ -468,6 +468,14 @@ async fn test_calls_on_multiple_connections(
     assert!(incoming_call_b1.next().await.unwrap().is_none());
     assert!(incoming_call_b2.next().await.unwrap().is_none());
 
+    // User B disconnects the client that is not on the call. Everything should be fine.
+    client_b1.disconnect(&cx_b1.to_async()).unwrap();
+    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
+    client_b1
+        .authenticate_and_connect(false, &cx_b1.to_async())
+        .await
+        .unwrap();
+
     // User B hangs up, and user A calls them again.
     active_call_b2.update(cx_b2, |call, cx| call.hang_up(cx).unwrap());
     deterministic.run_until_parked();
@@ -520,9 +528,9 @@ async fn test_calls_on_multiple_connections(
     assert!(incoming_call_b1.next().await.unwrap().is_some());
     assert!(incoming_call_b2.next().await.unwrap().is_some());
 
-    // User A disconnects up, causing both connections to stop ringing.
+    // User A disconnects, causing both connections to stop ringing.
     server.disconnect_client(client_a.current_user_id(cx_a));
-    cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
+    deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
     assert!(incoming_call_b1.next().await.unwrap().is_none());
     assert!(incoming_call_b2.next().await.unwrap().is_none());
 }

crates/collab/src/rpc.rs 🔗

@@ -477,6 +477,10 @@ impl Server {
         let mut contacts_to_update = HashSet::default();
         {
             let mut store = self.store().await;
+
+            #[cfg(test)]
+            let removed_connection = store.remove_connection(connection_id).unwrap();
+            #[cfg(not(test))]
             let removed_connection = store.remove_connection(connection_id)?;
 
             for project in removed_connection.hosted_projects {

crates/collab/src/rpc/store.rs 🔗

@@ -215,11 +215,13 @@ impl Store {
         let connected_user = self.connected_users.get(&user_id).unwrap();
         if let Some(active_call) = connected_user.active_call.as_ref() {
             let room_id = active_call.room_id;
-            let left_room = self.leave_room(room_id, connection_id)?;
-            result.hosted_projects = left_room.unshared_projects;
-            result.guest_projects = left_room.left_projects;
-            result.room_id = Some(room_id);
-            result.canceled_call_connection_ids = left_room.canceled_call_connection_ids;
+            if active_call.connection_id == Some(connection_id) {
+                let left_room = self.leave_room(room_id, connection_id)?;
+                result.hosted_projects = left_room.unshared_projects;
+                result.guest_projects = left_room.left_projects;
+                result.room_id = Some(room_id);
+                result.canceled_call_connection_ids = left_room.canceled_call_connection_ids;
+            }
         }
 
         let connected_user = self.connected_users.get_mut(&user_id).unwrap();