From f24968c444019b0959479e2a5cd2c4ad243a93bb Mon Sep 17 00:00:00 2001 From: Kent Tristan Yves Sarmiento Date: Tue, 26 Dec 2023 11:46:50 +0800 Subject: [PATCH] refactor: add error details for tracing/debugging (#34) --- link-for-later/src/controller/auth.rs | 15 ++-- link-for-later/src/controller/error.rs | 97 +++++++++++++++++++++-- link-for-later/src/controller/links.rs | 30 ++++--- link-for-later/src/controller/users.rs | 18 ++--- link-for-later/src/repository/inmemory.rs | 6 +- link-for-later/src/repository/mongodb.rs | 4 +- link-for-later/src/service/links.rs | 28 +++---- link-for-later/src/service/users.rs | 49 ++++++------ link-for-later/src/types.rs | 4 +- link-for-later/src/types/error.rs | 78 ++++++++++++++++++ link-for-later/src/types/errors.rs | 61 -------------- link-for-later/tests/links.rs | 4 +- link-for-later/tests/users.rs | 9 ++- 13 files changed, 251 insertions(+), 152 deletions(-) create mode 100644 link-for-later/src/types/error.rs delete mode 100644 link-for-later/src/types/errors.rs diff --git a/link-for-later/src/controller/auth.rs b/link-for-later/src/controller/auth.rs index 7627ee2..ee60fb7 100644 --- a/link-for-later/src/controller/auth.rs +++ b/link-for-later/src/controller/auth.rs @@ -20,21 +20,18 @@ where let TypedHeader(Authorization(bearer)) = TypedHeader::>::from_request_parts(parts, state) .await - .map_err(|_| AppError::AuthorizationError)?; + .map_err(|_| { + AppError::AuthorizationError(String::from("Authorization token not found")) + })?; let secret = std::env::var(JWT_SECRET_KEY).map_or_else(|_| String::default(), |secret| secret); - let token_data = match decode::( + let token_data = decode::( bearer.token(), &DecodingKey::from_secret(secret.as_bytes()), &Validation::default(), - ) { - Ok(token) => token, - Err(e) => { - tracing::error!("Error: {}", e.to_string()); - return Err(AppError::AuthorizationError); - } - }; + ) + .map_err(|e| AppError::AuthorizationError(format!("decode() {e:?}")))?; Ok(token_data.claims) } diff --git a/link-for-later/src/controller/error.rs b/link-for-later/src/controller/error.rs index 87a0bbc..37b476b 100644 --- a/link-for-later/src/controller/error.rs +++ b/link-for-later/src/controller/error.rs @@ -7,20 +7,45 @@ use serde_json::json; use crate::types::AppError; +#[allow(clippy::cognitive_complexity)] impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, error_message) = match self { - Self::ServerError | Self::DatabaseError(_) => { + Self::ServerError(ref e) => { + tracing::debug!("Server error: {}", e.to_string()); (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } - Self::LinkNotFound => (StatusCode::NOT_FOUND, self.to_string()), - Self::UserAlreadyExists - | Self::UserNotFound - | Self::InvalidEmail - | Self::InvalidUrl => (StatusCode::BAD_REQUEST, self.to_string()), - Self::AuthorizationError | Self::IncorrectPassword => { + Self::DatabaseError(ref e) => { + tracing::debug!("Database error: {}", e.to_string()); + (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) + } + Self::LinkNotFound(ref e) => { + tracing::debug!("Link not found: {}", e.to_string()); + (StatusCode::NOT_FOUND, self.to_string()) + } + Self::UserAlreadyExists(ref e) => { + tracing::debug!("User already exists: {}", e.to_string()); + (StatusCode::BAD_REQUEST, self.to_string()) + } + Self::UserNotFound(ref e) => { + tracing::debug!("User not found: {}", e.to_string()); + (StatusCode::BAD_REQUEST, self.to_string()) + } + Self::IncorrectPassword(ref e) => { + tracing::debug!("Incorrect password: {}", e.to_string()); + (StatusCode::UNAUTHORIZED, self.to_string()) + } + Self::AuthorizationError(ref e) => { + tracing::debug!("Authorization error: {}", e.to_string()); (StatusCode::UNAUTHORIZED, self.to_string()) } + Self::ValidationError(ref e) => { + tracing::debug!("Payload validation error: {}", e.to_string()); + (StatusCode::BAD_REQUEST, self.to_string()) + } + + #[cfg(test)] + Self::TestError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), }; let body = Json(json!({ @@ -30,3 +55,61 @@ impl IntoResponse for AppError { (status, body).into_response() } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_error_response() { + assert_eq!( + AppError::ServerError("a server operation failed".into()) + .into_response() + .status(), + StatusCode::INTERNAL_SERVER_ERROR + ); + assert_eq!( + AppError::DatabaseError("a database operation failed".into()) + .into_response() + .status(), + StatusCode::INTERNAL_SERVER_ERROR + ); + assert_eq!( + AppError::LinkNotFound("link".into()) + .into_response() + .status(), + StatusCode::NOT_FOUND + ); + assert_eq!( + AppError::UserAlreadyExists("user".into()) + .into_response() + .status(), + StatusCode::BAD_REQUEST + ); + assert_eq!( + AppError::UserNotFound("user".into()) + .into_response() + .status(), + StatusCode::BAD_REQUEST + ); + assert_eq!( + AppError::IncorrectPassword("user".into()) + .into_response() + .status(), + StatusCode::UNAUTHORIZED + ); + assert_eq!( + AppError::AuthorizationError("an authorization error occurred".into()) + .into_response() + .status(), + StatusCode::UNAUTHORIZED + ); + assert_eq!( + AppError::ValidationError("a validation error occurred".into()) + .into_response() + .status(), + StatusCode::BAD_REQUEST + ); + } +} diff --git a/link-for-later/src/controller/links.rs b/link-for-later/src/controller/links.rs index 3333889..f058feb 100644 --- a/link-for-later/src/controller/links.rs +++ b/link-for-later/src/controller/links.rs @@ -53,8 +53,7 @@ async fn post( match payload.validate() { Ok(()) => {} Err(e) => { - tracing::error!("Error: {}", e); - return AppError::InvalidUrl.into_response(); + return AppError::ValidationError(format!("post_link() {e:?}")).into_response(); } } @@ -105,8 +104,7 @@ async fn put( match payload.validate() { Ok(()) => {} Err(e) => { - tracing::error!("Error: {}", e); - return AppError::InvalidUrl.into_response(); + return AppError::ValidationError(format!("put_link() {e:?}")).into_response(); } } @@ -231,7 +229,7 @@ mod tests { .expect_search() .withf(move |_, query| query == &repo_query) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_links_service)).build(); let response = list(State(app_state), Claims::new("user-id", 0, 0)).await; @@ -241,7 +239,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } #[traced_test] @@ -301,7 +299,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid url"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); } #[traced_test] @@ -315,7 +313,7 @@ mod tests { .expect_create() .withf(move |_, item| item == &item_to_create) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_links_service)).build(); let response = post( @@ -330,7 +328,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } #[traced_test] @@ -378,7 +376,7 @@ mod tests { .expect_get() .withf(move |_, query| query == &repo_query) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_links_service)).build(); let response = get( @@ -393,7 +391,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } #[traced_test] @@ -458,7 +456,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid url"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); } #[traced_test] @@ -475,7 +473,7 @@ mod tests { .expect_update() .withf(move |_, id, item| id == "1" && item == &item_to_update) .times(1) - .returning(|_, _, _| Err(AppError::ServerError)); + .returning(|_, _, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_links_service)).build(); let response = put( @@ -491,7 +489,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } #[traced_test] @@ -528,7 +526,7 @@ mod tests { .expect_delete() .withf(move |_, item| item == &item_to_delete) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_links_service)).build(); let response = delete( @@ -543,7 +541,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } struct AppStateBuilder { diff --git a/link-for-later/src/controller/users.rs b/link-for-later/src/controller/users.rs index e6c5a70..851b3d3 100644 --- a/link-for-later/src/controller/users.rs +++ b/link-for-later/src/controller/users.rs @@ -31,8 +31,7 @@ async fn register( match payload.validate() { Ok(()) => {} Err(e) => { - tracing::error!("Error: {}", e); - return AppError::InvalidEmail.into_response(); + return AppError::ValidationError(format!("register() {e:?}")).into_response(); } } @@ -58,8 +57,7 @@ async fn login( match payload.validate() { Ok(()) => {} Err(e) => { - tracing::error!("Error: {}", e); - return AppError::InvalidEmail.into_response(); + return AppError::ValidationError(format!("login() {e:?}")).into_response(); } } @@ -141,7 +139,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid email"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); } #[traced_test] @@ -155,7 +153,7 @@ mod tests { .expect_register() .withf(move |_, user| user == &user_to_register) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_users_service)).build(); let response = register(State(app_state), Json(request)).await; @@ -165,7 +163,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } #[traced_test] @@ -209,7 +207,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid email"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); } #[traced_test] @@ -223,7 +221,7 @@ mod tests { .expect_login() .withf(move |_, user| user == &user_to_login) .times(1) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let app_state = AppStateBuilder::new(Arc::new(mock_users_service)).build(); let response = login(State(app_state), Json(request)).await; @@ -233,7 +231,7 @@ mod tests { let body = body.collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "server error"}).to_string()); + assert_eq!(body, json!({"error": "test error"}).to_string()); } struct AppStateBuilder { diff --git a/link-for-later/src/repository/inmemory.rs b/link-for-later/src/repository/inmemory.rs index d69599a..cb66fab 100644 --- a/link-for-later/src/repository/inmemory.rs +++ b/link-for-later/src/repository/inmemory.rs @@ -46,7 +46,7 @@ impl LinksRepository for LinksRepositoryProvider { .iter() .find(|link| link.id() == query.id() && link.owner() == query.owner()) .cloned() - .ok_or(AppError::LinkNotFound) + .ok_or_else(|| AppError::LinkNotFound(query.id().to_owned())) } async fn create(&self, item: &LinkItem) -> Result { @@ -66,7 +66,7 @@ impl LinksRepository for LinksRepositoryProvider { .iter() .find(|link| link.id() == id && link.owner() == item.owner()) .cloned() - .ok_or(AppError::LinkNotFound)?; + .ok_or_else(|| AppError::LinkNotFound(id.to_owned()))?; self.delete(item).await?; INMEMORY_LINKS_DATA.lock().unwrap().push(item.clone()); Ok(item.clone()) @@ -92,7 +92,7 @@ impl UsersRepository for UsersRepositoryProvider { .iter() .find(|user| user.email() == query.email()) .cloned() - .ok_or(AppError::UserNotFound) + .ok_or_else(|| AppError::UserNotFound(query.email().to_owned())) } async fn create(&self, info: &UserInfo) -> Result { diff --git a/link-for-later/src/repository/mongodb.rs b/link-for-later/src/repository/mongodb.rs index 36de5bf..f92a829 100644 --- a/link-for-later/src/repository/mongodb.rs +++ b/link-for-later/src/repository/mongodb.rs @@ -65,7 +65,7 @@ impl LinksRepository for LinksRepositoryProvider { .find_one(db_query, None) .await .map_err(|e| AppError::DatabaseError(format!("find_one() {e:?}")))?; - item.ok_or(AppError::LinkNotFound) + item.ok_or_else(|| AppError::LinkNotFound(query.id().to_owned())) } async fn create(&self, item: &LinkItem) -> Result { @@ -123,7 +123,7 @@ impl UsersRepository for UsersRepositoryProvider { .find_one(db_query, None) .await .map_err(|e| AppError::DatabaseError(format!("find_one() {e:?}")))?; - item.ok_or(AppError::UserNotFound) + item.ok_or_else(|| AppError::UserNotFound(query.email().to_owned())) } async fn create(&self, info: &UserInfo) -> Result { diff --git a/link-for-later/src/service/links.rs b/link-for-later/src/service/links.rs index 3bfaec0..d5c636b 100644 --- a/link-for-later/src/service/links.rs +++ b/link-for-later/src/service/links.rs @@ -147,14 +147,14 @@ mod tests { .expect_find() .withf(move |query| query == &expected_query) .times(1) - .returning(|_| Err(AppError::ServerError)); + .returning(|_| Err(AppError::TestError)); let links_service = ServiceProvider {}; let response = links_service .search(Box::new(Arc::new(mock_links_repo)), &repo_query) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } #[tokio::test] @@ -193,14 +193,14 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::LinkNotFound)); + .returning(|_| Err(AppError::LinkNotFound("1".into()))); let links_service = ServiceProvider {}; let response = links_service .get(Box::new(Arc::new(mock_links_repo)), &request_query) .await; - assert_eq!(response, Err(AppError::LinkNotFound)); + assert_eq!(response, Err(AppError::LinkNotFound("1".into()))); } #[tokio::test] @@ -239,14 +239,14 @@ mod tests { .expect_create() //.withf(move |item| item == &item_to_create) .times(1) - .returning(|_| Err(AppError::ServerError)); + .returning(|_| Err(AppError::TestError)); let links_service = ServiceProvider {}; let response = links_service .create(Box::new(Arc::new(mock_links_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } #[tokio::test] @@ -307,7 +307,7 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::LinkNotFound)); + .returning(|_| Err(AppError::LinkNotFound("1".into()))); mock_links_repo .expect_update() //.withf(move |item| item == &item_to_update) @@ -318,7 +318,7 @@ mod tests { .update(Box::new(Arc::new(mock_links_repo)), "1", &request_item) .await; - assert_eq!(response, Err(AppError::LinkNotFound)); + assert_eq!(response, Err(AppError::LinkNotFound("1".into()))); } #[tokio::test] @@ -348,14 +348,14 @@ mod tests { //.withf(move |item| item == &item_to_update) .times(1) .in_sequence(&mut seq) - .returning(|_, _| Err(AppError::ServerError)); + .returning(|_, _| Err(AppError::TestError)); let links_service = ServiceProvider {}; let response = links_service .update(Box::new(Arc::new(mock_links_repo)), "1", &request_item) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } #[tokio::test] @@ -409,7 +409,7 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::LinkNotFound)); + .returning(|_| Err(AppError::LinkNotFound("1".into()))); mock_links_repo .expect_delete() //.withf(move |item| item == &item_to_delete) @@ -420,7 +420,7 @@ mod tests { .delete(Box::new(Arc::new(mock_links_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::LinkNotFound)); + assert_eq!(response, Err(AppError::LinkNotFound("1".into()))); } #[tokio::test] @@ -450,13 +450,13 @@ mod tests { //.withf(move |item| item == &item_to_delete) .times(1) .in_sequence(&mut seq) - .returning(|_| Err(AppError::ServerError)); + .returning(|_| Err(AppError::TestError)); let links_service = ServiceProvider {}; let response = links_service .delete(Box::new(Arc::new(mock_links_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } } diff --git a/link-for-later/src/service/users.rs b/link-for-later/src/service/users.rs index 09fcdf7..5fe8618 100644 --- a/link-for-later/src/service/users.rs +++ b/link-for-later/src/service/users.rs @@ -27,8 +27,8 @@ impl UsersService for ServiceProvider { ) -> Result { let user_query = UserQueryBuilder::new(user_info.email()).build(); let user_info = match users_repo.get(&user_query).await { - Ok(_) => return Err(AppError::UserAlreadyExists), - Err(AppError::UserNotFound) => user_info.clone(), + Ok(_) => return Err(AppError::UserAlreadyExists(user_info.email().to_owned())), + Err(AppError::UserNotFound(_)) => user_info.clone(), Err(e) => return Err(e), }; @@ -52,15 +52,14 @@ impl UsersService for ServiceProvider { let retrieved_user_info = users_repo.get(&user_query).await?; if retrieved_user_info.password() != user_info.password() { - tracing::info!("invalid password for user {}", &user_info.email()); - return Err(AppError::IncorrectPassword); + return Err(AppError::IncorrectPassword(user_info.email().to_owned())); } let timestamp = |timestamp: DateTime| -> Result { let timestamp: usize = timestamp .timestamp() .try_into() - .map_err(|_| AppError::ServerError)?; + .map_err(|e| AppError::ServerError(format!("timestamp() {e:?}")))?; Ok(timestamp) }; @@ -73,17 +72,12 @@ impl UsersService for ServiceProvider { let secret = std::env::var(JWT_SECRET_KEY).map_or_else(|_| String::default(), |secret| secret); - let token = match encode( + let token = encode( &Header::default(), &claims, &EncodingKey::from_secret(secret.as_bytes()), - ) { - Ok(token) => token, - Err(e) => { - tracing::error!("Error: {}", e.to_string()); - return Err(AppError::ServerError); - } - }; + ) + .map_err(|e| AppError::ServerError(format!("encode() {e:?}")))?; Ok(Token::new(&token)) } @@ -111,7 +105,7 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::UserNotFound)); + .returning(|_| Err(AppError::UserNotFound("user@test.com".into()))); mock_users_repo .expect_create() //.withf(move |user| user == &user_to_register) @@ -147,7 +141,10 @@ mod tests { .register(Box::new(Arc::new(mock_users_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::UserAlreadyExists)); + assert_eq!( + response, + Err(AppError::UserAlreadyExists("user@test.com".into())) + ); } #[tokio::test] @@ -161,7 +158,7 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::ServerError)); + .returning(|_| Err(AppError::TestError)); mock_users_repo.expect_create().times(0); let users_service = ServiceProvider {}; @@ -169,7 +166,7 @@ mod tests { .register(Box::new(Arc::new(mock_users_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } #[tokio::test] @@ -183,19 +180,19 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(|_| Err(AppError::UserNotFound)); + .returning(|_| Err(AppError::UserNotFound("user@test.com".into()))); mock_users_repo .expect_create() //.withf(move |user| user == &user_to_register) .times(1) - .returning(move |_| Err(AppError::ServerError)); + .returning(move |_| Err(AppError::TestError)); let users_service = ServiceProvider {}; let response = users_service .register(Box::new(Arc::new(mock_users_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::ServerError)); + assert_eq!(response, Err(AppError::TestError)); } #[tokio::test] @@ -231,14 +228,17 @@ mod tests { .expect_get() .withf(move |query| query == &repo_query) .times(1) - .returning(move |_| Err(AppError::UserNotFound)); + .returning(move |_| Err(AppError::UserNotFound("user@test.com".into()))); let users_service = ServiceProvider {}; let response = users_service .login(Box::new(Arc::new(mock_users_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::UserNotFound)); + assert_eq!( + response, + Err(AppError::UserNotFound("user@test.com".into())) + ); } #[tokio::test] @@ -260,6 +260,9 @@ mod tests { .login(Box::new(Arc::new(mock_users_repo)), &request_item) .await; - assert_eq!(response, Err(AppError::IncorrectPassword)); + assert_eq!( + response, + Err(AppError::IncorrectPassword("user@test.com".into())) + ); } } diff --git a/link-for-later/src/types.rs b/link-for-later/src/types.rs index c41ea17..4133d87 100644 --- a/link-for-later/src/types.rs +++ b/link-for-later/src/types.rs @@ -1,4 +1,4 @@ -pub use self::errors::App as AppError; +pub use self::error::App as AppError; pub type Result = std::result::Result; @@ -10,4 +10,4 @@ pub enum Database { pub mod auth; pub mod dto; pub mod entity; -pub mod errors; +pub mod error; diff --git a/link-for-later/src/types/error.rs b/link-for-later/src/types/error.rs new file mode 100644 index 0000000..0ac2723 --- /dev/null +++ b/link-for-later/src/types/error.rs @@ -0,0 +1,78 @@ +use std::{error, fmt}; + +#[derive(Debug, PartialEq, Eq)] +pub enum App { + ServerError(String), + DatabaseError(String), + LinkNotFound(String), + UserAlreadyExists(String), + UserNotFound(String), + IncorrectPassword(String), + AuthorizationError(String), + ValidationError(String), + + #[cfg(test)] + TestError, +} + +impl fmt::Display for App { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::ServerError(_) => write!(f, "server error"), + Self::DatabaseError(_) => write!(f, "database error"), + Self::LinkNotFound(_) => write!(f, "link item not found"), + Self::UserAlreadyExists(_) => write!(f, "user already registered"), + Self::UserNotFound(_) => write!(f, "user not found"), + Self::IncorrectPassword(_) => write!(f, "incorrect password for user"), + Self::AuthorizationError(_) => write!(f, "invalid authorization token"), + Self::ValidationError(_) => write!(f, "invalid request"), + + #[cfg(test)] + Self::TestError => write!(f, "test error"), + } + } +} + +impl error::Error for App {} + +#[cfg(test)] +mod tests { + + use super::App as AppError; + + #[test] + fn test_error_messages() { + assert_eq!( + AppError::ServerError("a server operation failed".into()).to_string(), + "server error" + ); + assert_eq!( + AppError::DatabaseError("a database operation failed".into()).to_string(), + "database error" + ); + assert_eq!( + AppError::LinkNotFound("link".into()).to_string(), + "link item not found" + ); + assert_eq!( + AppError::UserAlreadyExists("user".into()).to_string(), + "user already registered" + ); + assert_eq!( + AppError::UserNotFound("user".into()).to_string(), + "user not found" + ); + assert_eq!( + AppError::IncorrectPassword("user".into()).to_string(), + "incorrect password for user" + ); + assert_eq!( + AppError::AuthorizationError("an authorization error occurred".into()).to_string(), + "invalid authorization token" + ); + assert_eq!( + AppError::ValidationError("invalid email".into()).to_string(), + "invalid request" + ); + } +} diff --git a/link-for-later/src/types/errors.rs b/link-for-later/src/types/errors.rs deleted file mode 100644 index 1917c3b..0000000 --- a/link-for-later/src/types/errors.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::{error, fmt}; - -#[derive(Debug, PartialEq, Eq)] -pub enum App { - ServerError, - DatabaseError(String), - LinkNotFound, - UserAlreadyExists, - UserNotFound, - IncorrectPassword, - AuthorizationError, - InvalidEmail, - InvalidUrl, -} - -impl fmt::Display for App { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::ServerError => write!(f, "server error"), - Self::DatabaseError(_) => write!(f, "database error"), - Self::LinkNotFound => write!(f, "link item not found"), - Self::UserAlreadyExists => write!(f, "user already regisered"), - Self::UserNotFound => write!(f, "user not found"), - Self::IncorrectPassword => write!(f, "incorrect password for user"), - Self::AuthorizationError => write!(f, "invalid authorization token"), - Self::InvalidEmail => write!(f, "invalid email"), - Self::InvalidUrl => write!(f, "invalid url"), - } - } -} - -impl error::Error for App {} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn test_error_messages() { - assert_eq!(App::ServerError.to_string(), "server error"); - assert_eq!( - App::DatabaseError("a database error occurred".into()).to_string(), - "database error" - ); - assert_eq!(App::ServerError.to_string(), "server error"); - assert_eq!(App::LinkNotFound.to_string(), "link item not found"); - assert_eq!(App::UserAlreadyExists.to_string(), "user already regisered"); - assert_eq!(App::UserNotFound.to_string(), "user not found"); - assert_eq!( - App::IncorrectPassword.to_string(), - "incorrect password for user" - ); - assert_eq!( - App::AuthorizationError.to_string(), - "invalid authorization token" - ); - assert_eq!(App::InvalidEmail.to_string(), "invalid email"); - assert_eq!(App::InvalidUrl.to_string(), "invalid url"); - } -} diff --git a/link-for-later/tests/links.rs b/link-for-later/tests/links.rs index a109780..2b153df 100644 --- a/link-for-later/tests/links.rs +++ b/link-for-later/tests/links.rs @@ -209,7 +209,7 @@ async fn test_post_link_invalid_url(#[values(DatabaseType::MongoDb)] db_type: Da let body = response.into_body().collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid url"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); let db_count = repository.count_links().await; assert!(db_count == 0); @@ -290,7 +290,7 @@ async fn test_put_link_invalid_url(#[values(DatabaseType::MongoDb)] db_type: Dat let body = response.into_body().collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid url"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); let db_count = repository.count_links().await; assert!(db_count == 1); diff --git a/link-for-later/tests/users.rs b/link-for-later/tests/users.rs index aa56887..a635263 100644 --- a/link-for-later/tests/users.rs +++ b/link-for-later/tests/users.rs @@ -80,7 +80,7 @@ async fn test_register_user_invalid_email(#[values(DatabaseType::MongoDb)] db_ty let body = response.into_body().collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid email"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); let db_count = repository.count_users().await; assert!(db_count == 0); @@ -117,7 +117,10 @@ async fn test_register_user_already_registered( let body = response.into_body().collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "user already regisered"}).to_string()); + assert_eq!( + body, + json!({"error": "user already registered"}).to_string() + ); let db_count = repository.count_users().await; assert!(db_count == 1); @@ -184,7 +187,7 @@ async fn test_login_user_invalid_email(#[values(DatabaseType::MongoDb)] db_type: let body = response.into_body().collect().await.unwrap().to_bytes(); let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, json!({"error": "invalid email"}).to_string()); + assert_eq!(body, json!({"error": "invalid request"}).to_string()); } #[rstest]