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