Merge pull request #1897 from zed-industries/allow-users-to-sign-up-multiple-times

Joseph T. Lyons created

Change summary

crates/collab/src/api.rs      |  2 
crates/collab/src/db.rs       | 10 ++++---
crates/collab/src/db_tests.rs | 45 +++++++++++++++++++++++-------------
3 files changed, 35 insertions(+), 22 deletions(-)

Detailed changes

crates/collab/src/api.rs 🔗

@@ -338,7 +338,7 @@ async fn create_signup(
     Json(params): Json<Signup>,
     Extension(app): Extension<Arc<AppState>>,
 ) -> Result<()> {
-    app.db.create_signup(params).await?;
+    app.db.create_signup(&params).await?;
     Ok(())
 }
 

crates/collab/src/db.rs 🔗

@@ -157,7 +157,7 @@ impl Db<sqlx::Sqlite> {
         unimplemented!()
     }
 
-    pub async fn create_signup(&self, _signup: Signup) -> Result<()> {
+    pub async fn create_signup(&self, _signup: &Signup) -> Result<()> {
         unimplemented!()
     }
 
@@ -375,7 +375,7 @@ impl Db<sqlx::Postgres> {
         })
     }
 
-    pub async fn create_signup(&self, signup: Signup) -> Result<()> {
+    pub async fn create_signup(&self, signup: &Signup) -> Result<()> {
         test_support!(self, {
             sqlx::query(
                 "
@@ -394,6 +394,8 @@ impl Db<sqlx::Postgres> {
                 )
                 VALUES
                     ($1, $2, FALSE, $3, $4, $5, FALSE, $6, $7, $8)
+                ON CONFLICT (email_address) DO UPDATE SET
+                    email_address = excluded.email_address
                 RETURNING id
                 ",
             )
@@ -1259,7 +1261,7 @@ pub struct IncomingContactRequest {
     pub should_notify: bool,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, Default)]
 pub struct Signup {
     pub email_address: String,
     pub platform_mac: bool,
@@ -1284,7 +1286,7 @@ pub struct WaitlistSummary {
     pub unknown_count: i64,
 }
 
-#[derive(FromRow, PartialEq, Debug, Serialize, Deserialize)]
+#[derive(Clone, FromRow, PartialEq, Debug, Serialize, Deserialize)]
 pub struct Invite {
     pub email_address: String,
     pub email_confirmation_code: String,

crates/collab/src/db_tests.rs 🔗

@@ -644,10 +644,14 @@ async fn test_signups() {
     let test_db = PostgresTestDb::new(build_background_executor());
     let db = test_db.db();
 
+    let usernames = (0..8).map(|i| format!("person-{i}")).collect::<Vec<_>>();
+
     // people sign up on the waitlist
-    for i in 0..8 {
-        db.create_signup(Signup {
-            email_address: format!("person-{i}@example.com"),
+    let all_signups = usernames
+        .iter()
+        .enumerate()
+        .map(|(i, username)| Signup {
+            email_address: format!("{username}@example.com"),
             platform_mac: true,
             platform_linux: i % 2 == 0,
             platform_windows: i % 4 == 0,
@@ -655,8 +659,13 @@ async fn test_signups() {
             programming_languages: vec!["rust".into(), "c".into()],
             device_id: Some(format!("device_id_{i}")),
         })
-        .await
-        .unwrap();
+        .collect::<Vec<Signup>>();
+
+    for signup in &all_signups {
+        // Users can sign up multiple times without issues
+        for _ in 0..2 {
+            db.create_signup(&signup).await.unwrap();
+        }
     }
 
     assert_eq!(
@@ -679,9 +688,9 @@ async fn test_signups() {
     assert_eq!(
         addresses,
         &[
-            "person-0@example.com",
-            "person-1@example.com",
-            "person-2@example.com"
+            all_signups[0].email_address.as_str(),
+            all_signups[1].email_address.as_str(),
+            all_signups[2].email_address.as_str()
         ]
     );
     assert_ne!(
@@ -705,9 +714,9 @@ async fn test_signups() {
     assert_eq!(
         addresses,
         &[
-            "person-3@example.com",
-            "person-4@example.com",
-            "person-5@example.com"
+            all_signups[3].email_address.as_str(),
+            all_signups[4].email_address.as_str(),
+            all_signups[5].email_address.as_str()
         ]
     );
 
@@ -733,11 +742,10 @@ async fn test_signups() {
     } = db
         .create_user_from_invite(
             &Invite {
-                email_address: signups_batch1[0].email_address.clone(),
-                email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(),
+                ..signups_batch1[0].clone()
             },
             NewUserParams {
-                github_login: "person-0".into(),
+                github_login: usernames[0].clone(),
                 github_user_id: 0,
                 invite_count: 5,
             },
@@ -747,8 +755,11 @@ async fn test_signups() {
         .unwrap();
     let user = db.get_user_by_id(user_id).await.unwrap().unwrap();
     assert!(inviting_user_id.is_none());
-    assert_eq!(user.github_login, "person-0");
-    assert_eq!(user.email_address.as_deref(), Some("person-0@example.com"));
+    assert_eq!(user.github_login, usernames[0]);
+    assert_eq!(
+        user.email_address,
+        Some(all_signups[0].email_address.clone())
+    );
     assert_eq!(user.invite_count, 5);
     assert_eq!(signup_device_id.unwrap(), "device_id_0");
 
@@ -776,7 +787,7 @@ async fn test_signups() {
             email_confirmation_code: "the-wrong-code".to_string(),
         },
         NewUserParams {
-            github_login: "person-1".into(),
+            github_login: usernames[1].clone(),
             github_user_id: 2,
             invite_count: 5,
         },