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