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                },
188                proto::ChannelMessage {
189                    id: 11,
190                    body: "b".into(),
191                    timestamp: 1001,
192                    sender_id: 6,
193                    mentions: vec![],
194                    nonce: Some(2.into()),
195                },
196            ],
197            done: false,
198        },
199    );
200
201    cx.executor().start_waiting();
202
203    // Client requests all users for the received messages
204    let mut get_users = server.receive::<proto::GetUsers>().await.unwrap();
205    get_users.payload.user_ids.sort();
206    assert_eq!(get_users.payload.user_ids, vec![6]);
207    server.respond(
208        get_users.receipt(),
209        proto::UsersResponse {
210            users: vec![proto::User {
211                id: 6,
212                github_login: "maxbrunsfeld".into(),
213                avatar_url: "http://avatar.com/maxbrunsfeld".into(),
214            }],
215        },
216    );
217
218    let channel = channel.await.unwrap();
219    channel.update(cx, |channel, _| {
220        assert_eq!(
221            channel
222                .messages_in_range(0..2)
223                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
224                .collect::<Vec<_>>(),
225            &[
226                ("nathansobo".into(), "a".into()),
227                ("maxbrunsfeld".into(), "b".into())
228            ]
229        );
230    });
231
232    // Receive a new message.
233    server.send(proto::ChannelMessageSent {
234        channel_id,
235        message: Some(proto::ChannelMessage {
236            id: 12,
237            body: "c".into(),
238            timestamp: 1002,
239            sender_id: 7,
240            mentions: vec![],
241            nonce: Some(3.into()),
242        }),
243    });
244
245    // Client requests user for message since they haven't seen them yet
246    let get_users = server.receive::<proto::GetUsers>().await.unwrap();
247    assert_eq!(get_users.payload.user_ids, vec![7]);
248    server.respond(
249        get_users.receipt(),
250        proto::UsersResponse {
251            users: vec![proto::User {
252                id: 7,
253                github_login: "as-cii".into(),
254                avatar_url: "http://avatar.com/as-cii".into(),
255            }],
256        },
257    );
258
259    assert_eq!(
260        channel.next_event(cx),
261        ChannelChatEvent::MessagesUpdated {
262            old_range: 2..2,
263            new_count: 1,
264        }
265    );
266    channel.update(cx, |channel, _| {
267        assert_eq!(
268            channel
269                .messages_in_range(2..3)
270                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
271                .collect::<Vec<_>>(),
272            &[("as-cii".into(), "c".into())]
273        )
274    });
275
276    // Scroll up to view older messages.
277    channel.update(cx, |channel, cx| {
278        channel.load_more_messages(cx).unwrap().detach();
279    });
280    let get_messages = server.receive::<proto::GetChannelMessages>().await.unwrap();
281    assert_eq!(get_messages.payload.channel_id, 5);
282    assert_eq!(get_messages.payload.before_message_id, 10);
283    server.respond(
284        get_messages.receipt(),
285        proto::GetChannelMessagesResponse {
286            done: true,
287            messages: vec![
288                proto::ChannelMessage {
289                    id: 8,
290                    body: "y".into(),
291                    timestamp: 998,
292                    sender_id: 5,
293                    nonce: Some(4.into()),
294                    mentions: vec![],
295                },
296                proto::ChannelMessage {
297                    id: 9,
298                    body: "z".into(),
299                    timestamp: 999,
300                    sender_id: 6,
301                    nonce: Some(5.into()),
302                    mentions: vec![],
303                },
304            ],
305        },
306    );
307
308    assert_eq!(
309        channel.next_event(cx),
310        ChannelChatEvent::MessagesUpdated {
311            old_range: 0..0,
312            new_count: 2,
313        }
314    );
315    channel.update(cx, |channel, _| {
316        assert_eq!(
317            channel
318                .messages_in_range(0..2)
319                .map(|message| (message.sender.github_login.clone(), message.body.clone()))
320                .collect::<Vec<_>>(),
321            &[
322                ("nathansobo".into(), "y".into()),
323                ("maxbrunsfeld".into(), "z".into())
324            ]
325        );
326    });
327}
328
329fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
330    let settings_store = SettingsStore::test(cx);
331    cx.set_global(settings_store);
332
333    let http = FakeHttpClient::with_404_response();
334    let client = Client::new(http.clone(), cx);
335    let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
336
337    client::init(&client, cx);
338    crate::init(&client, user_store, cx);
339
340    ChannelStore::global(cx)
341}
342
343fn update_channels(
344    channel_store: &Model<ChannelStore>,
345    message: proto::UpdateChannels,
346    cx: &mut AppContext,
347) {
348    let task = channel_store.update(cx, |store, cx| store.update_channels(message, cx));
349    assert!(task.is_none());
350}
351
352#[track_caller]
353fn assert_channels(
354    channel_store: &Model<ChannelStore>,
355    expected_channels: &[(usize, String)],
356    cx: &mut AppContext,
357) {
358    let actual = channel_store.update(cx, |store, _| {
359        store
360            .ordered_channels()
361            .map(|(depth, channel)| (depth, channel.name.to_string()))
362            .collect::<Vec<_>>()
363    });
364    assert_eq!(actual, expected_channels);
365}