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