db_tests.rs

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