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