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}