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