1use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
2use channel::{ChannelChat, ChannelMessageId};
3use collab_ui::chat_panel::ChatPanel;
4use gpui::{executor::Deterministic, BorrowAppContext, ModelHandle, TestAppContext};
5use std::sync::Arc;
6use workspace::dock::Panel;
7
8#[gpui::test]
9async fn test_basic_channel_messages(
10 deterministic: Arc<Deterministic>,
11 cx_a: &mut TestAppContext,
12 cx_b: &mut TestAppContext,
13) {
14 deterministic.forbid_parking();
15 let mut server = TestServer::start(&deterministic).await;
16 let client_a = server.create_client(cx_a, "user_a").await;
17 let client_b = server.create_client(cx_b, "user_b").await;
18
19 let channel_id = server
20 .make_channel(
21 "the-channel",
22 None,
23 (&client_a, cx_a),
24 &mut [(&client_b, cx_b)],
25 )
26 .await;
27
28 let channel_chat_a = client_a
29 .channel_store()
30 .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
31 .await
32 .unwrap();
33 let channel_chat_b = client_b
34 .channel_store()
35 .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
36 .await
37 .unwrap();
38
39 channel_chat_a
40 .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
41 .await
42 .unwrap();
43 channel_chat_a
44 .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
45 .await
46 .unwrap();
47
48 deterministic.run_until_parked();
49 channel_chat_b
50 .update(cx_b, |c, cx| c.send_message("three".into(), cx).unwrap())
51 .await
52 .unwrap();
53
54 deterministic.run_until_parked();
55 channel_chat_a.update(cx_a, |c, _| {
56 assert_eq!(
57 c.messages()
58 .iter()
59 .map(|m| m.body.as_str())
60 .collect::<Vec<_>>(),
61 vec!["one", "two", "three"]
62 );
63 })
64}
65
66#[gpui::test]
67async fn test_rejoin_channel_chat(
68 deterministic: Arc<Deterministic>,
69 cx_a: &mut TestAppContext,
70 cx_b: &mut TestAppContext,
71) {
72 deterministic.forbid_parking();
73 let mut server = TestServer::start(&deterministic).await;
74 let client_a = server.create_client(cx_a, "user_a").await;
75 let client_b = server.create_client(cx_b, "user_b").await;
76
77 let channel_id = server
78 .make_channel(
79 "the-channel",
80 None,
81 (&client_a, cx_a),
82 &mut [(&client_b, cx_b)],
83 )
84 .await;
85
86 let channel_chat_a = client_a
87 .channel_store()
88 .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
89 .await
90 .unwrap();
91 let channel_chat_b = client_b
92 .channel_store()
93 .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
94 .await
95 .unwrap();
96
97 channel_chat_a
98 .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
99 .await
100 .unwrap();
101 channel_chat_b
102 .update(cx_b, |c, cx| c.send_message("two".into(), cx).unwrap())
103 .await
104 .unwrap();
105
106 server.forbid_connections();
107 server.disconnect_client(client_a.peer_id().unwrap());
108
109 // While client A is disconnected, clients A and B both send new messages.
110 channel_chat_a
111 .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
112 .await
113 .unwrap_err();
114 channel_chat_a
115 .update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
116 .await
117 .unwrap_err();
118 channel_chat_b
119 .update(cx_b, |c, cx| c.send_message("five".into(), cx).unwrap())
120 .await
121 .unwrap();
122 channel_chat_b
123 .update(cx_b, |c, cx| c.send_message("six".into(), cx).unwrap())
124 .await
125 .unwrap();
126
127 // Client A reconnects.
128 server.allow_connections();
129 deterministic.advance_clock(RECONNECT_TIMEOUT);
130
131 // Client A fetches the messages that were sent while they were disconnected
132 // and resends their own messages which failed to send.
133 let expected_messages = &["one", "two", "five", "six", "three", "four"];
134 assert_messages(&channel_chat_a, expected_messages, cx_a);
135 assert_messages(&channel_chat_b, expected_messages, cx_b);
136}
137
138#[gpui::test]
139async fn test_remove_channel_message(
140 deterministic: Arc<Deterministic>,
141 cx_a: &mut TestAppContext,
142 cx_b: &mut TestAppContext,
143 cx_c: &mut TestAppContext,
144) {
145 deterministic.forbid_parking();
146 let mut server = TestServer::start(&deterministic).await;
147 let client_a = server.create_client(cx_a, "user_a").await;
148 let client_b = server.create_client(cx_b, "user_b").await;
149 let client_c = server.create_client(cx_c, "user_c").await;
150
151 let channel_id = server
152 .make_channel(
153 "the-channel",
154 None,
155 (&client_a, cx_a),
156 &mut [(&client_b, cx_b), (&client_c, cx_c)],
157 )
158 .await;
159
160 let channel_chat_a = client_a
161 .channel_store()
162 .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
163 .await
164 .unwrap();
165 let channel_chat_b = client_b
166 .channel_store()
167 .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
168 .await
169 .unwrap();
170
171 // Client A sends some messages.
172 channel_chat_a
173 .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
174 .await
175 .unwrap();
176 channel_chat_a
177 .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
178 .await
179 .unwrap();
180 channel_chat_a
181 .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
182 .await
183 .unwrap();
184
185 // Clients A and B see all of the messages.
186 deterministic.run_until_parked();
187 let expected_messages = &["one", "two", "three"];
188 assert_messages(&channel_chat_a, expected_messages, cx_a);
189 assert_messages(&channel_chat_b, expected_messages, cx_b);
190
191 // Client A deletes one of their messages.
192 channel_chat_a
193 .update(cx_a, |c, cx| {
194 let ChannelMessageId::Saved(id) = c.message(1).id else {
195 panic!("message not saved")
196 };
197 c.remove_message(id, cx)
198 })
199 .await
200 .unwrap();
201
202 // Client B sees that the message is gone.
203 deterministic.run_until_parked();
204 let expected_messages = &["one", "three"];
205 assert_messages(&channel_chat_a, expected_messages, cx_a);
206 assert_messages(&channel_chat_b, expected_messages, cx_b);
207
208 // Client C joins the channel chat, and does not see the deleted message.
209 let channel_chat_c = client_c
210 .channel_store()
211 .update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
212 .await
213 .unwrap();
214 assert_messages(&channel_chat_c, expected_messages, cx_c);
215}
216
217#[track_caller]
218fn assert_messages(chat: &ModelHandle<ChannelChat>, messages: &[&str], cx: &mut TestAppContext) {
219 assert_eq!(
220 chat.read_with(cx, |chat, _| chat
221 .messages()
222 .iter()
223 .map(|m| m.body.clone())
224 .collect::<Vec<_>>(),),
225 messages
226 );
227}
228
229#[gpui::test]
230async fn test_channel_message_changes(
231 deterministic: Arc<Deterministic>,
232 cx_a: &mut TestAppContext,
233 cx_b: &mut TestAppContext,
234) {
235 deterministic.forbid_parking();
236 let mut server = TestServer::start(&deterministic).await;
237 let client_a = server.create_client(cx_a, "user_a").await;
238 let client_b = server.create_client(cx_b, "user_b").await;
239
240 let channel_id = server
241 .make_channel(
242 "the-channel",
243 None,
244 (&client_a, cx_a),
245 &mut [(&client_b, cx_b)],
246 )
247 .await;
248
249 // Client A sends a message, client B should see that there is a new message.
250 let channel_chat_a = client_a
251 .channel_store()
252 .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
253 .await
254 .unwrap();
255
256 channel_chat_a
257 .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
258 .await
259 .unwrap();
260
261 deterministic.run_until_parked();
262
263 let b_has_messages = cx_b.read_with(|cx| {
264 client_b
265 .channel_store()
266 .read(cx)
267 .has_new_messages(channel_id)
268 .unwrap()
269 });
270
271 assert!(b_has_messages);
272
273 // Opening the chat should clear the changed flag.
274 cx_b.update(|cx| {
275 collab_ui::init(&client_b.app_state, cx);
276 });
277 let project_b = client_b.build_empty_local_project(cx_b);
278 let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
279 let chat_panel_b = workspace_b.update(cx_b, |workspace, cx| ChatPanel::new(workspace, cx));
280 chat_panel_b
281 .update(cx_b, |chat_panel, cx| {
282 chat_panel.set_active(true, cx);
283 chat_panel.select_channel(channel_id, cx)
284 })
285 .await
286 .unwrap();
287
288 deterministic.run_until_parked();
289
290 let b_has_messages = cx_b.read_with(|cx| {
291 client_b
292 .channel_store()
293 .read(cx)
294 .has_new_messages(channel_id)
295 .unwrap()
296 });
297
298 assert!(!b_has_messages);
299
300 // Sending a message while the chat is open should not change the flag.
301 channel_chat_a
302 .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
303 .await
304 .unwrap();
305
306 deterministic.run_until_parked();
307
308 let b_has_messages = cx_b.read_with(|cx| {
309 client_b
310 .channel_store()
311 .read(cx)
312 .has_new_messages(channel_id)
313 .unwrap()
314 });
315
316 assert!(!b_has_messages);
317
318 // Sending a message while the chat is closed should change the flag.
319 chat_panel_b.update(cx_b, |chat_panel, cx| {
320 chat_panel.set_active(false, cx);
321 });
322
323 // Sending a message while the chat is open should not change the flag.
324 channel_chat_a
325 .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
326 .await
327 .unwrap();
328
329 deterministic.run_until_parked();
330
331 let b_has_messages = cx_b.read_with(|cx| {
332 client_b
333 .channel_store()
334 .read(cx)
335 .has_new_messages(channel_id)
336 .unwrap()
337 });
338
339 assert!(b_has_messages);
340
341 // Closing the chat should re-enable change tracking
342 cx_b.update(|_| drop(chat_panel_b));
343
344 channel_chat_a
345 .update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
346 .await
347 .unwrap();
348
349 deterministic.run_until_parked();
350
351 let b_has_messages = cx_b.read_with(|cx| {
352 client_b
353 .channel_store()
354 .read(cx)
355 .has_new_messages(channel_id)
356 .unwrap()
357 });
358
359 assert!(b_has_messages);
360}