channel_store_tests.rs

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