channel_store_tests.rs

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