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