1use crate::tests::TestServer;
2use call::ActiveCall;
3use fs::{FakeFs, Fs as _};
4use gpui::{Context as _, TestAppContext};
5use language::language_settings::all_language_settings;
6use remote::SshSession;
7use remote_server::HeadlessProject;
8use serde_json::json;
9use std::{path::Path, sync::Arc};
10
11#[gpui::test]
12async fn test_sharing_an_ssh_remote_project(
13 cx_a: &mut TestAppContext,
14 cx_b: &mut TestAppContext,
15 server_cx: &mut TestAppContext,
16) {
17 let executor = cx_a.executor();
18 let mut server = TestServer::start(executor.clone()).await;
19 let client_a = server.create_client(cx_a, "user_a").await;
20 let client_b = server.create_client(cx_b, "user_b").await;
21 server
22 .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
23 .await;
24
25 // Set up project on remote FS
26 let (client_ssh, server_ssh) = SshSession::fake(cx_a, server_cx);
27 let remote_fs = FakeFs::new(server_cx.executor());
28 remote_fs
29 .insert_tree(
30 "/code",
31 json!({
32 "project1": {
33 ".zed": {
34 "settings.json": r#"{"languages":{"Rust":{"language_servers":["override-rust-analyzer"]}}}"#
35 },
36 "README.md": "# project 1",
37 "src": {
38 "lib.rs": "fn one() -> usize { 1 }"
39 }
40 },
41 "project2": {
42 "README.md": "# project 2",
43 },
44 }),
45 )
46 .await;
47
48 // User A connects to the remote project via SSH.
49 server_cx.update(HeadlessProject::init);
50 let _headless_project =
51 server_cx.new_model(|cx| HeadlessProject::new(server_ssh, remote_fs.clone(), cx));
52
53 let (project_a, worktree_id) = client_a
54 .build_ssh_project("/code/project1", client_ssh, cx_a)
55 .await;
56 executor.run_until_parked();
57
58 // User A shares the remote project.
59 let active_call_a = cx_a.read(ActiveCall::global);
60 let project_id = active_call_a
61 .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
62 .await
63 .unwrap();
64
65 // User B joins the project.
66 let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
67 let worktree_b = project_b
68 .update(cx_b, |project, cx| project.worktree_for_id(worktree_id, cx))
69 .unwrap();
70
71 executor.run_until_parked();
72 worktree_b.update(cx_b, |worktree, _cx| {
73 assert_eq!(
74 worktree.paths().map(Arc::as_ref).collect::<Vec<_>>(),
75 vec![
76 Path::new(".zed"),
77 Path::new(".zed/settings.json"),
78 Path::new("README.md"),
79 Path::new("src"),
80 Path::new("src/lib.rs"),
81 ]
82 );
83 });
84
85 // User B can open buffers in the remote project.
86 let buffer_b = project_b
87 .update(cx_b, |project, cx| {
88 project.open_buffer((worktree_id, "src/lib.rs"), cx)
89 })
90 .await
91 .unwrap();
92 buffer_b.update(cx_b, |buffer, cx| {
93 assert_eq!(buffer.text(), "fn one() -> usize { 1 }");
94 let ix = buffer.text().find('1').unwrap();
95 buffer.edit([(ix..ix + 1, "100")], None, cx);
96 });
97
98 executor.run_until_parked();
99
100 cx_b.read(|cx| {
101 let file = buffer_b.read(cx).file();
102 assert_eq!(
103 all_language_settings(file, cx)
104 .language(Some(&("Rust".into())))
105 .language_servers,
106 ["override-rust-analyzer".to_string()]
107 )
108 });
109
110 project_b
111 .update(cx_b, |project, cx| project.save_buffer(buffer_b, cx))
112 .await
113 .unwrap();
114 assert_eq!(
115 remote_fs
116 .load("/code/project1/src/lib.rs".as_ref())
117 .await
118 .unwrap(),
119 "fn one() -> usize { 100 }"
120 );
121}