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