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}