tests.rs

  1use super::*;
  2use gpui::executor::{Background, Deterministic};
  3use std::sync::Arc;
  4
  5macro_rules! test_both_dbs {
  6    ($postgres_test_name:ident, $sqlite_test_name:ident, $db:ident, $body:block) => {
  7        #[gpui::test]
  8        async fn $postgres_test_name() {
  9            let test_db = TestDb::postgres(Deterministic::new(0).build_background());
 10            let $db = test_db.db();
 11            $body
 12        }
 13
 14        #[gpui::test]
 15        async fn $sqlite_test_name() {
 16            let test_db = TestDb::sqlite(Deterministic::new(0).build_background());
 17            let $db = test_db.db();
 18            $body
 19        }
 20    };
 21}
 22
 23test_both_dbs!(
 24    test_get_users_by_ids_postgres,
 25    test_get_users_by_ids_sqlite,
 26    db,
 27    {
 28        let mut user_ids = Vec::new();
 29        let mut user_metric_ids = Vec::new();
 30        for i in 1..=4 {
 31            let user = db
 32                .create_user(
 33                    &format!("user{i}@example.com"),
 34                    false,
 35                    NewUserParams {
 36                        github_login: format!("user{i}"),
 37                        github_user_id: i,
 38                        invite_count: 0,
 39                    },
 40                )
 41                .await
 42                .unwrap();
 43            user_ids.push(user.user_id);
 44            user_metric_ids.push(user.metrics_id);
 45        }
 46
 47        assert_eq!(
 48            db.get_users_by_ids(user_ids.clone()).await.unwrap(),
 49            vec![
 50                User {
 51                    id: user_ids[0],
 52                    github_login: "user1".to_string(),
 53                    github_user_id: Some(1),
 54                    email_address: Some("user1@example.com".to_string()),
 55                    admin: false,
 56                    metrics_id: user_metric_ids[0].parse().unwrap(),
 57                    ..Default::default()
 58                },
 59                User {
 60                    id: user_ids[1],
 61                    github_login: "user2".to_string(),
 62                    github_user_id: Some(2),
 63                    email_address: Some("user2@example.com".to_string()),
 64                    admin: false,
 65                    metrics_id: user_metric_ids[1].parse().unwrap(),
 66                    ..Default::default()
 67                },
 68                User {
 69                    id: user_ids[2],
 70                    github_login: "user3".to_string(),
 71                    github_user_id: Some(3),
 72                    email_address: Some("user3@example.com".to_string()),
 73                    admin: false,
 74                    metrics_id: user_metric_ids[2].parse().unwrap(),
 75                    ..Default::default()
 76                },
 77                User {
 78                    id: user_ids[3],
 79                    github_login: "user4".to_string(),
 80                    github_user_id: Some(4),
 81                    email_address: Some("user4@example.com".to_string()),
 82                    admin: false,
 83                    metrics_id: user_metric_ids[3].parse().unwrap(),
 84                    ..Default::default()
 85                }
 86            ]
 87        );
 88    }
 89);
 90
 91test_both_dbs!(
 92    test_get_user_by_github_account_postgres,
 93    test_get_user_by_github_account_sqlite,
 94    db,
 95    {
 96        let user_id1 = db
 97            .create_user(
 98                "user1@example.com",
 99                false,
100                NewUserParams {
101                    github_login: "login1".into(),
102                    github_user_id: 101,
103                    invite_count: 0,
104                },
105            )
106            .await
107            .unwrap()
108            .user_id;
109        let user_id2 = db
110            .create_user(
111                "user2@example.com",
112                false,
113                NewUserParams {
114                    github_login: "login2".into(),
115                    github_user_id: 102,
116                    invite_count: 0,
117                },
118            )
119            .await
120            .unwrap()
121            .user_id;
122
123        let user = db
124            .get_user_by_github_account("login1", None)
125            .await
126            .unwrap()
127            .unwrap();
128        assert_eq!(user.id, user_id1);
129        assert_eq!(&user.github_login, "login1");
130        assert_eq!(user.github_user_id, Some(101));
131
132        assert!(db
133            .get_user_by_github_account("non-existent-login", None)
134            .await
135            .unwrap()
136            .is_none());
137
138        let user = db
139            .get_user_by_github_account("the-new-login2", Some(102))
140            .await
141            .unwrap()
142            .unwrap();
143        assert_eq!(user.id, user_id2);
144        assert_eq!(&user.github_login, "the-new-login2");
145        assert_eq!(user.github_user_id, Some(102));
146    }
147);
148
149test_both_dbs!(
150    test_create_access_tokens_postgres,
151    test_create_access_tokens_sqlite,
152    db,
153    {
154        let user = db
155            .create_user(
156                "u1@example.com",
157                false,
158                NewUserParams {
159                    github_login: "u1".into(),
160                    github_user_id: 1,
161                    invite_count: 0,
162                },
163            )
164            .await
165            .unwrap()
166            .user_id;
167
168        db.create_access_token_hash(user, "h1", 3).await.unwrap();
169        db.create_access_token_hash(user, "h2", 3).await.unwrap();
170        assert_eq!(
171            db.get_access_token_hashes(user).await.unwrap(),
172            &["h2".to_string(), "h1".to_string()]
173        );
174
175        db.create_access_token_hash(user, "h3", 3).await.unwrap();
176        assert_eq!(
177            db.get_access_token_hashes(user).await.unwrap(),
178            &["h3".to_string(), "h2".to_string(), "h1".to_string(),]
179        );
180
181        db.create_access_token_hash(user, "h4", 3).await.unwrap();
182        assert_eq!(
183            db.get_access_token_hashes(user).await.unwrap(),
184            &["h4".to_string(), "h3".to_string(), "h2".to_string(),]
185        );
186
187        db.create_access_token_hash(user, "h5", 3).await.unwrap();
188        assert_eq!(
189            db.get_access_token_hashes(user).await.unwrap(),
190            &["h5".to_string(), "h4".to_string(), "h3".to_string()]
191        );
192    }
193);
194
195test_both_dbs!(test_add_contacts_postgres, test_add_contacts_sqlite, db, {
196    let mut user_ids = Vec::new();
197    for i in 0..3 {
198        user_ids.push(
199            db.create_user(
200                &format!("user{i}@example.com"),
201                false,
202                NewUserParams {
203                    github_login: format!("user{i}"),
204                    github_user_id: i,
205                    invite_count: 0,
206                },
207            )
208            .await
209            .unwrap()
210            .user_id,
211        );
212    }
213
214    let user_1 = user_ids[0];
215    let user_2 = user_ids[1];
216    let user_3 = user_ids[2];
217
218    // User starts with no contacts
219    assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
220
221    // User requests a contact. Both users see the pending request.
222    db.send_contact_request(user_1, user_2).await.unwrap();
223    assert!(!db.has_contact(user_1, user_2).await.unwrap());
224    assert!(!db.has_contact(user_2, user_1).await.unwrap());
225    assert_eq!(
226        db.get_contacts(user_1).await.unwrap(),
227        &[Contact::Outgoing { user_id: user_2 }],
228    );
229    assert_eq!(
230        db.get_contacts(user_2).await.unwrap(),
231        &[Contact::Incoming {
232            user_id: user_1,
233            should_notify: true
234        }]
235    );
236
237    // User 2 dismisses the contact request notification without accepting or rejecting.
238    // We shouldn't notify them again.
239    db.dismiss_contact_notification(user_1, user_2)
240        .await
241        .unwrap_err();
242    db.dismiss_contact_notification(user_2, user_1)
243        .await
244        .unwrap();
245    assert_eq!(
246        db.get_contacts(user_2).await.unwrap(),
247        &[Contact::Incoming {
248            user_id: user_1,
249            should_notify: false
250        }]
251    );
252
253    // User can't accept their own contact request
254    db.respond_to_contact_request(user_1, user_2, true)
255        .await
256        .unwrap_err();
257
258    // User accepts a contact request. Both users see the contact.
259    db.respond_to_contact_request(user_2, user_1, true)
260        .await
261        .unwrap();
262    assert_eq!(
263        db.get_contacts(user_1).await.unwrap(),
264        &[Contact::Accepted {
265            user_id: user_2,
266            should_notify: true,
267            busy: false,
268        }],
269    );
270    assert!(db.has_contact(user_1, user_2).await.unwrap());
271    assert!(db.has_contact(user_2, user_1).await.unwrap());
272    assert_eq!(
273        db.get_contacts(user_2).await.unwrap(),
274        &[Contact::Accepted {
275            user_id: user_1,
276            should_notify: false,
277            busy: false,
278        }]
279    );
280
281    // Users cannot re-request existing contacts.
282    db.send_contact_request(user_1, user_2).await.unwrap_err();
283    db.send_contact_request(user_2, user_1).await.unwrap_err();
284
285    // Users can't dismiss notifications of them accepting other users' requests.
286    db.dismiss_contact_notification(user_2, user_1)
287        .await
288        .unwrap_err();
289    assert_eq!(
290        db.get_contacts(user_1).await.unwrap(),
291        &[Contact::Accepted {
292            user_id: user_2,
293            should_notify: true,
294            busy: false,
295        }]
296    );
297
298    // Users can dismiss notifications of other users accepting their requests.
299    db.dismiss_contact_notification(user_1, user_2)
300        .await
301        .unwrap();
302    assert_eq!(
303        db.get_contacts(user_1).await.unwrap(),
304        &[Contact::Accepted {
305            user_id: user_2,
306            should_notify: false,
307            busy: false,
308        }]
309    );
310
311    // Users send each other concurrent contact requests and
312    // see that they are immediately accepted.
313    db.send_contact_request(user_1, user_3).await.unwrap();
314    db.send_contact_request(user_3, user_1).await.unwrap();
315    assert_eq!(
316        db.get_contacts(user_1).await.unwrap(),
317        &[
318            Contact::Accepted {
319                user_id: user_2,
320                should_notify: false,
321                busy: false,
322            },
323            Contact::Accepted {
324                user_id: user_3,
325                should_notify: false,
326                busy: false,
327            }
328        ]
329    );
330    assert_eq!(
331        db.get_contacts(user_3).await.unwrap(),
332        &[Contact::Accepted {
333            user_id: user_1,
334            should_notify: false,
335            busy: false,
336        }],
337    );
338
339    // User declines a contact request. Both users see that it is gone.
340    db.send_contact_request(user_2, user_3).await.unwrap();
341    db.respond_to_contact_request(user_3, user_2, false)
342        .await
343        .unwrap();
344    assert!(!db.has_contact(user_2, user_3).await.unwrap());
345    assert!(!db.has_contact(user_3, user_2).await.unwrap());
346    assert_eq!(
347        db.get_contacts(user_2).await.unwrap(),
348        &[Contact::Accepted {
349            user_id: user_1,
350            should_notify: false,
351            busy: false,
352        }]
353    );
354    assert_eq!(
355        db.get_contacts(user_3).await.unwrap(),
356        &[Contact::Accepted {
357            user_id: user_1,
358            should_notify: false,
359            busy: false,
360        }],
361    );
362});
363
364test_both_dbs!(test_metrics_id_postgres, test_metrics_id_sqlite, db, {
365    let NewUserResult {
366        user_id: user1,
367        metrics_id: metrics_id1,
368        ..
369    } = db
370        .create_user(
371            "person1@example.com",
372            false,
373            NewUserParams {
374                github_login: "person1".into(),
375                github_user_id: 101,
376                invite_count: 5,
377            },
378        )
379        .await
380        .unwrap();
381    let NewUserResult {
382        user_id: user2,
383        metrics_id: metrics_id2,
384        ..
385    } = db
386        .create_user(
387            "person2@example.com",
388            false,
389            NewUserParams {
390                github_login: "person2".into(),
391                github_user_id: 102,
392                invite_count: 5,
393            },
394        )
395        .await
396        .unwrap();
397
398    assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1);
399    assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2);
400    assert_eq!(metrics_id1.len(), 36);
401    assert_eq!(metrics_id2.len(), 36);
402    assert_ne!(metrics_id1, metrics_id2);
403});
404
405test_both_dbs!(
406    test_project_count_postgres,
407    test_project_count_sqlite,
408    db,
409    {
410        let user1 = db
411            .create_user(
412                &format!("admin@example.com"),
413                true,
414                NewUserParams {
415                    github_login: "admin".into(),
416                    github_user_id: 0,
417                    invite_count: 0,
418                },
419            )
420            .await
421            .unwrap();
422        let user2 = db
423            .create_user(
424                &format!("user@example.com"),
425                false,
426                NewUserParams {
427                    github_login: "user".into(),
428                    github_user_id: 1,
429                    invite_count: 0,
430                },
431            )
432            .await
433            .unwrap();
434
435        let room_id = RoomId::from_proto(
436            db.create_room(user1.user_id, ConnectionId(0), "")
437                .await
438                .unwrap()
439                .id,
440        );
441        db.call(room_id, user1.user_id, ConnectionId(0), user2.user_id, None)
442            .await
443            .unwrap();
444        db.join_room(room_id, user2.user_id, ConnectionId(1))
445            .await
446            .unwrap();
447        assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
448
449        db.share_project(room_id, ConnectionId(1), &[])
450            .await
451            .unwrap();
452        assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
453
454        db.share_project(room_id, ConnectionId(1), &[])
455            .await
456            .unwrap();
457        assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
458
459        // Projects shared by admins aren't counted.
460        db.share_project(room_id, ConnectionId(0), &[])
461            .await
462            .unwrap();
463        assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
464
465        db.leave_room(ConnectionId(1)).await.unwrap();
466        assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
467    }
468);
469
470#[test]
471fn test_fuzzy_like_string() {
472    assert_eq!(Database::fuzzy_like_string("abcd"), "%a%b%c%d%");
473    assert_eq!(Database::fuzzy_like_string("x y"), "%x%y%");
474    assert_eq!(Database::fuzzy_like_string(" z  "), "%z%");
475}
476
477#[gpui::test]
478async fn test_fuzzy_search_users() {
479    let test_db = TestDb::postgres(build_background_executor());
480    let db = test_db.db();
481    for (i, github_login) in [
482        "California",
483        "colorado",
484        "oregon",
485        "washington",
486        "florida",
487        "delaware",
488        "rhode-island",
489    ]
490    .into_iter()
491    .enumerate()
492    {
493        db.create_user(
494            &format!("{github_login}@example.com"),
495            false,
496            NewUserParams {
497                github_login: github_login.into(),
498                github_user_id: i as i32,
499                invite_count: 0,
500            },
501        )
502        .await
503        .unwrap();
504    }
505
506    assert_eq!(
507        fuzzy_search_user_names(db, "clr").await,
508        &["colorado", "California"]
509    );
510    assert_eq!(
511        fuzzy_search_user_names(db, "ro").await,
512        &["rhode-island", "colorado", "oregon"],
513    );
514
515    async fn fuzzy_search_user_names(db: &Database, query: &str) -> Vec<String> {
516        db.fuzzy_search_users(query, 10)
517            .await
518            .unwrap()
519            .into_iter()
520            .map(|user| user.github_login)
521            .collect::<Vec<_>>()
522    }
523}
524
525#[gpui::test]
526async fn test_invite_codes() {
527    let test_db = TestDb::postgres(build_background_executor());
528    let db = test_db.db();
529
530    let NewUserResult { user_id: user1, .. } = db
531        .create_user(
532            "user1@example.com",
533            false,
534            NewUserParams {
535                github_login: "user1".into(),
536                github_user_id: 0,
537                invite_count: 0,
538            },
539        )
540        .await
541        .unwrap();
542
543    // Initially, user 1 has no invite code
544    assert_eq!(db.get_invite_code_for_user(user1).await.unwrap(), None);
545
546    // Setting invite count to 0 when no code is assigned does not assign a new code
547    db.set_invite_count_for_user(user1, 0).await.unwrap();
548    assert!(db.get_invite_code_for_user(user1).await.unwrap().is_none());
549
550    // User 1 creates an invite code that can be used twice.
551    db.set_invite_count_for_user(user1, 2).await.unwrap();
552    let (invite_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
553    assert_eq!(invite_count, 2);
554
555    // User 2 redeems the invite code and becomes a contact of user 1.
556    let user2_invite = db
557        .create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id"))
558        .await
559        .unwrap();
560    let NewUserResult {
561        user_id: user2,
562        inviting_user_id,
563        signup_device_id,
564        metrics_id,
565    } = db
566        .create_user_from_invite(
567            &user2_invite,
568            NewUserParams {
569                github_login: "user2".into(),
570                github_user_id: 2,
571                invite_count: 7,
572            },
573        )
574        .await
575        .unwrap()
576        .unwrap();
577    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
578    assert_eq!(invite_count, 1);
579    assert_eq!(inviting_user_id, Some(user1));
580    assert_eq!(signup_device_id.unwrap(), "user-2-device-id");
581    assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id);
582    assert_eq!(
583        db.get_contacts(user1).await.unwrap(),
584        [Contact::Accepted {
585            user_id: user2,
586            should_notify: true,
587            busy: false,
588        }]
589    );
590    assert_eq!(
591        db.get_contacts(user2).await.unwrap(),
592        [Contact::Accepted {
593            user_id: user1,
594            should_notify: false,
595            busy: false,
596        }]
597    );
598    assert_eq!(
599        db.get_invite_code_for_user(user2).await.unwrap().unwrap().1,
600        7
601    );
602
603    // User 3 redeems the invite code and becomes a contact of user 1.
604    let user3_invite = db
605        .create_invite_from_code(&invite_code, "user3@example.com", None)
606        .await
607        .unwrap();
608    let NewUserResult {
609        user_id: user3,
610        inviting_user_id,
611        signup_device_id,
612        ..
613    } = db
614        .create_user_from_invite(
615            &user3_invite,
616            NewUserParams {
617                github_login: "user-3".into(),
618                github_user_id: 3,
619                invite_count: 3,
620            },
621        )
622        .await
623        .unwrap()
624        .unwrap();
625    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
626    assert_eq!(invite_count, 0);
627    assert_eq!(inviting_user_id, Some(user1));
628    assert!(signup_device_id.is_none());
629    assert_eq!(
630        db.get_contacts(user1).await.unwrap(),
631        [
632            Contact::Accepted {
633                user_id: user2,
634                should_notify: true,
635                busy: false,
636            },
637            Contact::Accepted {
638                user_id: user3,
639                should_notify: true,
640                busy: false,
641            }
642        ]
643    );
644    assert_eq!(
645        db.get_contacts(user3).await.unwrap(),
646        [Contact::Accepted {
647            user_id: user1,
648            should_notify: false,
649            busy: false,
650        }]
651    );
652    assert_eq!(
653        db.get_invite_code_for_user(user3).await.unwrap().unwrap().1,
654        3
655    );
656
657    // Trying to reedem the code for the third time results in an error.
658    db.create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id"))
659        .await
660        .unwrap_err();
661
662    // Invite count can be updated after the code has been created.
663    db.set_invite_count_for_user(user1, 2).await.unwrap();
664    let (latest_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
665    assert_eq!(latest_code, invite_code); // Invite code doesn't change when we increment above 0
666    assert_eq!(invite_count, 2);
667
668    // User 4 can now redeem the invite code and becomes a contact of user 1.
669    let user4_invite = db
670        .create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id"))
671        .await
672        .unwrap();
673    let user4 = db
674        .create_user_from_invite(
675            &user4_invite,
676            NewUserParams {
677                github_login: "user-4".into(),
678                github_user_id: 4,
679                invite_count: 5,
680            },
681        )
682        .await
683        .unwrap()
684        .unwrap()
685        .user_id;
686
687    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
688    assert_eq!(invite_count, 1);
689    assert_eq!(
690        db.get_contacts(user1).await.unwrap(),
691        [
692            Contact::Accepted {
693                user_id: user2,
694                should_notify: true,
695                busy: false,
696            },
697            Contact::Accepted {
698                user_id: user3,
699                should_notify: true,
700                busy: false,
701            },
702            Contact::Accepted {
703                user_id: user4,
704                should_notify: true,
705                busy: false,
706            }
707        ]
708    );
709    assert_eq!(
710        db.get_contacts(user4).await.unwrap(),
711        [Contact::Accepted {
712            user_id: user1,
713            should_notify: false,
714            busy: false,
715        }]
716    );
717    assert_eq!(
718        db.get_invite_code_for_user(user4).await.unwrap().unwrap().1,
719        5
720    );
721
722    // An existing user cannot redeem invite codes.
723    db.create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id"))
724        .await
725        .unwrap_err();
726    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
727    assert_eq!(invite_count, 1);
728}
729
730#[gpui::test]
731async fn test_signups() {
732    let test_db = TestDb::postgres(build_background_executor());
733    let db = test_db.db();
734
735    let usernames = (0..8).map(|i| format!("person-{i}")).collect::<Vec<_>>();
736
737    let all_signups = usernames
738        .iter()
739        .enumerate()
740        .map(|(i, username)| NewSignup {
741            email_address: format!("{username}@example.com"),
742            platform_mac: true,
743            platform_linux: i % 2 == 0,
744            platform_windows: i % 4 == 0,
745            editor_features: vec!["speed".into()],
746            programming_languages: vec!["rust".into(), "c".into()],
747            device_id: Some(format!("device_id_{i}")),
748            added_to_mailing_list: i != 0, // One user failed to subscribe
749        })
750        .collect::<Vec<NewSignup>>();
751
752    // people sign up on the waitlist
753    for signup in &all_signups {
754        // users can sign up multiple times without issues
755        for _ in 0..2 {
756            db.create_signup(&signup).await.unwrap();
757        }
758    }
759
760    assert_eq!(
761        db.get_waitlist_summary().await.unwrap(),
762        WaitlistSummary {
763            count: 8,
764            mac_count: 8,
765            linux_count: 4,
766            windows_count: 2,
767            unknown_count: 0,
768        }
769    );
770
771    // retrieve the next batch of signup emails to send
772    let signups_batch1 = db.get_unsent_invites(3).await.unwrap();
773    let addresses = signups_batch1
774        .iter()
775        .map(|s| &s.email_address)
776        .collect::<Vec<_>>();
777    assert_eq!(
778        addresses,
779        &[
780            all_signups[0].email_address.as_str(),
781            all_signups[1].email_address.as_str(),
782            all_signups[2].email_address.as_str()
783        ]
784    );
785    assert_ne!(
786        signups_batch1[0].email_confirmation_code,
787        signups_batch1[1].email_confirmation_code
788    );
789
790    // the waitlist isn't updated until we record that the emails
791    // were successfully sent.
792    let signups_batch = db.get_unsent_invites(3).await.unwrap();
793    assert_eq!(signups_batch, signups_batch1);
794
795    // once the emails go out, we can retrieve the next batch
796    // of signups.
797    db.record_sent_invites(&signups_batch1).await.unwrap();
798    let signups_batch2 = db.get_unsent_invites(3).await.unwrap();
799    let addresses = signups_batch2
800        .iter()
801        .map(|s| &s.email_address)
802        .collect::<Vec<_>>();
803    assert_eq!(
804        addresses,
805        &[
806            all_signups[3].email_address.as_str(),
807            all_signups[4].email_address.as_str(),
808            all_signups[5].email_address.as_str()
809        ]
810    );
811
812    // the sent invites are excluded from the summary.
813    assert_eq!(
814        db.get_waitlist_summary().await.unwrap(),
815        WaitlistSummary {
816            count: 5,
817            mac_count: 5,
818            linux_count: 2,
819            windows_count: 1,
820            unknown_count: 0,
821        }
822    );
823
824    // user completes the signup process by providing their
825    // github account.
826    let NewUserResult {
827        user_id,
828        inviting_user_id,
829        signup_device_id,
830        ..
831    } = db
832        .create_user_from_invite(
833            &Invite {
834                ..signups_batch1[0].clone()
835            },
836            NewUserParams {
837                github_login: usernames[0].clone(),
838                github_user_id: 0,
839                invite_count: 5,
840            },
841        )
842        .await
843        .unwrap()
844        .unwrap();
845    let user = db.get_user_by_id(user_id).await.unwrap().unwrap();
846    assert!(inviting_user_id.is_none());
847    assert_eq!(user.github_login, usernames[0]);
848    assert_eq!(
849        user.email_address,
850        Some(all_signups[0].email_address.clone())
851    );
852    assert_eq!(user.invite_count, 5);
853    assert_eq!(signup_device_id.unwrap(), "device_id_0");
854
855    // cannot redeem the same signup again.
856    assert!(db
857        .create_user_from_invite(
858            &Invite {
859                email_address: signups_batch1[0].email_address.clone(),
860                email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(),
861            },
862            NewUserParams {
863                github_login: "some-other-github_account".into(),
864                github_user_id: 1,
865                invite_count: 5,
866            },
867        )
868        .await
869        .unwrap()
870        .is_none());
871
872    // cannot redeem a signup with the wrong confirmation code.
873    db.create_user_from_invite(
874        &Invite {
875            email_address: signups_batch1[1].email_address.clone(),
876            email_confirmation_code: "the-wrong-code".to_string(),
877        },
878        NewUserParams {
879            github_login: usernames[1].clone(),
880            github_user_id: 2,
881            invite_count: 5,
882        },
883    )
884    .await
885    .unwrap_err();
886}
887
888fn build_background_executor() -> Arc<Background> {
889    Deterministic::new(0).build_background()
890}