Add failing integration test for resharing projects on reconnect

Max Brunsfeld created

Change summary

crates/collab/src/integration_tests.rs | 94 +++++++++++++++++++++++++++
1 file changed, 93 insertions(+), 1 deletion(-)

Detailed changes

crates/collab/src/integration_tests.rs 🔗

@@ -15,7 +15,7 @@ use editor::{
     self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, ExcerptRange, MultiBuffer,
     Redo, Rename, ToOffset, ToggleCodeActions, Undo,
 };
-use fs::{FakeFs, Fs as _, HomeDir, LineEnding};
+use fs::{FakeFs, Fs as _, HomeDir, LineEnding, RemoveOptions};
 use futures::{channel::oneshot, StreamExt as _};
 use gpui::{
     executor::Deterministic, geometry::vector::vec2f, test::EmptyView, ModelHandle, Task,
@@ -1306,6 +1306,98 @@ async fn test_host_disconnect(
     project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
 }
 
+#[gpui::test(iterations = 10)]
+async fn test_host_reconnect(
+    deterministic: Arc<Deterministic>,
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    cx_b.update(editor::init);
+    deterministic.forbid_parking();
+    let mut server = TestServer::start(&deterministic).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;
+
+    client_a
+        .fs
+        .insert_tree(
+            "/root",
+            json!({
+                "dir1": {
+                    "a.txt": "a-contents",
+                    "b.txt": "b-contents",
+                    "subdir1": {
+                        "c.txt": "c-contents",
+                        "d.txt": "d-contents",
+                        "e.txt": "e-contents",
+                    }
+                },
+                "dir2": {
+                    "x.txt": "x-contents",
+                },
+            }),
+        )
+        .await;
+
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let (project_a, _) = client_a.build_local_project("/root/dir1", cx_a).await;
+    let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
+
+    // Drop client A's connection.
+    server.forbid_connections();
+    server.disconnect_client(client_a.peer_id().unwrap());
+    deterministic.advance_clock(RECEIVE_TIMEOUT);
+    project_a.read_with(cx_a, |project, _| {
+        assert!(project.collaborators().is_empty())
+    });
+    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
+    project_b.read_with(cx_b, |project, _| assert!(project.is_read_only()));
+    worktree_a.read_with(cx_a, |tree, _| {
+        assert!(!tree.as_local().unwrap().is_shared())
+    });
+
+    // While disconnected, add and remove files from the client A's project.
+    client_a
+        .fs
+        .insert_tree(
+            "/root/dir1/subdir2",
+            json!({
+                "f.txt": "f-contents",
+                "g.txt": "g-contents",
+                "h.txt": "h-contents",
+                "i.txt": "i-contents",
+            }),
+        )
+        .await;
+    client_a
+        .fs
+        .remove_dir(
+            "/root/dir1/subdir1".as_ref(),
+            RemoveOptions {
+                recursive: true,
+                ..Default::default()
+            },
+        )
+        .await
+        .unwrap();
+
+    // Client A reconnects. Their project is re-shared, and client B re-joins it.
+    server.allow_connections();
+    deterministic.advance_clock(RECEIVE_TIMEOUT);
+    project_a.read_with(cx_a, |project, _| assert!(project.is_shared()));
+    project_b.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
+}
+
 #[gpui::test(iterations = 10)]
 async fn test_active_call_events(
     deterministic: Arc<Deterministic>,