From df9c71edb29471fd54513ca8643bf5b327c82b1d Mon Sep 17 00:00:00 2001 From: Zack <33050391+speed2exe@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:05:25 +0800 Subject: [PATCH] feat: returns if user is new for both oauth and password login (#67) * feat: returns if user is new for both oauth and password login * test: add test for firt time sign_in and subsequent * chore: remove uneeded use * fix: compile --------- Co-authored-by: nathan --- .github/workflows/docker.yml | 10 +--- ...4d64d5625b0fef0a7c14b1b751bd64567f9b7.json | 16 +++++++ ...931b50c481f29a36b0f9264c1c1d4439f5935.json | 16 ------- build/init_registered_user.sh | 20 ++++++++ build/run_local_server.sh | 13 ++--- dev.env | 2 + libs/client-api/src/http.rs | 28 +++++------ libs/shared-entity/Cargo.toml | 4 +- libs/shared-entity/src/dto.rs | 14 ++++++ libs/storage/src/workspace.rs | 32 +++++++------ src/api/user.rs | 35 +++++++------- src/biz/user.rs | 25 +++++----- tests/client/sign_in.rs | 47 +++++++++++++------ tests/client/utils.rs | 7 ++- 14 files changed, 158 insertions(+), 111 deletions(-) create mode 100644 .sqlx/query-6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7.json delete mode 100644 .sqlx/query-6bbb6f2e06a63df25a7a50624f1931b50c481f29a36b0f9264c1c1d4439f5935.json create mode 100755 build/init_registered_user.sh diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f84f271a..45c5713f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,14 +47,8 @@ jobs: docker-compose up -d sleep 5 # sometimes the gotrue server may not be ready yet - source .env - curl localhost:9998/signup \ - --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_1"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_1"'"}' \ - --header 'Content-Type: application/json' - source .env - curl localhost:9998/signup \ - --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_2"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_2"'"}' \ - --header 'Content-Type: application/json' + + ./build/init_registered_user.sh # revert to require signup email verification export GOTRUE_MAILER_AUTOCONFIRM=false diff --git a/.sqlx/query-6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7.json b/.sqlx/query-6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7.json new file mode 100644 index 00000000..cfdd1ee6 --- /dev/null +++ b/.sqlx/query-6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO af_user (uuid, email, name)\n SELECT $1, $2, $3\n WHERE NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE email = $2\n )\n AND NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE uuid = $1\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7" +} diff --git a/.sqlx/query-6bbb6f2e06a63df25a7a50624f1931b50c481f29a36b0f9264c1c1d4439f5935.json b/.sqlx/query-6bbb6f2e06a63df25a7a50624f1931b50c481f29a36b0f9264c1c1d4439f5935.json deleted file mode 100644 index 57f328db..00000000 --- a/.sqlx/query-6bbb6f2e06a63df25a7a50624f1931b50c481f29a36b0f9264c1c1d4439f5935.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO af_user (uuid, email, name)\n SELECT $1, $2, $3\n WHERE NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE email = $2\n )\n AND NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE uuid = $1\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "6bbb6f2e06a63df25a7a50624f1931b50c481f29a36b0f9264c1c1d4439f5935" -} diff --git a/build/init_registered_user.sh b/build/init_registered_user.sh new file mode 100755 index 00000000..1fb4b713 --- /dev/null +++ b/build/init_registered_user.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -x +set -eo pipefail + +cd "$(dirname "$0")/.." + +source .env +# register user 1 +curl localhost:9998/signup \ + --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_1"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_1"'"}' \ + --header 'Content-Type: application/json' +# register user 2 +curl localhost:9998/signup \ + --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_2"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_2"'"}' \ + --header 'Content-Type: application/json' +# register user 3 +curl localhost:9998/signup \ + --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_3"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_3"'"}' \ + --header 'Content-Type: application/json' + diff --git a/build/run_local_server.sh b/build/run_local_server.sh index 6f8f335d..49fb0ebb 100755 --- a/build/run_local_server.sh +++ b/build/run_local_server.sh @@ -46,18 +46,11 @@ pkill -f appflowy_cloud || true cargo sqlx database create && cargo sqlx migrate run && cargo sqlx prepare --workspace RUST_LOG=trace cargo run & - # sometimes the gotrue server may not be ready yet sleep 1 -source .env -# register user 1 -curl localhost:9998/signup \ - --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_1"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_1"'"}' \ - --header 'Content-Type: application/json' -# register user 2 -curl localhost:9998/signup \ - --data-raw '{"email":"'"$GOTRUE_REGISTERED_EMAIL_2"'","password":"'"$GOTRUE_REGISTERED_PASSWORD_2"'"}' \ - --header 'Content-Type: application/json' + +# created registered user +./build/init_registered_user.sh # revert to require signup email verification export GOTRUE_MAILER_AUTOCONFIRM=false diff --git a/dev.env b/dev.env index bec78d19..c88be601 100644 --- a/dev.env +++ b/dev.env @@ -24,6 +24,8 @@ GOTRUE_REGISTERED_EMAIL_1=user1@example.com GOTRUE_REGISTERED_PASSWORD_1=Password123! GOTRUE_REGISTERED_EMAIL_2=user2@example.com GOTRUE_REGISTERED_PASSWORD_2=Password456! +GOTRUE_REGISTERED_EMAIL_3=user3@example.com +GOTRUE_REGISTERED_PASSWORD_3=Password789! # url to the postgres database DATABASE_URL=postgres://postgres:password@localhost:5433/postgres diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 1cdf6539..ce9baf2f 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -4,6 +4,8 @@ use reqwest::Method; use reqwest::RequestBuilder; use shared_entity::data::AppResponse; use shared_entity::dto::SignInParams; +use shared_entity::dto::SignInPasswordResponse; +use shared_entity::dto::SignInTokenResponse; use shared_entity::dto::UserUpdateParams; use shared_entity::dto::WorkspaceMembersParams; use std::time::SystemTime; @@ -41,7 +43,7 @@ impl Client { } // e.g. appflowy-flutter://#access_token=...&expires_in=3600&provider_token=...&refresh_token=...&token_type=bearer - pub async fn sign_in_url(&mut self, url: &str) -> Result<(), AppError> { + pub async fn sign_in_url(&mut self, url: &str) -> Result { let mut access_token: Option = None; let mut token_type: Option = None; let mut expires_in: Option = None; @@ -84,7 +86,7 @@ impl Client { })?; let access_token = access_token.ok_or(url_missing_param("access_token"))?; - let user = self.user_info(&access_token).await?; + let (user, new) = self.sign_in_token(&access_token).await?; self.token.set(AccessTokenResponse { access_token, @@ -97,16 +99,14 @@ impl Client { provider_refresh_token, }); - Ok(()) + Ok(new) } - pub async fn user_info(&self, access_token: &str) -> Result { - let url = format!("{}/api/user/info/{}", self.base_url, access_token); + pub async fn sign_in_token(&self, access_token: &str) -> Result<(User, bool), AppError> { + let url = format!("{}/api/user/sign_in/token/{}", self.base_url, access_token); let resp = self.http_client.get(&url).send().await?; - let user = AppResponse::::from_response(resp) - .await? - .into_data()?; - Ok(user) + let sign_in_resp: SignInTokenResponse = AppResponse::from_response(resp).await?.into_data()?; + Ok((sign_in_resp.user, sign_in_resp.is_new)) } pub fn token(&self) -> Option<&AccessTokenResponse> { @@ -205,17 +205,17 @@ impl Client { Ok(()) } - pub async fn sign_in_password(&mut self, email: &str, password: &str) -> Result<(), AppError> { + pub async fn sign_in_password(&mut self, email: &str, password: &str) -> Result { let url = format!("{}/api/user/sign_in/password", self.base_url); let params = SignInParams { email: email.to_owned(), password: password.to_owned(), }; let resp = self.http_client.post(&url).json(¶ms).send().await?; - self - .token - .set(AppResponse::from_response(resp).await?.into_data()?); - Ok(()) + let sign_in_resp: SignInPasswordResponse = + AppResponse::from_response(resp).await?.into_data()?; + self.token.set(sign_in_resp.access_token_resp); + Ok(sign_in_resp.is_new) } pub async fn refresh(&mut self) -> Result<(), AppError> { diff --git a/libs/shared-entity/Cargo.toml b/libs/shared-entity/Cargo.toml index 3844a492..54b5ce7c 100644 --- a/libs/shared-entity/Cargo.toml +++ b/libs/shared-entity/Cargo.toml @@ -13,13 +13,13 @@ serde_repr = "0.1.16" thiserror = "1.0.47" reqwest = "0.11.18" uuid = { version = "1.3.3", features = ["v4"] } +gotrue-entity = { path = "../gotrue-entity" } actix-web = { version = "4.4.0", default-features = false, features = ["http2"], optional = true } sqlx = { version = "0.7", default-features = false, features = ["postgres"], optional = true } validator = { version = "0.16", features = ["validator_derive", "derive"], optional = true } -gotrue-entity = { path = "../gotrue-entity", optional = true } opener = "0.6.1" url = "2.4.1" [features] -cloud = ["actix-web", "sqlx", "validator", "gotrue-entity"] +cloud = ["actix-web", "sqlx", "validator"] diff --git a/libs/shared-entity/src/dto.rs b/libs/shared-entity/src/dto.rs index f9342e49..e4736640 100644 --- a/libs/shared-entity/src/dto.rs +++ b/libs/shared-entity/src/dto.rs @@ -1,5 +1,7 @@ // Data Transfer Objects (DTO) +use gotrue_entity::{AccessTokenResponse, User}; + #[derive(serde::Deserialize, serde::Serialize)] pub struct WorkspaceMembersParams { pub workspace_uuid: uuid::Uuid, @@ -18,3 +20,15 @@ pub struct UserUpdateParams { pub password: String, pub name: Option, } + +#[derive(serde::Deserialize, serde::Serialize)] +pub struct SignInPasswordResponse { + pub access_token_resp: AccessTokenResponse, + pub is_new: bool, +} + +#[derive(serde::Deserialize, serde::Serialize)] +pub struct SignInTokenResponse { + pub user: User, + pub is_new: bool, +} diff --git a/libs/storage/src/workspace.rs b/libs/storage/src/workspace.rs index a396a130..989116d4 100644 --- a/libs/storage/src/workspace.rs +++ b/libs/storage/src/workspace.rs @@ -26,28 +26,30 @@ pub async fn update_user_name( pub async fn create_user_if_not_exists( pool: &PgPool, - gotrue_uuid: &uuid::Uuid, + user_uuid: &uuid::Uuid, email: &str, name: &str, -) -> Result<(), sqlx::Error> { - sqlx::query!( +) -> Result { + let affected_rows = sqlx::query!( r#" - INSERT INTO af_user (uuid, email, name) - SELECT $1, $2, $3 - WHERE NOT EXISTS ( - SELECT 1 FROM public.af_user WHERE email = $2 - ) - AND NOT EXISTS ( - SELECT 1 FROM public.af_user WHERE uuid = $1 - ) - "#, - gotrue_uuid, + INSERT INTO af_user (uuid, email, name) + SELECT $1, $2, $3 + WHERE NOT EXISTS ( + SELECT 1 FROM public.af_user WHERE email = $2 + ) + AND NOT EXISTS ( + SELECT 1 FROM public.af_user WHERE uuid = $1 + ) + "#, + user_uuid, email, name ) .execute(pool) - .await?; - Ok(()) + .await? + .rows_affected(); + + Ok(affected_rows > 0) } pub async fn get_user_id(pool: &PgPool, gotrue_uuid: &uuid::Uuid) -> Result { diff --git a/src/api/user.rs b/src/api/user.rs index df416ebe..53ae2ca2 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -9,7 +9,9 @@ use crate::domain::{UserEmail, UserName, UserPassword}; use crate::state::AppState; use gotrue_entity::{AccessTokenResponse, OAuthProvider, OAuthURL, User}; use shared_entity::data::{AppResponse, JsonAppResponse}; -use shared_entity::dto::{SignInParams, UserUpdateParams}; +use shared_entity::dto::{ + SignInParams, SignInPasswordResponse, SignInTokenResponse, UserUpdateParams, +}; use shared_entity::error::AppError; use shared_entity::error_code::ErrorCode; use storage_entity::AFUserProfileView; @@ -25,10 +27,10 @@ pub fn user_scope() -> Scope { // auth server integration .service(web::resource("/sign_up").route(web::post().to(sign_up_handler))) .service(web::resource("/sign_in/password").route(web::post().to(sign_in_password_handler))) + .service(web::resource("/sign_in/token/{access_token}").route(web::get().to(sign_in_token_handler))) .service(web::resource("/sign_out").route(web::post().to(sign_out_handler))) .service(web::resource("/update").route(web::post().to(update_handler))) .service(web::resource("/oauth/{provider}").route(web::get().to(oauth_handler))) - .service(web::resource("/info/{access_token}").route(web::get().to(info_handler))) .service(web::resource("/refresh/{refresh_token}").route(web::get().to(refresh_handler))) .service(web::resource("/profile").route(web::get().to(profile_handler))) @@ -48,13 +50,15 @@ async fn refresh_handler( Ok(AppResponse::Ok().with_data(oauth_url).into()) } -async fn info_handler( +async fn sign_in_token_handler( path: web::Path, state: Data, -) -> Result> { +) -> Result> { let access_token = path.into_inner(); - let user = biz::user::info(&state.pg_pool, &state.gotrue_client, &access_token).await?; - Ok(AppResponse::Ok().with_data(user).into()) + let (user, is_new) = + biz::user::sign_in_token(&state.pg_pool, &state.gotrue_client, &access_token).await?; + let resp = SignInTokenResponse { user, is_new }; + Ok(AppResponse::Ok().with_data(resp).into()) } async fn oauth_handler( @@ -110,17 +114,20 @@ async fn sign_out_handler( async fn sign_in_password_handler( req: Json, state: Data, -) -> Result> { +) -> Result> { let req = req.into_inner(); - let token = biz::user::sign_in( + let (token, new) = biz::user::sign_in_password( &state.pg_pool, &state.gotrue_client, req.email, req.password, ) .await?; - - Ok(AppResponse::Ok().with_data(token).into()) + let resp = SignInPasswordResponse { + access_token_resp: token, + is_new: new, + }; + Ok(AppResponse::Ok().with_data(resp).into()) } async fn sign_up_handler( @@ -128,13 +135,7 @@ async fn sign_up_handler( state: Data, ) -> Result> { let req = req.into_inner(); - biz::user::sign_up( - &state.pg_pool, - &state.gotrue_client, - req.email, - req.password, - ) - .await?; + biz::user::sign_up(&state.gotrue_client, req.email, req.password).await?; Ok(AppResponse::Ok().into()) } diff --git a/src/biz/user.rs b/src/biz/user.rs index 62e4d713..83c6b082 100644 --- a/src/biz/user.rs +++ b/src/biz/user.rs @@ -28,7 +28,6 @@ pub async fn refresh( #[instrument(level = "info", skip_all, err)] pub async fn sign_up( - pg_pool: &PgPool, gotrue_client: &Client, email: String, password: String, @@ -36,23 +35,20 @@ pub async fn sign_up( validate_email_password(&email, &password)?; let user = gotrue_client.sign_up(&email, &password).await??; tracing::info!("user sign up: {:?}", user); - if user.confirmed_at.is_some() { - let gotrue_uuid = uuid::Uuid::from_str(&user.id)?; - storage::workspace::create_user_if_not_exists(pg_pool, &gotrue_uuid, &user.email, "").await?; - } Ok(()) } -pub async fn info( +pub async fn sign_in_token( pg_pool: &PgPool, gotrue_client: &Client, access_token: &str, -) -> Result { +) -> Result<(User, bool), AppError> { let user = gotrue_client.user_info(access_token).await??; let user_uuid = uuid::Uuid::from_str(&user.id)?; let name: String = name_from_user_metadata(&user.user_metadata); - storage::workspace::create_user_if_not_exists(pg_pool, &user_uuid, &user.email, &name).await?; - Ok(user) + let new = + storage::workspace::create_user_if_not_exists(pg_pool, &user_uuid, &user.email, &name).await?; + Ok((user, new)) } pub async fn oauth(gotrue_client: &Client, provider: OAuthProvider) -> Result { @@ -81,18 +77,19 @@ pub async fn get_profile( } #[instrument(level = "info", skip_all, err)] -pub async fn sign_in( +pub async fn sign_in_password( pg_pool: &PgPool, gotrue_client: &Client, email: String, password: String, -) -> Result { +) -> Result<(AccessTokenResponse, bool), AppError> { let grant = Grant::Password(PasswordGrant { email, password }); let token = gotrue_client.token(&grant).await??; let gotrue_uuid = uuid::Uuid::from_str(&token.user.id)?; - storage::workspace::create_user_if_not_exists(pg_pool, &gotrue_uuid, &token.user.email, "") - .await?; - Ok(token) + let new = + storage::workspace::create_user_if_not_exists(pg_pool, &gotrue_uuid, &token.user.email, "") + .await?; + Ok((token, new)) } pub async fn update( diff --git a/tests/client/sign_in.rs b/tests/client/sign_in.rs index eb19eaac..98b40ba6 100644 --- a/tests/client/sign_in.rs +++ b/tests/client/sign_in.rs @@ -1,6 +1,6 @@ use shared_entity::error_code::ErrorCode; -use crate::client::utils::{generate_unique_email, REGISTERED_USERS, REGISTERED_USERS_MUTEX}; +use crate::client::utils::{generate_unique_email, REGISTERED_USERS}; use crate::client_api_client; #[tokio::test] @@ -45,20 +45,39 @@ async fn sign_in_unconfirmed_email() { #[tokio::test] async fn sign_in_success() { - let _guard = REGISTERED_USERS_MUTEX.lock().await; + { + // First Time + let mut c = client_api_client(); + let registered_user = ®ISTERED_USERS[2]; + let is_new = c + .sign_in_password(®istered_user.email, ®istered_user.password) + .await + .unwrap(); + assert!(is_new); + let token = c.token().unwrap(); + assert!(token.user.confirmed_at.is_some()); - let mut c = client_api_client(); - c.sign_in_password(®ISTERED_USERS[0].email, ®ISTERED_USERS[0].password) - .await - .unwrap(); - let token = c.token().unwrap(); - assert!(token.user.confirmed_at.is_some()); + let workspaces = c.workspaces().await.unwrap(); + assert_eq!(workspaces.0.len(), 1); + let profile = c.profile().await.unwrap(); + let latest_workspace = workspaces.get_latest(&profile); + assert!(latest_workspace.is_some()); + } - let workspaces = c.workspaces().await.unwrap(); - assert!(!workspaces.0.is_empty()); - let profile = c.profile().await.unwrap(); - let latest_workspace = workspaces.get_latest(&profile); - assert!(latest_workspace.is_some()); + { + // Subsequent Times + let mut c = client_api_client(); + let registered_user = ®ISTERED_USERS[2]; + let is_new = c + .sign_in_password(®istered_user.email, ®istered_user.password) + .await + .unwrap(); + assert!(!is_new); + + // workspaces should be the same + let workspaces = c.workspaces().await.unwrap(); + assert_eq!(workspaces.0.len(), 1); + } } #[tokio::test] @@ -66,7 +85,7 @@ async fn sign_in_with_url() { let url_str = "appflowy-flutter://#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTQ1ODIyMjMsInN1YiI6Ijk5MGM2NDNjLTMyMWEtNGNmMi04OWY1LTNhNmJhZGFjMTg5NCIsImVtYWlsIjoiNG5uaWhpbGF0ZWRAZ21haWwuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJnb29nbGUiLCJwcm92aWRlcnMiOlsiZ29vZ2xlIl19LCJ1c2VyX21ldGFkYXRhIjp7ImF2YXRhcl91cmwiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJdGZpa28xX0lpMmZiNzM4VnpGekViLVBqT0NCY3FUQzdrNjVIX0hnRTQwOVk9czk2LWMiLCJlbWFpbCI6IjRubmloaWxhdGVkQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmdWxsX25hbWUiOiJmdSB6aXhpYW5nIiwiaXNzIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vdXNlcmluZm8vdjIvbWUiLCJuYW1lIjoiZnUgeml4aWFuZyIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJdGZpa28xX0lpMmZiNzM4VnpGekViLVBqT0NCY3FUQzdrNjVIX0hnRTQwOVk9czk2LWMiLCJwcm92aWRlcl9pZCI6IjEwMTQ5OTYxMDMxOTYxNjE0NTcyNSIsInN1YiI6IjEwMTQ5OTYxMDMxOTYxNjE0NTcyNSJ9LCJyb2xlIjoiIn0.I-7j-Tdj62P56zhzEqvBc7cHMldv5MA_MM7xtrBibbE&expires_in=3600&provider_token=ya29.a0AfB_byCovXs1CUiC9_f9VBTupQPsIxwh9aSlOg0PLYJvv1x1zvVfssrQfW6_Aq9no7EKpCzFUCLElOvK1Xz4x4K5r7tug79tr5b1yiOoUMWTeWTXyV61fZHQbZ9vscAiyKYtq5NqYTiytHcQEFlKr7UMfu6BTbKsUwaCgYKAaISARISFQGOcNnC0Vsx2QCAXgYO3XbfcF91WQ0169&refresh_token=Hi3Jc3I_pj9YrexcR91i5g&token_type=bearer"; let mut c = client_api_client(); match c.sign_in_url(url_str).await { - Ok(()) => panic!("should not be ok"), + Ok(_) => panic!("should not be ok"), Err(e) => { assert_eq!(e.code, ErrorCode::OAuthError); assert!(e diff --git a/tests/client/utils.rs b/tests/client/utils.rs index 133d6e63..54d4d32b 100644 --- a/tests/client/utils.rs +++ b/tests/client/utils.rs @@ -6,7 +6,7 @@ use lazy_static::lazy_static; lazy_static! { pub static ref REGISTERED_USERS_MUTEX: Mutex<()> = Mutex::new(()); - pub static ref REGISTERED_USERS: [RegisteredUser; 2] = { + pub static ref REGISTERED_USERS: [RegisteredUser; 3] = { dotenv().ok(); [ RegisteredUser { @@ -17,6 +17,11 @@ lazy_static! { email: std::env::var("GOTRUE_REGISTERED_EMAIL_2").unwrap(), password: std::env::var("GOTRUE_REGISTERED_PASSWORD_2").unwrap(), }, + RegisteredUser { + // used for testing user sign up, and if they are new + email: std::env::var("GOTRUE_REGISTERED_EMAIL_3").unwrap(), + password: std::env::var("GOTRUE_REGISTERED_PASSWORD_3").unwrap(), + }, ] }; }