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                Some(1),
 49                Some("user1@example.com".to_string()),
 50            ),
 51            (
 52                user_ids[1],
 53                "user2".to_string(),
 54                Some(2),
 55                Some("user2@example.com".to_string()),
 56            ),
 57            (
 58                user_ids[2],
 59                "user3".to_string(),
 60                Some(3),
 61                Some("user3@example.com".to_string()),
 62            ),
 63            (
 64                user_ids[3],
 65                "user4".to_string(),
 66                Some(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(
105            "the-new-login2",
106            Some(102),
107            None,
108            Some(Utc::now()),
109            None,
110        )
111        .await
112        .unwrap();
113    assert_eq!(user.id, user_id2);
114    assert_eq!(&user.github_login, "the-new-login2");
115    assert_eq!(user.github_user_id, Some(102));
116
117    let user = db
118        .get_or_create_user_by_github_account(
119            "login3",
120            Some(103),
121            Some("user3@example.com"),
122            Some(Utc::now()),
123            None,
124        )
125        .await
126        .unwrap();
127    assert_eq!(&user.github_login, "login3");
128    assert_eq!(user.github_user_id, Some(103));
129    assert_eq!(user.email_address, Some("user3@example.com".into()));
130}
131
132test_both_dbs!(
133    test_create_access_tokens,
134    test_create_access_tokens_postgres,
135    test_create_access_tokens_sqlite
136);
137
138async fn test_create_access_tokens(db: &Arc<Database>) {
139    let user_1 = db
140        .create_user(
141            "u1@example.com",
142            false,
143            NewUserParams {
144                github_login: "u1".into(),
145                github_user_id: 1,
146            },
147        )
148        .await
149        .unwrap()
150        .user_id;
151    let user_2 = db
152        .create_user(
153            "u2@example.com",
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                false,
306                NewUserParams {
307                    github_login: format!("user{i}"),
308                    github_user_id: i,
309                },
310            )
311            .await
312            .unwrap()
313            .user_id,
314        );
315    }
316
317    let user_1 = user_ids[0];
318    let user_2 = user_ids[1];
319    let user_3 = user_ids[2];
320
321    // User starts with no contacts
322    assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
323
324    // User requests a contact. Both users see the pending request.
325    db.send_contact_request(user_1, user_2).await.unwrap();
326    assert!(!db.has_contact(user_1, user_2).await.unwrap());
327    assert!(!db.has_contact(user_2, user_1).await.unwrap());
328    assert_eq!(
329        db.get_contacts(user_1).await.unwrap(),
330        &[Contact::Outgoing { user_id: user_2 }],
331    );
332    assert_eq!(
333        db.get_contacts(user_2).await.unwrap(),
334        &[Contact::Incoming { user_id: user_1 }]
335    );
336
337    // User 2 dismisses the contact request notification without accepting or rejecting.
338    // We shouldn't notify them again.
339    db.dismiss_contact_notification(user_1, user_2)
340        .await
341        .unwrap_err();
342    db.dismiss_contact_notification(user_2, user_1)
343        .await
344        .unwrap();
345    assert_eq!(
346        db.get_contacts(user_2).await.unwrap(),
347        &[Contact::Incoming { user_id: user_1 }]
348    );
349
350    // User can't accept their own contact request
351    db.respond_to_contact_request(user_1, user_2, true)
352        .await
353        .unwrap_err();
354
355    // User accepts a contact request. Both users see the contact.
356    db.respond_to_contact_request(user_2, user_1, true)
357        .await
358        .unwrap();
359    assert_eq!(
360        db.get_contacts(user_1).await.unwrap(),
361        &[Contact::Accepted {
362            user_id: user_2,
363            busy: false,
364        }],
365    );
366    assert!(db.has_contact(user_1, user_2).await.unwrap());
367    assert!(db.has_contact(user_2, user_1).await.unwrap());
368    assert_eq!(
369        db.get_contacts(user_2).await.unwrap(),
370        &[Contact::Accepted {
371            user_id: user_1,
372            busy: false,
373        }]
374    );
375
376    // Users cannot re-request existing contacts.
377    db.send_contact_request(user_1, user_2).await.unwrap_err();
378    db.send_contact_request(user_2, user_1).await.unwrap_err();
379
380    // Users can't dismiss notifications of them accepting other users' requests.
381    db.dismiss_contact_notification(user_2, user_1)
382        .await
383        .unwrap_err();
384    assert_eq!(
385        db.get_contacts(user_1).await.unwrap(),
386        &[Contact::Accepted {
387            user_id: user_2,
388            busy: false,
389        }]
390    );
391
392    // Users can dismiss notifications of other users accepting their requests.
393    db.dismiss_contact_notification(user_1, user_2)
394        .await
395        .unwrap();
396    assert_eq!(
397        db.get_contacts(user_1).await.unwrap(),
398        &[Contact::Accepted {
399            user_id: user_2,
400            busy: false,
401        }]
402    );
403
404    // Users send each other concurrent contact requests and
405    // see that they are immediately accepted.
406    db.send_contact_request(user_1, user_3).await.unwrap();
407    db.send_contact_request(user_3, user_1).await.unwrap();
408    assert_eq!(
409        db.get_contacts(user_1).await.unwrap(),
410        &[
411            Contact::Accepted {
412                user_id: user_2,
413                busy: false,
414            },
415            Contact::Accepted {
416                user_id: user_3,
417                busy: false,
418            }
419        ]
420    );
421    assert_eq!(
422        db.get_contacts(user_3).await.unwrap(),
423        &[Contact::Accepted {
424            user_id: user_1,
425            busy: false,
426        }],
427    );
428
429    // User declines a contact request. Both users see that it is gone.
430    db.send_contact_request(user_2, user_3).await.unwrap();
431    db.respond_to_contact_request(user_3, user_2, false)
432        .await
433        .unwrap();
434    assert!(!db.has_contact(user_2, user_3).await.unwrap());
435    assert!(!db.has_contact(user_3, user_2).await.unwrap());
436    assert_eq!(
437        db.get_contacts(user_2).await.unwrap(),
438        &[Contact::Accepted {
439            user_id: user_1,
440            busy: false,
441        }]
442    );
443    assert_eq!(
444        db.get_contacts(user_3).await.unwrap(),
445        &[Contact::Accepted {
446            user_id: user_1,
447            busy: false,
448        }],
449    );
450}
451
452test_both_dbs!(
453    test_metrics_id,
454    test_metrics_id_postgres,
455    test_metrics_id_sqlite
456);
457
458async fn test_metrics_id(db: &Arc<Database>) {
459    let NewUserResult {
460        user_id: user1,
461        metrics_id: metrics_id1,
462        ..
463    } = db
464        .create_user(
465            "person1@example.com",
466            false,
467            NewUserParams {
468                github_login: "person1".into(),
469                github_user_id: 101,
470            },
471        )
472        .await
473        .unwrap();
474    let NewUserResult {
475        user_id: user2,
476        metrics_id: metrics_id2,
477        ..
478    } = db
479        .create_user(
480            "person2@example.com",
481            false,
482            NewUserParams {
483                github_login: "person2".into(),
484                github_user_id: 102,
485            },
486        )
487        .await
488        .unwrap();
489
490    assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1);
491    assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2);
492    assert_eq!(metrics_id1.len(), 36);
493    assert_eq!(metrics_id2.len(), 36);
494    assert_ne!(metrics_id1, metrics_id2);
495}
496
497test_both_dbs!(
498    test_project_count,
499    test_project_count_postgres,
500    test_project_count_sqlite
501);
502
503async fn test_project_count(db: &Arc<Database>) {
504    let owner_id = db.create_server("test").await.unwrap().0 as u32;
505
506    let user1 = db
507        .create_user(
508            "admin@example.com",
509            true,
510            NewUserParams {
511                github_login: "admin".into(),
512                github_user_id: 0,
513            },
514        )
515        .await
516        .unwrap();
517    let user2 = db
518        .create_user(
519            "user@example.com",
520            false,
521            NewUserParams {
522                github_login: "user".into(),
523                github_user_id: 1,
524            },
525        )
526        .await
527        .unwrap();
528
529    let room_id = RoomId::from_proto(
530        db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "")
531            .await
532            .unwrap()
533            .id,
534    );
535    db.call(
536        room_id,
537        user1.user_id,
538        ConnectionId { owner_id, id: 0 },
539        user2.user_id,
540        None,
541    )
542    .await
543    .unwrap();
544    db.join_room(room_id, user2.user_id, ConnectionId { owner_id, id: 1 })
545        .await
546        .unwrap();
547    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
548
549    db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None)
550        .await
551        .unwrap();
552    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
553
554    db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None)
555        .await
556        .unwrap();
557    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
558
559    // Projects shared by admins aren't counted.
560    db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], None)
561        .await
562        .unwrap();
563    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
564
565    db.leave_room(ConnectionId { owner_id, id: 1 })
566        .await
567        .unwrap();
568    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
569}
570
571#[test]
572fn test_fuzzy_like_string() {
573    assert_eq!(Database::fuzzy_like_string("abcd"), "%a%b%c%d%");
574    assert_eq!(Database::fuzzy_like_string("x y"), "%x%y%");
575    assert_eq!(Database::fuzzy_like_string(" z  "), "%z%");
576}
577
578#[cfg(target_os = "macos")]
579#[gpui::test]
580async fn test_fuzzy_search_users(cx: &mut gpui::TestAppContext) {
581    let test_db = tests::TestDb::postgres(cx.executor());
582    let db = test_db.db();
583    for (i, github_login) in [
584        "California",
585        "colorado",
586        "oregon",
587        "washington",
588        "florida",
589        "delaware",
590        "rhode-island",
591    ]
592    .into_iter()
593    .enumerate()
594    {
595        db.create_user(
596            &format!("{github_login}@example.com"),
597            false,
598            NewUserParams {
599                github_login: github_login.into(),
600                github_user_id: i as i32,
601            },
602        )
603        .await
604        .unwrap();
605    }
606
607    assert_eq!(
608        fuzzy_search_user_names(db, "clr").await,
609        &["colorado", "California"]
610    );
611    assert_eq!(
612        fuzzy_search_user_names(db, "ro").await,
613        &["rhode-island", "colorado", "oregon"],
614    );
615
616    async fn fuzzy_search_user_names(db: &Database, query: &str) -> Vec<String> {
617        db.fuzzy_search_users(query, 10)
618            .await
619            .unwrap()
620            .into_iter()
621            .map(|user| user.github_login)
622            .collect::<Vec<_>>()
623    }
624}