channel_message_tests.rs

  1use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
  2use channel::{ChannelChat, ChannelMessageId, MessageParams};
  3use collab_ui::chat_panel::ChatPanel;
  4use gpui::{BackgroundExecutor, Model, TestAppContext};
  5use rpc::Notification;
  6use workspace::dock::Panel;
  7
  8#[gpui::test]
  9async fn test_basic_channel_messages(
 10    executor: BackgroundExecutor,
 11    mut cx_a: &mut TestAppContext,
 12    mut cx_b: &mut TestAppContext,
 13    mut cx_c: &mut TestAppContext,
 14) {
 15    let mut server = TestServer::start(executor.clone()).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    let client_c = server.create_client(cx_c, "user_c").await;
 19
 20    let channel_id = server
 21        .make_channel(
 22            "the-channel",
 23            None,
 24            (&client_a, cx_a),
 25            &mut [(&client_b, cx_b), (&client_c, cx_c)],
 26        )
 27        .await;
 28
 29    let channel_chat_a = client_a
 30        .channel_store()
 31        .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
 32        .await
 33        .unwrap();
 34    let channel_chat_b = client_b
 35        .channel_store()
 36        .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
 37        .await
 38        .unwrap();
 39
 40    let message_id = channel_chat_a
 41        .update(cx_a, |c, cx| {
 42            c.send_message(
 43                MessageParams {
 44                    text: "hi @user_c!".into(),
 45                    mentions: vec![(3..10, client_c.id())],
 46                    reply_to_message_id: None,
 47                },
 48                cx,
 49            )
 50            .unwrap()
 51        })
 52        .await
 53        .unwrap();
 54    channel_chat_a
 55        .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
 56        .await
 57        .unwrap();
 58
 59    executor.run_until_parked();
 60    channel_chat_b
 61        .update(cx_b, |c, cx| c.send_message("three".into(), cx).unwrap())
 62        .await
 63        .unwrap();
 64
 65    executor.run_until_parked();
 66
 67    let channel_chat_c = client_c
 68        .channel_store()
 69        .update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
 70        .await
 71        .unwrap();
 72
 73    for (chat, cx) in [
 74        (&channel_chat_a, &mut cx_a),
 75        (&channel_chat_b, &mut cx_b),
 76        (&channel_chat_c, &mut cx_c),
 77    ] {
 78        chat.update(*cx, |c, _| {
 79            assert_eq!(
 80                c.messages()
 81                    .iter()
 82                    .map(|m| (m.body.as_str(), m.mentions.as_slice()))
 83                    .collect::<Vec<_>>(),
 84                vec![
 85                    ("hi @user_c!", [(3..10, client_c.id())].as_slice()),
 86                    ("two", &[]),
 87                    ("three", &[])
 88                ],
 89                "results for user {}",
 90                c.client().id(),
 91            );
 92        });
 93    }
 94
 95    client_c.notification_store().update(cx_c, |store, _| {
 96        assert_eq!(store.notification_count(), 2);
 97        assert_eq!(store.unread_notification_count(), 1);
 98        assert_eq!(
 99            store.notification_at(0).unwrap().notification,
100            Notification::ChannelMessageMention {
101                message_id,
102                sender_id: client_a.id(),
103                channel_id: channel_id.0,
104            }
105        );
106        assert_eq!(
107            store.notification_at(1).unwrap().notification,
108            Notification::ChannelInvitation {
109                channel_id: channel_id.0,
110                channel_name: "the-channel".to_string(),
111                inviter_id: client_a.id()
112            }
113        );
114    });
115}
116
117#[gpui::test]
118async fn test_rejoin_channel_chat(
119    executor: BackgroundExecutor,
120    cx_a: &mut TestAppContext,
121    cx_b: &mut TestAppContext,
122) {
123    let mut server = TestServer::start(executor.clone()).await;
124    let client_a = server.create_client(cx_a, "user_a").await;
125    let client_b = server.create_client(cx_b, "user_b").await;
126
127    let channel_id = server
128        .make_channel(
129            "the-channel",
130            None,
131            (&client_a, cx_a),
132            &mut [(&client_b, cx_b)],
133        )
134        .await;
135
136    let channel_chat_a = client_a
137        .channel_store()
138        .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
139        .await
140        .unwrap();
141    let channel_chat_b = client_b
142        .channel_store()
143        .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
144        .await
145        .unwrap();
146
147    channel_chat_a
148        .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
149        .await
150        .unwrap();
151    channel_chat_b
152        .update(cx_b, |c, cx| c.send_message("two".into(), cx).unwrap())
153        .await
154        .unwrap();
155
156    server.forbid_connections();
157    server.disconnect_client(client_a.peer_id().unwrap());
158
159    // While client A is disconnected, clients A and B both send new messages.
160    channel_chat_a
161        .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
162        .await
163        .unwrap_err();
164    channel_chat_a
165        .update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
166        .await
167        .unwrap_err();
168    channel_chat_b
169        .update(cx_b, |c, cx| c.send_message("five".into(), cx).unwrap())
170        .await
171        .unwrap();
172    channel_chat_b
173        .update(cx_b, |c, cx| c.send_message("six".into(), cx).unwrap())
174        .await
175        .unwrap();
176
177    // Client A reconnects.
178    server.allow_connections();
179    executor.advance_clock(RECONNECT_TIMEOUT);
180
181    // Client A fetches the messages that were sent while they were disconnected
182    // and resends their own messages which failed to send.
183    let expected_messages = &["one", "two", "five", "six", "three", "four"];
184    assert_messages(&channel_chat_a, expected_messages, cx_a);
185    assert_messages(&channel_chat_b, expected_messages, cx_b);
186}
187
188#[gpui::test]
189async fn test_remove_channel_message(
190    executor: BackgroundExecutor,
191    cx_a: &mut TestAppContext,
192    cx_b: &mut TestAppContext,
193    cx_c: &mut TestAppContext,
194) {
195    let mut server = TestServer::start(executor.clone()).await;
196    let client_a = server.create_client(cx_a, "user_a").await;
197    let client_b = server.create_client(cx_b, "user_b").await;
198    let client_c = server.create_client(cx_c, "user_c").await;
199
200    let channel_id = server
201        .make_channel(
202            "the-channel",
203            None,
204            (&client_a, cx_a),
205            &mut [(&client_b, cx_b), (&client_c, cx_c)],
206        )
207        .await;
208
209    let channel_chat_a = client_a
210        .channel_store()
211        .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
212        .await
213        .unwrap();
214    let channel_chat_b = client_b
215        .channel_store()
216        .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
217        .await
218        .unwrap();
219
220    // Client A sends some messages.
221    channel_chat_a
222        .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
223        .await
224        .unwrap();
225    channel_chat_a
226        .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
227        .await
228        .unwrap();
229    channel_chat_a
230        .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
231        .await
232        .unwrap();
233
234    // Clients A and B see all of the messages.
235    executor.run_until_parked();
236    let expected_messages = &["one", "two", "three"];
237    assert_messages(&channel_chat_a, expected_messages, cx_a);
238    assert_messages(&channel_chat_b, expected_messages, cx_b);
239
240    // Client A deletes one of their messages.
241    channel_chat_a
242        .update(cx_a, |c, cx| {
243            let ChannelMessageId::Saved(id) = c.message(1).id else {
244                panic!("message not saved")
245            };
246            c.remove_message(id, cx)
247        })
248        .await
249        .unwrap();
250
251    // Client B sees that the message is gone.
252    executor.run_until_parked();
253    let expected_messages = &["one", "three"];
254    assert_messages(&channel_chat_a, expected_messages, cx_a);
255    assert_messages(&channel_chat_b, expected_messages, cx_b);
256
257    // Client C joins the channel chat, and does not see the deleted message.
258    let channel_chat_c = client_c
259        .channel_store()
260        .update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
261        .await
262        .unwrap();
263    assert_messages(&channel_chat_c, expected_messages, cx_c);
264}
265
266#[track_caller]
267fn assert_messages(chat: &Model<ChannelChat>, messages: &[&str], cx: &mut TestAppContext) {
268    assert_eq!(
269        chat.read_with(cx, |chat, _| {
270            chat.messages()
271                .iter()
272                .map(|m| m.body.clone())
273                .collect::<Vec<_>>()
274        }),
275        messages
276    );
277}
278
279#[gpui::test]
280async fn test_channel_message_changes(
281    executor: BackgroundExecutor,
282    cx_a: &mut TestAppContext,
283    cx_b: &mut TestAppContext,
284) {
285    let mut server = TestServer::start(executor.clone()).await;
286    let client_a = server.create_client(cx_a, "user_a").await;
287    let client_b = server.create_client(cx_b, "user_b").await;
288
289    let channel_id = server
290        .make_channel(
291            "the-channel",
292            None,
293            (&client_a, cx_a),
294            &mut [(&client_b, cx_b)],
295        )
296        .await;
297
298    // Client A sends a message, client B should see that there is a new message.
299    let channel_chat_a = client_a
300        .channel_store()
301        .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
302        .await
303        .unwrap();
304
305    channel_chat_a
306        .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
307        .await
308        .unwrap();
309
310    executor.run_until_parked();
311
312    let b_has_messages = cx_b.update(|cx| {
313        client_b
314            .channel_store()
315            .read(cx)
316            .has_new_messages(channel_id)
317    });
318
319    assert!(b_has_messages);
320
321    // Opening the chat should clear the changed flag.
322    cx_b.update(|cx| {
323        collab_ui::init(&client_b.app_state, cx);
324    });
325    let project_b = client_b.build_empty_local_project(cx_b);
326    let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
327
328    let chat_panel_b = workspace_b.update(cx_b, |workspace, cx| ChatPanel::new(workspace, cx));
329    chat_panel_b
330        .update(cx_b, |chat_panel, cx| {
331            chat_panel.set_active(true, cx);
332            chat_panel.select_channel(channel_id, None, cx)
333        })
334        .await
335        .unwrap();
336
337    executor.run_until_parked();
338
339    let b_has_messages = cx_b.update(|cx| {
340        client_b
341            .channel_store()
342            .read(cx)
343            .has_new_messages(channel_id)
344    });
345
346    assert!(!b_has_messages);
347
348    // Sending a message while the chat is open should not change the flag.
349    channel_chat_a
350        .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
351        .await
352        .unwrap();
353
354    executor.run_until_parked();
355
356    let b_has_messages = cx_b.update(|cx| {
357        client_b
358            .channel_store()
359            .read(cx)
360            .has_new_messages(channel_id)
361    });
362
363    assert!(!b_has_messages);
364
365    // Sending a message while the chat is closed should change the flag.
366    chat_panel_b.update(cx_b, |chat_panel, cx| {
367        chat_panel.set_active(false, cx);
368    });
369
370    // Sending a message while the chat is open should not change the flag.
371    channel_chat_a
372        .update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
373        .await
374        .unwrap();
375
376    executor.run_until_parked();
377
378    let b_has_messages = cx_b.update(|cx| {
379        client_b
380            .channel_store()
381            .read(cx)
382            .has_new_messages(channel_id)
383    });
384
385    assert!(b_has_messages);
386
387    // Closing the chat should re-enable change tracking
388    cx_b.update(|_| drop(chat_panel_b));
389
390    channel_chat_a
391        .update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
392        .await
393        .unwrap();
394
395    executor.run_until_parked();
396
397    let b_has_messages = cx_b.update(|cx| {
398        client_b
399            .channel_store()
400            .read(cx)
401            .has_new_messages(channel_id)
402    });
403
404    assert!(b_has_messages);
405}
406
407#[gpui::test]
408async fn test_chat_replies(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
409    let mut server = TestServer::start(cx_a.executor()).await;
410    let client_a = server.create_client(cx_a, "user_a").await;
411    let client_b = server.create_client(cx_b, "user_b").await;
412
413    let channel_id = server
414        .make_channel(
415            "the-channel",
416            None,
417            (&client_a, cx_a),
418            &mut [(&client_b, cx_b)],
419        )
420        .await;
421
422    // Client A sends a message, client B should see that there is a new message.
423    let channel_chat_a = client_a
424        .channel_store()
425        .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
426        .await
427        .unwrap();
428
429    let channel_chat_b = client_b
430        .channel_store()
431        .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
432        .await
433        .unwrap();
434
435    let msg_id = channel_chat_a
436        .update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
437        .await
438        .unwrap();
439
440    cx_a.run_until_parked();
441
442    let reply_id = channel_chat_b
443        .update(cx_b, |c, cx| {
444            c.send_message(
445                MessageParams {
446                    text: "reply".into(),
447                    reply_to_message_id: Some(msg_id),
448                    mentions: Vec::new(),
449                },
450                cx,
451            )
452            .unwrap()
453        })
454        .await
455        .unwrap();
456
457    cx_a.run_until_parked();
458
459    channel_chat_a.update(cx_a, |channel_chat, _| {
460        assert_eq!(
461            channel_chat
462                .find_loaded_message(reply_id)
463                .unwrap()
464                .reply_to_message_id,
465            Some(msg_id),
466        )
467    });
468}