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