db_tests.rs

  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}