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    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", Some(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", Some(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}