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                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_metrics_id,
462    test_metrics_id_postgres,
463    test_metrics_id_sqlite
464);
465
466async fn test_metrics_id(db: &Arc<Database>) {
467    let NewUserResult {
468        user_id: user1,
469        metrics_id: metrics_id1,
470        ..
471    } = db
472        .create_user(
473            "person1@example.com",
474            None,
475            false,
476            NewUserParams {
477                github_login: "person1".into(),
478                github_user_id: 101,
479            },
480        )
481        .await
482        .unwrap();
483    let NewUserResult {
484        user_id: user2,
485        metrics_id: metrics_id2,
486        ..
487    } = db
488        .create_user(
489            "person2@example.com",
490            None,
491            false,
492            NewUserParams {
493                github_login: "person2".into(),
494                github_user_id: 102,
495            },
496        )
497        .await
498        .unwrap();
499
500    assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1);
501    assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2);
502    assert_eq!(metrics_id1.len(), 36);
503    assert_eq!(metrics_id2.len(), 36);
504    assert_ne!(metrics_id1, metrics_id2);
505}
506
507test_both_dbs!(
508    test_project_count,
509    test_project_count_postgres,
510    test_project_count_sqlite
511);
512
513async fn test_project_count(db: &Arc<Database>) {
514    let owner_id = db.create_server("test").await.unwrap().0 as u32;
515
516    let user1 = db
517        .create_user(
518            "admin@example.com",
519            None,
520            true,
521            NewUserParams {
522                github_login: "admin".into(),
523                github_user_id: 0,
524            },
525        )
526        .await
527        .unwrap();
528    let user2 = db
529        .create_user(
530            "user@example.com",
531            None,
532            false,
533            NewUserParams {
534                github_login: "user".into(),
535                github_user_id: 1,
536            },
537        )
538        .await
539        .unwrap();
540
541    let room_id = RoomId::from_proto(
542        db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "")
543            .await
544            .unwrap()
545            .id,
546    );
547    db.call(
548        room_id,
549        user1.user_id,
550        ConnectionId { owner_id, id: 0 },
551        user2.user_id,
552        None,
553    )
554    .await
555    .unwrap();
556    db.join_room(room_id, user2.user_id, ConnectionId { owner_id, id: 1 })
557        .await
558        .unwrap();
559    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
560
561    db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, false)
562        .await
563        .unwrap();
564    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
565
566    db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, false)
567        .await
568        .unwrap();
569    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
570
571    // Projects shared by admins aren't counted.
572    db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false, false)
573        .await
574        .unwrap();
575    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
576
577    db.leave_room(ConnectionId { owner_id, id: 1 })
578        .await
579        .unwrap();
580    assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
581}
582
583#[test]
584fn test_fuzzy_like_string() {
585    assert_eq!(Database::fuzzy_like_string("abcd"), "%a%b%c%d%");
586    assert_eq!(Database::fuzzy_like_string("x y"), "%x%y%");
587    assert_eq!(Database::fuzzy_like_string(" z  "), "%z%");
588}
589
590#[cfg(target_os = "macos")]
591#[gpui::test]
592async fn test_fuzzy_search_users(cx: &mut gpui::TestAppContext) {
593    let test_db = tests::TestDb::postgres(cx.executor());
594    let db = test_db.db();
595    for (i, github_login) in [
596        "California",
597        "colorado",
598        "oregon",
599        "washington",
600        "florida",
601        "delaware",
602        "rhode-island",
603    ]
604    .into_iter()
605    .enumerate()
606    {
607        db.create_user(
608            &format!("{github_login}@example.com"),
609            None,
610            false,
611            NewUserParams {
612                github_login: github_login.into(),
613                github_user_id: i as i32,
614            },
615        )
616        .await
617        .unwrap();
618    }
619
620    assert_eq!(
621        fuzzy_search_user_names(db, "clr").await,
622        &["colorado", "California"]
623    );
624    assert_eq!(
625        fuzzy_search_user_names(db, "ro").await,
626        &["rhode-island", "colorado", "oregon"],
627    );
628
629    async fn fuzzy_search_user_names(db: &Database, query: &str) -> Vec<String> {
630        db.fuzzy_search_users(query, 10)
631            .await
632            .unwrap()
633            .into_iter()
634            .map(|user| user.github_login)
635            .collect::<Vec<_>>()
636    }
637}