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