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