diff options
author | Toby Vincent <tobyv@tobyvin.dev> | 2024-05-07 16:45:13 -0500 |
---|---|---|
committer | Toby Vincent <tobyv@tobyvin.dev> | 2024-05-07 16:45:13 -0500 |
commit | b383010105b79fcd4e9d671ce82f5c04d0fc9b13 (patch) | |
tree | 2f28e21168d20ba717daa7e94a215c0e694ad500 /src/api/users.rs | |
parent | c55c5b3ccf9f06775fb00a2bcb2912f218691758 (diff) |
fix(api): improve user create flow and move ...
...claim based tests from user module into claims module.
Diffstat (limited to 'src/api/users.rs')
-rw-r--r-- | src/api/users.rs | 184 |
1 files changed, 57 insertions, 127 deletions
diff --git a/src/api/users.rs b/src/api/users.rs index e07bf7e..d4e5d57 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -46,36 +46,29 @@ pub async fn create( email, password, }): Json<Registration>, -) -> impl IntoResponse { +) -> Result<impl IntoResponse, Error> { email_address::EmailAddress::from_str(&email)?; - let exists: Option<bool> = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM user_ WHERE email = $1 LIMIT 1)", - email.to_ascii_lowercase() - ) - .fetch_one(&pool) - .await?; - - if exists.is_some_and(|b| b) { - return Err(Error::EmailExists); - } - - // TODO: Move this into a micro service, possibly behind a feature flag. - let (status, (access, refresh)) = - crate::auth::credentials::create(State(pool.clone()), Json(Credential { password })) - .await?; - let user = sqlx::query_as!( User, - "INSERT INTO user_ (id,name,email) VALUES ($1, $2, $3) RETURNING *", - refresh.sub, + "INSERT INTO user_ (name,email) VALUES ($1, $2) RETURNING *", name, email.to_ascii_lowercase(), ) .fetch_one(&pool) .await?; - Ok((status, access, refresh, Json(user))) + // TODO: Move this into a micro service, possibly behind a feature flag. + crate::auth::credentials::create( + State(pool.clone()), + Json(Credential { + id: user.id, + password, + }), + ) + .await + .map(|(status, claims)| (status, claims, Json(user))) + .map_err(Into::into) } pub async fn show( @@ -116,30 +109,31 @@ mod tests { const USER_EMAIL: &str = "adent@earth.sol"; const USER_PASSWORD: &str = "solongandthanksforallthefish"; - #[sqlx::test(fixtures(path = "../../fixtures", scripts("users")))] - async fn test_get_ok_self(pool: PgPool) -> TestResult { + #[sqlx::test] + async fn test_users_post_created(pool: PgPool) -> TestResult { setup_test_env(); let router = Router::new().merge(router()).with_state(AppState { pool }); + let user = serde_json::json!( { + "name": USER_NAME, + "email": USER_EMAIL, + "password": USER_PASSWORD, + }); + let request = Request::builder() - .uri(format!("/users/{}", USER_ID)) - .header( - COOKIE, - AccessClaims::issue(USER_ID).as_cookie()?.to_string(), - ) - .body(Body::empty())?; + .uri("/users") + .method("POST") + .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) + .body(Body::from(serde_json::to_vec(&user)?))?; let response = router.oneshot(request).await?; - assert_eq!(StatusCode::OK, response.status()); + assert_eq!(StatusCode::CREATED, response.status()); let body_bytes = response.into_body().collect().await?.to_bytes(); - let User { - id, name, email, .. - } = serde_json::from_slice(&body_bytes)?; + let User { name, email, .. } = serde_json::from_slice(&body_bytes)?; - assert_eq!(USER_ID, id); assert_eq!(USER_NAME, name); assert_eq!(USER_EMAIL, email); @@ -147,7 +141,32 @@ mod tests { } #[sqlx::test(fixtures(path = "../../fixtures", scripts("users")))] - async fn test_get_ok_other(pool: PgPool) -> TestResult { + async fn test_users_post_conflict(pool: PgPool) -> TestResult { + setup_test_env(); + + let router = Router::new().merge(router()).with_state(AppState { pool }); + + let user = serde_json::json!( { + "name": USER_NAME, + "email": USER_EMAIL, + "password": USER_PASSWORD, + }); + + let request = Request::builder() + .uri("/users") + .method("POST") + .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) + .body(Body::from(serde_json::to_vec(&user)?))?; + + let response = router.oneshot(request).await?; + + assert_eq!(StatusCode::CONFLICT, response.status()); + + Ok(()) + } + + #[sqlx::test(fixtures(path = "../../fixtures", scripts("users")))] + async fn test_users_get_ok(pool: PgPool) -> TestResult { setup_test_env(); let router = Router::new().merge(router()).with_state(AppState { pool }); @@ -179,7 +198,7 @@ mod tests { } #[sqlx::test] - async fn test_get_not_found(pool: PgPool) -> TestResult { + async fn test_users_get_not_found(pool: PgPool) -> TestResult { setup_test_env(); let router = Router::new().merge(router()).with_state(AppState { pool }); @@ -188,7 +207,9 @@ mod tests { .uri(format!("/users/{}", USER_ID)) .header( COOKIE, - AccessClaims::issue(USER_ID).as_cookie()?.to_string(), + AccessClaims::issue(uuid::Uuid::new_v4()) + .as_cookie()? + .to_string(), ) .body(Body::empty())?; @@ -198,95 +219,4 @@ mod tests { Ok(()) } - - #[sqlx::test] - async fn test_get_unauthorized_invalid_token_format(pool: PgPool) -> TestResult { - setup_test_env(); - - let router = Router::new().merge(router()).with_state(AppState { pool }); - - let request = Request::builder() - .uri(format!("/users/{}", USER_ID)) - .header(COOKIE, "token=sadfasdfsdfs") - .body(Body::empty())?; - - let response = router.oneshot(request).await?; - - assert_eq!(StatusCode::UNPROCESSABLE_ENTITY, response.status()); - - Ok(()) - } - - #[sqlx::test] - async fn test_get_unauthorized_missing_token(pool: PgPool) -> TestResult { - setup_test_env(); - - let router = Router::new().merge(router()).with_state(AppState { pool }); - - let request = Request::builder() - .uri(format!("/users/{}", USER_ID)) - .body(Body::empty())?; - - let response = router.oneshot(request).await?; - - assert_eq!(StatusCode::UNAUTHORIZED, response.status()); - - Ok(()) - } - - #[sqlx::test] - async fn test_post_created(pool: PgPool) -> TestResult { - setup_test_env(); - - let router = Router::new().merge(router()).with_state(AppState { pool }); - - let user = serde_json::json!( { - "name": USER_NAME, - "email": USER_EMAIL, - "password": USER_PASSWORD, - }); - - let request = Request::builder() - .uri("/users") - .method("POST") - .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) - .body(Body::from(serde_json::to_vec(&user)?))?; - - let response = router.oneshot(request).await?; - - assert_eq!(StatusCode::CREATED, response.status()); - - let body_bytes = response.into_body().collect().await?.to_bytes(); - let User { name, email, .. } = serde_json::from_slice(&body_bytes)?; - - assert_eq!(USER_NAME, name); - assert_eq!(USER_EMAIL, email); - - Ok(()) - } - - #[sqlx::test(fixtures(path = "../../fixtures", scripts("users")))] - async fn test_post_conflict(pool: PgPool) -> TestResult { - setup_test_env(); - - let router = Router::new().merge(router()).with_state(AppState { pool }); - - let user = serde_json::json!( { - "name": USER_NAME, - "email": USER_EMAIL, - "password": USER_PASSWORD, - }); - - let request = Request::builder() - .uri("/users") - .method("POST") - .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) - .body(Body::from(serde_json::to_vec(&user)?))?; - - let response = router.oneshot(request).await?; - - assert_eq!(StatusCode::CONFLICT, response.status()); - - Ok(()) - } } |