channel_message_tests.rs

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