db_tests.rs

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