channel_store_tests.rs

  1use crate::channel_chat::ChannelChatEvent;
  2
  3use super::*;
  4use client::{test::FakeServer, Client, UserStore};
  5use gpui::{AppContext, Context, Model, TestAppContext};
  6use rpc::proto::{self};
  7use settings::SettingsStore;
  8use util::http::FakeHttpClient;
  9
 10#[gpui::test]
 11fn test_update_channels(cx: &mut AppContext) {
 12    let channel_store = init_test(cx);
 13
 14    update_channels(
 15        &channel_store,
 16        proto::UpdateChannels {
 17            channels: vec![
 18                proto::Channel {
 19                    id: 1,
 20                    name: "b".to_string(),
 21                    visibility: proto::ChannelVisibility::Members as i32,
 22                    parent_path: Vec::new(),
 23                },
 24                proto::Channel {
 25                    id: 2,
 26                    name: "a".to_string(),
 27                    visibility: proto::ChannelVisibility::Members as i32,
 28                    parent_path: Vec::new(),
 29                },
 30            ],
 31            ..Default::default()
 32        },
 33        cx,
 34    );
 35    assert_channels(
 36        &channel_store,
 37        &[
 38            //
 39            (0, "a".to_string()),
 40            (0, "b".to_string()),
 41        ],
 42        cx,
 43    );
 44
 45    update_channels(
 46        &channel_store,
 47        proto::UpdateChannels {
 48            channels: vec![
 49                proto::Channel {
 50                    id: 3,
 51                    name: "x".to_string(),
 52                    visibility: proto::ChannelVisibility::Members as i32,
 53                    parent_path: vec![1],
 54                },
 55                proto::Channel {
 56                    id: 4,
 57                    name: "y".to_string(),
 58                    visibility: proto::ChannelVisibility::Members as i32,
 59                    parent_path: vec![2],
 60                },
 61            ],
 62            ..Default::default()
 63        },
 64        cx,
 65    );
 66    assert_channels(
 67        &channel_store,
 68        &[
 69            (0, "a".to_string()),
 70            (1, "y".to_string()),
 71            (0, "b".to_string()),
 72            (1, "x".to_string()),
 73        ],
 74        cx,
 75    );
 76}
 77
 78#[gpui::test]
 79fn test_dangling_channel_paths(cx: &mut AppContext) {
 80    let channel_store = init_test(cx);
 81
 82    update_channels(
 83        &channel_store,
 84        proto::UpdateChannels {
 85            channels: vec![
 86                proto::Channel {
 87                    id: 0,
 88                    name: "a".to_string(),
 89                    visibility: proto::ChannelVisibility::Members as i32,
 90                    parent_path: vec![],
 91                },
 92                proto::Channel {
 93                    id: 1,
 94                    name: "b".to_string(),
 95                    visibility: proto::ChannelVisibility::Members as i32,
 96                    parent_path: vec![0],
 97                },
 98                proto::Channel {
 99                    id: 2,
100                    name: "c".to_string(),
101                    visibility: proto::ChannelVisibility::Members as i32,
102                    parent_path: vec![0, 1],
103                },
104            ],
105            ..Default::default()
106        },
107        cx,
108    );
109    // Sanity check
110    assert_channels(
111        &channel_store,
112        &[
113            //
114            (0, "a".to_string()),
115            (1, "b".to_string()),
116            (2, "c".to_string()),
117        ],
118        cx,
119    );
120
121    update_channels(
122        &channel_store,
123        proto::UpdateChannels {
124            delete_channels: vec![1, 2],
125            ..Default::default()
126        },
127        cx,
128    );
129
130    // Make sure that the 1/2/3 path is gone
131    assert_channels(&channel_store, &[(0, "a".to_string())], cx);
132}
133
134#[gpui::test]
135async fn test_channel_messages(cx: &mut TestAppContext) {
136    let user_id = 5;
137    let channel_id = 5;
138    let channel_store = cx.update(init_test);
139    let client = channel_store.update(cx, |s, _| s.client());
140    let server = FakeServer::for_client(user_id, &client, cx).await;
141
142    // Get the available channels.
143    server.send(proto::UpdateChannels {
144        channels: vec![proto::Channel {
145            id: channel_id,
146            name: "the-channel".to_string(),
147            visibility: proto::ChannelVisibility::Members as i32,
148            parent_path: vec![],
149        }],
150        ..Default::default()
151    });
152    cx.executor().run_until_parked();
153    cx.update(|cx| {
154        assert_channels(&channel_store, &[(0, "the-channel".to_string())], cx);
155    });
156
157    let get_users = server.receive::<proto::GetUsers>().await.unwrap();
158    assert_eq!(get_users.payload.user_ids, vec![5]);
159    server.respond(
160        get_users.receipt(),
161        proto::UsersResponse {
162            users: vec![proto::User {
163                id: 5,
164                github_login: "nathansobo".into(),
165                avatar_url: "http://avatar.com/nathansobo".into(),
166            }],
167        },
168    );
169
170    // Join a channel and populate its existing messages.
171    let channel = channel_store.update(cx, |store, cx| {
172        let channel_id = store.ordered_channels().next().unwrap().1.id;
173        store.open_channel_chat(channel_id, cx)
174    });
175    let join_channel = server.receive::<proto::JoinChannelChat>().await.unwrap();
176    server.respond(
177        join_channel.receipt(),
178        proto::JoinChannelChatResponse {
179            messages: vec![
180                proto::ChannelMessage {
181                    id: 10,
182                    body: "a".into(),
183                    timestamp: 1000,
184                    sender_id: 5,
185                    mentions: vec![],
186                    nonce: Some(1.into()),
187                    reply_to_message_id: None,
188                },
189                proto::ChannelMessage {
190                    id: 11,
191                    body: "b".into(),
192                    timestamp: 1001,
193                    sender_id: 6,
194                    mentions: vec![],
195                    nonce: Some(2.into()),
196                    reply_to_message_id: None,
197                },
198            ],
199            done: false,
200        },
201    );
202
203    cx.executor().start_waiting();
204
205    // Client requests all users for the received messages
206    let mut get_users = server.receive::<proto::GetUsers>().await.unwrap();
207    get_users.payload.user_ids.sort();
208    assert_eq!(get_users.payload.user_ids, vec![6]);
209    server.respond(
210        get_users.receipt(),
211        proto::UsersResponse {
212            users: vec![proto::User {
213                id: 6,
214                github_login: "maxbrunsfeld".into(),
215                avatar_url: "http://avatar.com/maxbrunsfeld".into(),
216            }],
217        },
218    );
219
220    let channel = channel.await.unwrap();
221    channel.update(cx, |channel, _| {
222        assert_eq!(
223            channel
224                .messages_in_range(0..2)
225                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
226                .collect::<Vec<_>>(),
227            &[
228                ("nathansobo".into(), "a".into()),
229                ("maxbrunsfeld".into(), "b".into())
230            ]
231        );
232    });
233
234    // Receive a new message.
235    server.send(proto::ChannelMessageSent {
236        channel_id,
237        message: Some(proto::ChannelMessage {
238            id: 12,
239            body: "c".into(),
240            timestamp: 1002,
241            sender_id: 7,
242            mentions: vec![],
243            nonce: Some(3.into()),
244            reply_to_message_id: None,
245        }),
246    });
247
248    // Client requests user for message since they haven't seen them yet
249    let get_users = server.receive::<proto::GetUsers>().await.unwrap();
250    assert_eq!(get_users.payload.user_ids, vec![7]);
251    server.respond(
252        get_users.receipt(),
253        proto::UsersResponse {
254            users: vec![proto::User {
255                id: 7,
256                github_login: "as-cii".into(),
257                avatar_url: "http://avatar.com/as-cii".into(),
258            }],
259        },
260    );
261
262    assert_eq!(
263        channel.next_event(cx),
264        ChannelChatEvent::MessagesUpdated {
265            old_range: 2..2,
266            new_count: 1,
267        }
268    );
269    channel.update(cx, |channel, _| {
270        assert_eq!(
271            channel
272                .messages_in_range(2..3)
273                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
274                .collect::<Vec<_>>(),
275            &[("as-cii".into(), "c".into())]
276        )
277    });
278
279    // Scroll up to view older messages.
280    channel.update(cx, |channel, cx| {
281        channel.load_more_messages(cx).unwrap().detach();
282    });
283    let get_messages = server.receive::<proto::GetChannelMessages>().await.unwrap();
284    assert_eq!(get_messages.payload.channel_id, 5);
285    assert_eq!(get_messages.payload.before_message_id, 10);
286    server.respond(
287        get_messages.receipt(),
288        proto::GetChannelMessagesResponse {
289            done: true,
290            messages: vec![
291                proto::ChannelMessage {
292                    id: 8,
293                    body: "y".into(),
294                    timestamp: 998,
295                    sender_id: 5,
296                    nonce: Some(4.into()),
297                    mentions: vec![],
298                    reply_to_message_id: None,
299                },
300                proto::ChannelMessage {
301                    id: 9,
302                    body: "z".into(),
303                    timestamp: 999,
304                    sender_id: 6,
305                    nonce: Some(5.into()),
306                    mentions: vec![],
307                    reply_to_message_id: None,
308                },
309            ],
310        },
311    );
312
313    assert_eq!(
314        channel.next_event(cx),
315        ChannelChatEvent::MessagesUpdated {
316            old_range: 0..0,
317            new_count: 2,
318        }
319    );
320    channel.update(cx, |channel, _| {
321        assert_eq!(
322            channel
323                .messages_in_range(0..2)
324                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
325                .collect::<Vec<_>>(),
326            &[
327                ("nathansobo".into(), "y".into()),
328                ("maxbrunsfeld".into(), "z".into())
329            ]
330        );
331    });
332}
333
334fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
335    let settings_store = SettingsStore::test(cx);
336    cx.set_global(settings_store);
337    release_channel::init("0.0.0", cx);
338    client::init_settings(cx);
339
340    let http = FakeHttpClient::with_404_response();
341    let client = Client::new(http.clone(), cx);
342    let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
343
344    client::init(&client, cx);
345    crate::init(&client, user_store, cx);
346
347    ChannelStore::global(cx)
348}
349
350fn update_channels(
351    channel_store: &Model<ChannelStore>,
352    message: proto::UpdateChannels,
353    cx: &mut AppContext,
354) {
355    let task = channel_store.update(cx, |store, cx| store.update_channels(message, cx));
356    assert!(task.is_none());
357}
358
359#[track_caller]
360fn assert_channels(
361    channel_store: &Model<ChannelStore>,
362    expected_channels: &[(usize, String)],
363    cx: &mut AppContext,
364) {
365    let actual = channel_store.update(cx, |store, _| {
366        store
367            .ordered_channels()
368            .map(|(depth, channel)| (depth, channel.name.to_string()))
369            .collect::<Vec<_>>()
370    });
371    assert_eq!(actual, expected_channels);
372}