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 },
188 proto::ChannelMessage {
189 id: 11,
190 body: "b".into(),
191 timestamp: 1001,
192 sender_id: 6,
193 mentions: vec![],
194 nonce: Some(2.into()),
195 },
196 ],
197 done: false,
198 },
199 );
200
201 cx.executor().start_waiting();
202
203 // Client requests all users for the received messages
204 let mut get_users = server.receive::<proto::GetUsers>().await.unwrap();
205 get_users.payload.user_ids.sort();
206 assert_eq!(get_users.payload.user_ids, vec![6]);
207 server.respond(
208 get_users.receipt(),
209 proto::UsersResponse {
210 users: vec![proto::User {
211 id: 6,
212 github_login: "maxbrunsfeld".into(),
213 avatar_url: "http://avatar.com/maxbrunsfeld".into(),
214 }],
215 },
216 );
217
218 let channel = channel.await.unwrap();
219 channel.update(cx, |channel, _| {
220 assert_eq!(
221 channel
222 .messages_in_range(0..2)
223 .map(|message| (message.sender.github_login.clone(), message.body.clone()))
224 .collect::<Vec<_>>(),
225 &[
226 ("nathansobo".into(), "a".into()),
227 ("maxbrunsfeld".into(), "b".into())
228 ]
229 );
230 });
231
232 // Receive a new message.
233 server.send(proto::ChannelMessageSent {
234 channel_id,
235 message: Some(proto::ChannelMessage {
236 id: 12,
237 body: "c".into(),
238 timestamp: 1002,
239 sender_id: 7,
240 mentions: vec![],
241 nonce: Some(3.into()),
242 }),
243 });
244
245 // Client requests user for message since they haven't seen them yet
246 let get_users = server.receive::<proto::GetUsers>().await.unwrap();
247 assert_eq!(get_users.payload.user_ids, vec![7]);
248 server.respond(
249 get_users.receipt(),
250 proto::UsersResponse {
251 users: vec![proto::User {
252 id: 7,
253 github_login: "as-cii".into(),
254 avatar_url: "http://avatar.com/as-cii".into(),
255 }],
256 },
257 );
258
259 assert_eq!(
260 channel.next_event(cx),
261 ChannelChatEvent::MessagesUpdated {
262 old_range: 2..2,
263 new_count: 1,
264 }
265 );
266 channel.update(cx, |channel, _| {
267 assert_eq!(
268 channel
269 .messages_in_range(2..3)
270 .map(|message| (message.sender.github_login.clone(), message.body.clone()))
271 .collect::<Vec<_>>(),
272 &[("as-cii".into(), "c".into())]
273 )
274 });
275
276 // Scroll up to view older messages.
277 channel.update(cx, |channel, cx| {
278 channel.load_more_messages(cx).unwrap().detach();
279 });
280 let get_messages = server.receive::<proto::GetChannelMessages>().await.unwrap();
281 assert_eq!(get_messages.payload.channel_id, 5);
282 assert_eq!(get_messages.payload.before_message_id, 10);
283 server.respond(
284 get_messages.receipt(),
285 proto::GetChannelMessagesResponse {
286 done: true,
287 messages: vec![
288 proto::ChannelMessage {
289 id: 8,
290 body: "y".into(),
291 timestamp: 998,
292 sender_id: 5,
293 nonce: Some(4.into()),
294 mentions: vec![],
295 },
296 proto::ChannelMessage {
297 id: 9,
298 body: "z".into(),
299 timestamp: 999,
300 sender_id: 6,
301 nonce: Some(5.into()),
302 mentions: vec![],
303 },
304 ],
305 },
306 );
307
308 assert_eq!(
309 channel.next_event(cx),
310 ChannelChatEvent::MessagesUpdated {
311 old_range: 0..0,
312 new_count: 2,
313 }
314 );
315 channel.update(cx, |channel, _| {
316 assert_eq!(
317 channel
318 .messages_in_range(0..2)
319 .map(|message| (message.sender.github_login.clone(), message.body.clone()))
320 .collect::<Vec<_>>(),
321 &[
322 ("nathansobo".into(), "y".into()),
323 ("maxbrunsfeld".into(), "z".into())
324 ]
325 );
326 });
327}
328
329fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
330 let settings_store = SettingsStore::test(cx);
331 cx.set_global(settings_store);
332
333 let http = FakeHttpClient::with_404_response();
334 let client = Client::new(http.clone(), cx);
335 let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
336
337 client::init(&client, cx);
338 crate::init(&client, user_store, cx);
339
340 ChannelStore::global(cx)
341}
342
343fn update_channels(
344 channel_store: &Model<ChannelStore>,
345 message: proto::UpdateChannels,
346 cx: &mut AppContext,
347) {
348 let task = channel_store.update(cx, |store, cx| store.update_channels(message, cx));
349 assert!(task.is_none());
350}
351
352#[track_caller]
353fn assert_channels(
354 channel_store: &Model<ChannelStore>,
355 expected_channels: &[(usize, String)],
356 cx: &mut AppContext,
357) {
358 let actual = channel_store.update(cx, |store, _| {
359 store
360 .ordered_channels()
361 .map(|(depth, channel)| (depth, channel.name.to_string()))
362 .collect::<Vec<_>>()
363 });
364 assert_eq!(actual, expected_channels);
365}