db_tests.rs

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