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