1use super::*;
2use crate::test_both_dbs;
3use gpui::TestAppContext;
4use pretty_assertions::{assert_eq, assert_ne};
5use std::sync::Arc;
6use tests::TestDb;
7
8test_both_dbs!(
9 test_get_users,
10 test_get_users_by_ids_postgres,
11 test_get_users_by_ids_sqlite
12);
13
14async fn test_get_users(db: &Arc<Database>) {
15 let mut user_ids = Vec::new();
16 let mut user_metric_ids = Vec::new();
17 for i in 1..=4 {
18 let user = db
19 .create_user(
20 &format!("user{i}@example.com"),
21 false,
22 NewUserParams {
23 github_login: format!("user{i}"),
24 github_user_id: i,
25 },
26 )
27 .await
28 .unwrap();
29 user_ids.push(user.user_id);
30 user_metric_ids.push(user.metrics_id);
31 }
32
33 assert_eq!(
34 db.get_users_by_ids(user_ids.clone())
35 .await
36 .unwrap()
37 .into_iter()
38 .map(|user| (
39 user.id,
40 user.github_login,
41 user.github_user_id,
42 user.email_address
43 ))
44 .collect::<Vec<_>>(),
45 vec![
46 (
47 user_ids[0],
48 "user1".to_string(),
49 Some(1),
50 Some("user1@example.com".to_string()),
51 ),
52 (
53 user_ids[1],
54 "user2".to_string(),
55 Some(2),
56 Some("user2@example.com".to_string()),
57 ),
58 (
59 user_ids[2],
60 "user3".to_string(),
61 Some(3),
62 Some("user3@example.com".to_string()),
63 ),
64 (
65 user_ids[3],
66 "user4".to_string(),
67 Some(4),
68 Some("user4@example.com".to_string()),
69 )
70 ]
71 );
72}
73
74test_both_dbs!(
75 test_get_or_create_user_by_github_account,
76 test_get_or_create_user_by_github_account_postgres,
77 test_get_or_create_user_by_github_account_sqlite
78);
79
80async fn test_get_or_create_user_by_github_account(db: &Arc<Database>) {
81 db.create_user(
82 "user1@example.com",
83 false,
84 NewUserParams {
85 github_login: "login1".into(),
86 github_user_id: 101,
87 },
88 )
89 .await
90 .unwrap();
91 let user_id2 = db
92 .create_user(
93 "user2@example.com",
94 false,
95 NewUserParams {
96 github_login: "login2".into(),
97 github_user_id: 102,
98 },
99 )
100 .await
101 .unwrap()
102 .user_id;
103
104 let user = db
105 .get_or_create_user_by_github_account("the-new-login2", Some(102), None, None)
106 .await
107 .unwrap();
108 assert_eq!(user.id, user_id2);
109 assert_eq!(&user.github_login, "the-new-login2");
110 assert_eq!(user.github_user_id, Some(102));
111
112 let user = db
113 .get_or_create_user_by_github_account("login3", Some(103), Some("user3@example.com"), None)
114 .await
115 .unwrap();
116 assert_eq!(&user.github_login, "login3");
117 assert_eq!(user.github_user_id, Some(103));
118 assert_eq!(user.email_address, Some("user3@example.com".into()));
119}
120
121test_both_dbs!(
122 test_create_access_tokens,
123 test_create_access_tokens_postgres,
124 test_create_access_tokens_sqlite
125);
126
127async fn test_create_access_tokens(db: &Arc<Database>) {
128 let user_1 = db
129 .create_user(
130 "u1@example.com",
131 false,
132 NewUserParams {
133 github_login: "u1".into(),
134 github_user_id: 1,
135 },
136 )
137 .await
138 .unwrap()
139 .user_id;
140 let user_2 = db
141 .create_user(
142 "u2@example.com",
143 false,
144 NewUserParams {
145 github_login: "u2".into(),
146 github_user_id: 2,
147 },
148 )
149 .await
150 .unwrap()
151 .user_id;
152
153 let token_1 = db.create_access_token(user_1, None, "h1", 2).await.unwrap();
154 let token_2 = db.create_access_token(user_1, None, "h2", 2).await.unwrap();
155 assert_eq!(
156 db.get_access_token(token_1).await.unwrap(),
157 access_token::Model {
158 id: token_1,
159 user_id: user_1,
160 impersonated_user_id: None,
161 hash: "h1".into(),
162 }
163 );
164 assert_eq!(
165 db.get_access_token(token_2).await.unwrap(),
166 access_token::Model {
167 id: token_2,
168 user_id: user_1,
169 impersonated_user_id: None,
170 hash: "h2".into()
171 }
172 );
173
174 let token_3 = db.create_access_token(user_1, None, "h3", 2).await.unwrap();
175 assert_eq!(
176 db.get_access_token(token_3).await.unwrap(),
177 access_token::Model {
178 id: token_3,
179 user_id: user_1,
180 impersonated_user_id: None,
181 hash: "h3".into()
182 }
183 );
184 assert_eq!(
185 db.get_access_token(token_2).await.unwrap(),
186 access_token::Model {
187 id: token_2,
188 user_id: user_1,
189 impersonated_user_id: None,
190 hash: "h2".into()
191 }
192 );
193 assert!(db.get_access_token(token_1).await.is_err());
194
195 let token_4 = db.create_access_token(user_1, None, "h4", 2).await.unwrap();
196 assert_eq!(
197 db.get_access_token(token_4).await.unwrap(),
198 access_token::Model {
199 id: token_4,
200 user_id: user_1,
201 impersonated_user_id: None,
202 hash: "h4".into()
203 }
204 );
205 assert_eq!(
206 db.get_access_token(token_3).await.unwrap(),
207 access_token::Model {
208 id: token_3,
209 user_id: user_1,
210 impersonated_user_id: None,
211 hash: "h3".into()
212 }
213 );
214 assert!(db.get_access_token(token_2).await.is_err());
215 assert!(db.get_access_token(token_1).await.is_err());
216
217 // An access token for user 2 impersonating user 1 does not
218 // count against user 1's access token limit (of 2).
219 let token_5 = db
220 .create_access_token(user_2, Some(user_1), "h5", 2)
221 .await
222 .unwrap();
223 assert_eq!(
224 db.get_access_token(token_5).await.unwrap(),
225 access_token::Model {
226 id: token_5,
227 user_id: user_2,
228 impersonated_user_id: Some(user_1),
229 hash: "h5".into()
230 }
231 );
232 assert_eq!(
233 db.get_access_token(token_3).await.unwrap(),
234 access_token::Model {
235 id: token_3,
236 user_id: user_1,
237 impersonated_user_id: None,
238 hash: "h3".into()
239 }
240 );
241
242 // Only a limited number (2) of access tokens are stored for user 2
243 // impersonating other users.
244 let token_6 = db
245 .create_access_token(user_2, Some(user_1), "h6", 2)
246 .await
247 .unwrap();
248 let token_7 = db
249 .create_access_token(user_2, Some(user_1), "h7", 2)
250 .await
251 .unwrap();
252 assert_eq!(
253 db.get_access_token(token_6).await.unwrap(),
254 access_token::Model {
255 id: token_6,
256 user_id: user_2,
257 impersonated_user_id: Some(user_1),
258 hash: "h6".into()
259 }
260 );
261 assert_eq!(
262 db.get_access_token(token_7).await.unwrap(),
263 access_token::Model {
264 id: token_7,
265 user_id: user_2,
266 impersonated_user_id: Some(user_1),
267 hash: "h7".into()
268 }
269 );
270 assert!(db.get_access_token(token_5).await.is_err());
271 assert_eq!(
272 db.get_access_token(token_3).await.unwrap(),
273 access_token::Model {
274 id: token_3,
275 user_id: user_1,
276 impersonated_user_id: None,
277 hash: "h3".into()
278 }
279 );
280}
281
282test_both_dbs!(
283 test_add_contacts,
284 test_add_contacts_postgres,
285 test_add_contacts_sqlite
286);
287
288async fn test_add_contacts(db: &Arc<Database>) {
289 let mut user_ids = Vec::new();
290 for i in 0..3 {
291 user_ids.push(
292 db.create_user(
293 &format!("user{i}@example.com"),
294 false,
295 NewUserParams {
296 github_login: format!("user{i}"),
297 github_user_id: i,
298 },
299 )
300 .await
301 .unwrap()
302 .user_id,
303 );
304 }
305
306 let user_1 = user_ids[0];
307 let user_2 = user_ids[1];
308 let user_3 = user_ids[2];
309
310 // User starts with no contacts
311 assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
312
313 // User requests a contact. Both users see the pending request.
314 db.send_contact_request(user_1, user_2).await.unwrap();
315 assert!(!db.has_contact(user_1, user_2).await.unwrap());
316 assert!(!db.has_contact(user_2, user_1).await.unwrap());
317 assert_eq!(
318 db.get_contacts(user_1).await.unwrap(),
319 &[Contact::Outgoing { user_id: user_2 }],
320 );
321 assert_eq!(
322 db.get_contacts(user_2).await.unwrap(),
323 &[Contact::Incoming { user_id: user_1 }]
324 );
325
326 // User 2 dismisses the contact request notification without accepting or rejecting.
327 // We shouldn't notify them again.
328 db.dismiss_contact_notification(user_1, user_2)
329 .await
330 .unwrap_err();
331 db.dismiss_contact_notification(user_2, user_1)
332 .await
333 .unwrap();
334 assert_eq!(
335 db.get_contacts(user_2).await.unwrap(),
336 &[Contact::Incoming { user_id: user_1 }]
337 );
338
339 // User can't accept their own contact request
340 db.respond_to_contact_request(user_1, user_2, true)
341 .await
342 .unwrap_err();
343
344 // User accepts a contact request. Both users see the contact.
345 db.respond_to_contact_request(user_2, user_1, true)
346 .await
347 .unwrap();
348 assert_eq!(
349 db.get_contacts(user_1).await.unwrap(),
350 &[Contact::Accepted {
351 user_id: user_2,
352 busy: false,
353 }],
354 );
355 assert!(db.has_contact(user_1, user_2).await.unwrap());
356 assert!(db.has_contact(user_2, user_1).await.unwrap());
357 assert_eq!(
358 db.get_contacts(user_2).await.unwrap(),
359 &[Contact::Accepted {
360 user_id: user_1,
361 busy: false,
362 }]
363 );
364
365 // Users cannot re-request existing contacts.
366 db.send_contact_request(user_1, user_2).await.unwrap_err();
367 db.send_contact_request(user_2, user_1).await.unwrap_err();
368
369 // Users can't dismiss notifications of them accepting other users' requests.
370 db.dismiss_contact_notification(user_2, user_1)
371 .await
372 .unwrap_err();
373 assert_eq!(
374 db.get_contacts(user_1).await.unwrap(),
375 &[Contact::Accepted {
376 user_id: user_2,
377 busy: false,
378 }]
379 );
380
381 // Users can dismiss notifications of other users accepting their requests.
382 db.dismiss_contact_notification(user_1, user_2)
383 .await
384 .unwrap();
385 assert_eq!(
386 db.get_contacts(user_1).await.unwrap(),
387 &[Contact::Accepted {
388 user_id: user_2,
389 busy: false,
390 }]
391 );
392
393 // Users send each other concurrent contact requests and
394 // see that they are immediately accepted.
395 db.send_contact_request(user_1, user_3).await.unwrap();
396 db.send_contact_request(user_3, user_1).await.unwrap();
397 assert_eq!(
398 db.get_contacts(user_1).await.unwrap(),
399 &[
400 Contact::Accepted {
401 user_id: user_2,
402 busy: false,
403 },
404 Contact::Accepted {
405 user_id: user_3,
406 busy: false,
407 }
408 ]
409 );
410 assert_eq!(
411 db.get_contacts(user_3).await.unwrap(),
412 &[Contact::Accepted {
413 user_id: user_1,
414 busy: false,
415 }],
416 );
417
418 // User declines a contact request. Both users see that it is gone.
419 db.send_contact_request(user_2, user_3).await.unwrap();
420 db.respond_to_contact_request(user_3, user_2, false)
421 .await
422 .unwrap();
423 assert!(!db.has_contact(user_2, user_3).await.unwrap());
424 assert!(!db.has_contact(user_3, user_2).await.unwrap());
425 assert_eq!(
426 db.get_contacts(user_2).await.unwrap(),
427 &[Contact::Accepted {
428 user_id: user_1,
429 busy: false,
430 }]
431 );
432 assert_eq!(
433 db.get_contacts(user_3).await.unwrap(),
434 &[Contact::Accepted {
435 user_id: user_1,
436 busy: false,
437 }],
438 );
439}
440
441test_both_dbs!(
442 test_metrics_id,
443 test_metrics_id_postgres,
444 test_metrics_id_sqlite
445);
446
447async fn test_metrics_id(db: &Arc<Database>) {
448 let NewUserResult {
449 user_id: user1,
450 metrics_id: metrics_id1,
451 ..
452 } = db
453 .create_user(
454 "person1@example.com",
455 false,
456 NewUserParams {
457 github_login: "person1".into(),
458 github_user_id: 101,
459 },
460 )
461 .await
462 .unwrap();
463 let NewUserResult {
464 user_id: user2,
465 metrics_id: metrics_id2,
466 ..
467 } = db
468 .create_user(
469 "person2@example.com",
470 false,
471 NewUserParams {
472 github_login: "person2".into(),
473 github_user_id: 102,
474 },
475 )
476 .await
477 .unwrap();
478
479 assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1);
480 assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2);
481 assert_eq!(metrics_id1.len(), 36);
482 assert_eq!(metrics_id2.len(), 36);
483 assert_ne!(metrics_id1, metrics_id2);
484}
485
486test_both_dbs!(
487 test_project_count,
488 test_project_count_postgres,
489 test_project_count_sqlite
490);
491
492async fn test_project_count(db: &Arc<Database>) {
493 let owner_id = db.create_server("test").await.unwrap().0 as u32;
494
495 let user1 = db
496 .create_user(
497 "admin@example.com",
498 true,
499 NewUserParams {
500 github_login: "admin".into(),
501 github_user_id: 0,
502 },
503 )
504 .await
505 .unwrap();
506 let user2 = db
507 .create_user(
508 "user@example.com",
509 false,
510 NewUserParams {
511 github_login: "user".into(),
512 github_user_id: 1,
513 },
514 )
515 .await
516 .unwrap();
517
518 let room_id = RoomId::from_proto(
519 db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "")
520 .await
521 .unwrap()
522 .id,
523 );
524 db.call(
525 room_id,
526 user1.user_id,
527 ConnectionId { owner_id, id: 0 },
528 user2.user_id,
529 None,
530 )
531 .await
532 .unwrap();
533 db.join_room(room_id, user2.user_id, ConnectionId { owner_id, id: 1 })
534 .await
535 .unwrap();
536 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
537
538 db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None)
539 .await
540 .unwrap();
541 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
542
543 db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None)
544 .await
545 .unwrap();
546 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
547
548 // Projects shared by admins aren't counted.
549 db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], None)
550 .await
551 .unwrap();
552 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
553
554 db.leave_room(ConnectionId { owner_id, id: 1 })
555 .await
556 .unwrap();
557 assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
558}
559
560#[test]
561fn test_fuzzy_like_string() {
562 assert_eq!(Database::fuzzy_like_string("abcd"), "%a%b%c%d%");
563 assert_eq!(Database::fuzzy_like_string("x y"), "%x%y%");
564 assert_eq!(Database::fuzzy_like_string(" z "), "%z%");
565}
566
567#[gpui::test]
568async fn test_fuzzy_search_users(cx: &mut TestAppContext) {
569 let test_db = TestDb::postgres(cx.executor());
570 let db = test_db.db();
571 for (i, github_login) in [
572 "California",
573 "colorado",
574 "oregon",
575 "washington",
576 "florida",
577 "delaware",
578 "rhode-island",
579 ]
580 .into_iter()
581 .enumerate()
582 {
583 db.create_user(
584 &format!("{github_login}@example.com"),
585 false,
586 NewUserParams {
587 github_login: github_login.into(),
588 github_user_id: i as i32,
589 },
590 )
591 .await
592 .unwrap();
593 }
594
595 assert_eq!(
596 fuzzy_search_user_names(db, "clr").await,
597 &["colorado", "California"]
598 );
599 assert_eq!(
600 fuzzy_search_user_names(db, "ro").await,
601 &["rhode-island", "colorado", "oregon"],
602 );
603
604 async fn fuzzy_search_user_names(db: &Database, query: &str) -> Vec<String> {
605 db.fuzzy_search_users(query, 10)
606 .await
607 .unwrap()
608 .into_iter()
609 .map(|user| user.github_login)
610 .collect::<Vec<_>>()
611 }
612}