feat: enhance error handling and user follow functionality, update tests for user context
This commit is contained in:
@@ -18,6 +18,9 @@ impl HTTPError for DbErr {
|
|||||||
fn to_status_code(&self) -> StatusCode {
|
fn to_status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
DbErr::ConnectionAcquire(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
DbErr::ConnectionAcquire(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
DbErr::UnpackInsertId => StatusCode::CONFLICT,
|
||||||
|
DbErr::RecordNotFound(_) => StatusCode::NOT_FOUND,
|
||||||
|
DbErr::Custom(s) if s == "Users cannot follow themselves" => StatusCode::BAD_REQUEST,
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR, // TODO:: more granularity
|
_ => StatusCode::INTERNAL_SERVER_ERROR, // TODO:: more granularity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +30,7 @@ impl HTTPError for UserError {
|
|||||||
fn to_status_code(&self) -> StatusCode {
|
fn to_status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
UserError::NotFound => StatusCode::NOT_FOUND,
|
UserError::NotFound => StatusCode::NOT_FOUND,
|
||||||
UserError::NotFollowing => StatusCode::BAD_REQUEST,
|
UserError::NotFollowing => StatusCode::NOT_FOUND,
|
||||||
UserError::Forbidden => StatusCode::FORBIDDEN,
|
UserError::Forbidden => StatusCode::FORBIDDEN,
|
||||||
UserError::UsernameTaken => StatusCode::BAD_REQUEST,
|
UserError::UsernameTaken => StatusCode::BAD_REQUEST,
|
||||||
UserError::AlreadyFollowing => StatusCode::BAD_REQUEST,
|
UserError::AlreadyFollowing => StatusCode::BAD_REQUEST,
|
||||||
|
@@ -15,7 +15,7 @@ impl FromRequestParts<AppState> for AuthUser {
|
|||||||
type Rejection = (StatusCode, &'static str);
|
type Rejection = (StatusCode, &'static str);
|
||||||
|
|
||||||
async fn from_request_parts(
|
async fn from_request_parts(
|
||||||
_parts: &mut Parts,
|
parts: &mut Parts,
|
||||||
_state: &AppState,
|
_state: &AppState,
|
||||||
) -> Result<Self, Self::Rejection> {
|
) -> Result<Self, Self::Rejection> {
|
||||||
// For now, we'll just return a hardcoded user.
|
// For now, we'll just return a hardcoded user.
|
||||||
@@ -24,6 +24,12 @@ impl FromRequestParts<AppState> for AuthUser {
|
|||||||
// 2. Validate the JWT.
|
// 2. Validate the JWT.
|
||||||
// 3. Extract the user ID from the token claims.
|
// 3. Extract the user ID from the token claims.
|
||||||
// 4. Return an error if the token is invalid or missing.
|
// 4. Return an error if the token is invalid or missing.
|
||||||
Ok(AuthUser { id: 1 }) // Assume user with ID 1 is always authenticated.
|
if let Some(user_id_header) = parts.headers.get("x-test-user-id") {
|
||||||
|
let user_id_str = user_id_header.to_str().unwrap_or("1");
|
||||||
|
let user_id = user_id_str.parse::<i32>().unwrap_or(1);
|
||||||
|
return Ok(AuthUser { id: user_id });
|
||||||
|
} else {
|
||||||
|
return Ok(AuthUser { id: 1 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,12 +38,15 @@ async fn users_post(
|
|||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
Valid(Json(params)): Valid<Json<CreateUserParams>>,
|
Valid(Json(params)): Valid<Json<CreateUserParams>>,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
let user = create_user(&state.conn, params)
|
let result = create_user(&state.conn, params).await;
|
||||||
.await
|
match result {
|
||||||
.map_err(ApiError::from)?;
|
Ok(user) => {
|
||||||
|
|
||||||
let user = user.try_into_model().unwrap();
|
let user = user.try_into_model().unwrap();
|
||||||
Ok((StatusCode::CREATED, Json(UserSchema::from(user))))
|
Ok((StatusCode::CREATED, Json(UserSchema::from(user))))
|
||||||
|
}
|
||||||
|
Err(DbErr::UnpackInsertId) => Err(UserError::UsernameTaken.into()),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -111,6 +114,7 @@ async fn user_thoughts_get(
|
|||||||
.ok_or(UserError::NotFound)?;
|
.ok_or(UserError::NotFound)?;
|
||||||
|
|
||||||
let thoughts_with_authors = get_thoughts_by_user(&state.conn, user.id).await?;
|
let thoughts_with_authors = get_thoughts_by_user(&state.conn, user.id).await?;
|
||||||
|
|
||||||
let thoughts_schema: Vec<ThoughtSchema> = thoughts_with_authors
|
let thoughts_schema: Vec<ThoughtSchema> = thoughts_with_authors
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ThoughtSchema::from)
|
.map(ThoughtSchema::from)
|
||||||
@@ -148,7 +152,14 @@ async fn user_follow_post(
|
|||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
||||||
Err(DbErr::UnpackInsertId) => Err(UserError::AlreadyFollowing.into()),
|
Err(e)
|
||||||
|
if matches!(
|
||||||
|
e.sql_err(),
|
||||||
|
Some(sea_orm::SqlErr::UniqueConstraintViolation { .. })
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
Err(UserError::AlreadyFollowing.into())
|
||||||
|
}
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,28 +3,28 @@ use sea_orm::{ActiveModelTrait, ColumnTrait, DbConn, DbErr, EntityTrait, QueryFi
|
|||||||
use crate::error::UserError;
|
use crate::error::UserError;
|
||||||
use models::domains::follow;
|
use models::domains::follow;
|
||||||
|
|
||||||
pub async fn follow_user(db: &DbConn, follower_id: i32, followee_id: i32) -> Result<(), DbErr> {
|
pub async fn follow_user(db: &DbConn, follower_id: i32, followed_id: i32) -> Result<(), DbErr> {
|
||||||
if follower_id == followee_id {
|
if follower_id == followed_id {
|
||||||
return Err(DbErr::Custom("Users cannot follow themselves".to_string()));
|
return Err(DbErr::Custom("Users cannot follow themselves".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let follow = follow::ActiveModel {
|
let follow = follow::ActiveModel {
|
||||||
follower_id: Set(follower_id),
|
follower_id: Set(follower_id),
|
||||||
followed_id: Set(followee_id),
|
followed_id: Set(followed_id),
|
||||||
};
|
};
|
||||||
|
|
||||||
follow.save(db).await?;
|
follow.insert(db).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unfollow_user(
|
pub async fn unfollow_user(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
follower_id: i32,
|
follower_id: i32,
|
||||||
followee_id: i32,
|
followed_id: i32,
|
||||||
) -> Result<(), UserError> {
|
) -> Result<(), UserError> {
|
||||||
let deleted_result = follow::Entity::delete_many()
|
let deleted_result = follow::Entity::delete_many()
|
||||||
.filter(follow::Column::FollowerId.eq(follower_id))
|
.filter(follow::Column::FollowerId.eq(follower_id))
|
||||||
.filter(follow::Column::FollowedId.eq(followee_id))
|
.filter(follow::Column::FollowedId.eq(followed_id))
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| UserError::Internal(e.to_string()))?;
|
.map_err(|e| UserError::Internal(e.to_string()))?;
|
||||||
|
@@ -39,8 +39,13 @@ pub async fn get_thoughts_by_user(
|
|||||||
user_id: i32,
|
user_id: i32,
|
||||||
) -> Result<Vec<ThoughtWithAuthor>, DbErr> {
|
) -> Result<Vec<ThoughtWithAuthor>, DbErr> {
|
||||||
thought::Entity::find()
|
thought::Entity::find()
|
||||||
|
.select_only()
|
||||||
|
.column(thought::Column::Id)
|
||||||
|
.column(thought::Column::Content)
|
||||||
|
.column(thought::Column::CreatedAt)
|
||||||
|
.column(thought::Column::AuthorId)
|
||||||
.column_as(user::Column::Username, "author_username")
|
.column_as(user::Column::Username, "author_username")
|
||||||
.join(JoinType::InnerJoin, thought::Relation::User.def().rev())
|
.join(JoinType::InnerJoin, thought::Relation::User.def())
|
||||||
.filter(thought::Column::AuthorId.eq(user_id))
|
.filter(thought::Column::AuthorId.eq(user_id))
|
||||||
.order_by_desc(thought::Column::CreatedAt)
|
.order_by_desc(thought::Column::CreatedAt)
|
||||||
.into_model::<ThoughtWithAuthor>()
|
.into_model::<ThoughtWithAuthor>()
|
||||||
@@ -55,9 +60,15 @@ pub async fn get_feed_for_user(
|
|||||||
if followed_ids.is_empty() {
|
if followed_ids.is_empty() {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
thought::Entity::find()
|
thought::Entity::find()
|
||||||
|
.select_only()
|
||||||
|
.column(thought::Column::Id)
|
||||||
|
.column(thought::Column::Content)
|
||||||
|
.column(thought::Column::CreatedAt)
|
||||||
|
.column(thought::Column::AuthorId)
|
||||||
.column_as(user::Column::Username, "author_username")
|
.column_as(user::Column::Username, "author_username")
|
||||||
.join(JoinType::InnerJoin, thought::Relation::User.def().rev())
|
.join(JoinType::InnerJoin, thought::Relation::User.def())
|
||||||
.filter(thought::Column::AuthorId.is_in(followed_ids))
|
.filter(thought::Column::AuthorId.is_in(followed_ids))
|
||||||
.order_by_desc(thought::Column::CreatedAt)
|
.order_by_desc(thought::Column::CreatedAt)
|
||||||
.into_model::<ThoughtWithAuthor>()
|
.into_model::<ThoughtWithAuthor>()
|
||||||
|
@@ -5,7 +5,7 @@ use sea_query::ValueTypeErr;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema)]
|
#[derive(Serialize, ToSchema, Debug)]
|
||||||
#[schema(example = "2025-09-05T12:34:56Z")]
|
#[schema(example = "2025-09-05T12:34:56Z")]
|
||||||
pub struct DateTimeWithTimeZoneWrapper(String);
|
pub struct DateTimeWithTimeZoneWrapper(String);
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ use sea_orm::FromQueryResult;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema, FromQueryResult)]
|
#[derive(Serialize, ToSchema, FromQueryResult, Debug)]
|
||||||
pub struct ThoughtSchema {
|
pub struct ThoughtSchema {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
#[schema(example = "frutiger")]
|
#[schema(example = "frutiger")]
|
||||||
|
@@ -13,7 +13,7 @@ async fn test_feed_and_user_thoughts() {
|
|||||||
|
|
||||||
// As user1, post a thought
|
// As user1, post a thought
|
||||||
let body = json!({ "content": "A thought from user1" }).to_string();
|
let body = json!({ "content": "A thought from user1" }).to_string();
|
||||||
make_post_request(app.router.clone(), "/thoughts", body).await;
|
make_post_request(app.router.clone(), "/thoughts", body, Some(1)).await;
|
||||||
|
|
||||||
// As a different "user", create thoughts for user2 and user3 (we cheat here since auth is hardcoded)
|
// As a different "user", create thoughts for user2 and user3 (we cheat here since auth is hardcoded)
|
||||||
app::persistence::thought::create_thought(
|
app::persistence::thought::create_thought(
|
||||||
@@ -36,7 +36,7 @@ async fn test_feed_and_user_thoughts() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// 1. Get thoughts for user2 - should only see their thought
|
// 1. Get thoughts for user2 - should only see their thought
|
||||||
let response = make_get_request(app.router.clone(), "/users/user2/thoughts").await;
|
let response = make_get_request(app.router.clone(), "/users/user2/thoughts", Some(2)).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
||||||
@@ -44,17 +44,23 @@ async fn test_feed_and_user_thoughts() {
|
|||||||
assert_eq!(v["thoughts"][0]["content"], "user2 was here");
|
assert_eq!(v["thoughts"][0]["content"], "user2 was here");
|
||||||
|
|
||||||
// 2. user1's feed is initially empty
|
// 2. user1's feed is initially empty
|
||||||
let response = make_get_request(app.router.clone(), "/feed").await;
|
let response = make_get_request(app.router.clone(), "/feed", Some(1)).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
||||||
assert!(v["thoughts"].as_array().unwrap().is_empty());
|
assert!(v["thoughts"].as_array().unwrap().is_empty());
|
||||||
|
|
||||||
// 3. user1 follows user2
|
// 3. user1 follows user2
|
||||||
make_post_request(app.router.clone(), "/users/user2/follow", "".to_string()).await;
|
make_post_request(
|
||||||
|
app.router.clone(),
|
||||||
|
"/users/user2/follow",
|
||||||
|
"".to_string(),
|
||||||
|
Some(1),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// 4. user1's feed now has user2's thought
|
// 4. user1's feed now has user2's thought
|
||||||
let response = make_get_request(app.router.clone(), "/feed").await;
|
let response = make_get_request(app.router.clone(), "/feed", Some(1)).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
||||||
|
@@ -9,25 +9,40 @@ async fn test_follow_endpoints() {
|
|||||||
create_test_user(&app.db, "user2").await;
|
create_test_user(&app.db, "user2").await;
|
||||||
|
|
||||||
// 1. user1 follows user2
|
// 1. user1 follows user2
|
||||||
let response =
|
let response = make_post_request(
|
||||||
make_post_request(app.router.clone(), "/users/user2/follow", "".to_string()).await;
|
app.router.clone(),
|
||||||
|
"/users/user2/follow",
|
||||||
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
||||||
|
|
||||||
// 2. user1 tries to follow user2 again (should fail)
|
// 2. user1 tries to follow user2 again (should fail)
|
||||||
let response =
|
let response = make_post_request(
|
||||||
make_post_request(app.router.clone(), "/users/user2/follow", "".to_string()).await;
|
app.router.clone(),
|
||||||
assert_eq!(response.status(), StatusCode::CONFLICT);
|
"/users/user2/follow",
|
||||||
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
// 3. user1 tries to follow a non-existent user
|
// 3. user1 tries to follow a non-existent user
|
||||||
let response =
|
let response = make_post_request(
|
||||||
make_post_request(app.router.clone(), "/users/nobody/follow", "".to_string()).await;
|
app.router.clone(),
|
||||||
|
"/users/nobody/follow",
|
||||||
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
// 4. user1 unfollows user2
|
// 4. user1 unfollows user2
|
||||||
let response = make_delete_request(app.router.clone(), "/users/user2/follow").await;
|
let response = make_delete_request(app.router.clone(), "/users/user2/follow", None).await;
|
||||||
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
||||||
|
|
||||||
// 5. user1 tries to unfollow user2 again (should fail)
|
// 5. user1 tries to unfollow user2 again (should fail)
|
||||||
let response = make_delete_request(app.router.clone(), "/users/user2/follow").await;
|
let response = make_delete_request(app.router.clone(), "/users/user2/follow", None).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
use axum::{http::StatusCode, Router};
|
|
||||||
use http_body_util::BodyExt;
|
|
||||||
|
|
||||||
use utils::testing::make_get_request;
|
|
||||||
|
|
||||||
pub(super) async fn test_root(app: Router) {
|
|
||||||
let response = make_get_request(app, "/").await;
|
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
|
||||||
|
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
||||||
assert_eq!(&body[..], b"Hello, World from DB!");
|
|
||||||
}
|
|
@@ -12,7 +12,7 @@ async fn test_thought_endpoints() {
|
|||||||
|
|
||||||
// 1. Post a new thought as user 1
|
// 1. Post a new thought as user 1
|
||||||
let body = json!({ "content": "My first thought!" }).to_string();
|
let body = json!({ "content": "My first thought!" }).to_string();
|
||||||
let response = make_post_request(app.router.clone(), "/thoughts", body).await;
|
let response = make_post_request(app.router.clone(), "/thoughts", body, Some(1)).await;
|
||||||
assert_eq!(response.status(), StatusCode::CREATED);
|
assert_eq!(response.status(), StatusCode::CREATED);
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
||||||
@@ -22,15 +22,20 @@ async fn test_thought_endpoints() {
|
|||||||
|
|
||||||
// 2. Post a thought with invalid content
|
// 2. Post a thought with invalid content
|
||||||
let body = json!({ "content": "" }).to_string(); // Too short
|
let body = json!({ "content": "" }).to_string(); // Too short
|
||||||
let response = make_post_request(app.router.clone(), "/thoughts", body).await;
|
let response = make_post_request(app.router.clone(), "/thoughts", body, Some(1)).await;
|
||||||
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||||
|
|
||||||
// 3. Attempt to delete another user's thought (user1 tries to delete a non-existent thought, but let's pretend it's user2's)
|
// 3. Attempt to delete another user's thought (user1 tries to delete a non-existent thought, but let's pretend it's user2's)
|
||||||
let response = make_delete_request(app.router.clone(), &format!("/thoughts/999")).await;
|
let response =
|
||||||
|
make_delete_request(app.router.clone(), &format!("/thoughts/999"), Some(1)).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
// 4. Delete the thought created in step 1
|
// 4. Delete the thought created in step 1
|
||||||
let response =
|
let response = make_delete_request(
|
||||||
make_delete_request(app.router.clone(), &format!("/thoughts/{}", thought_id)).await;
|
app.router.clone(),
|
||||||
|
&format!("/thoughts/{}", thought_id),
|
||||||
|
Some(1),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
assert_eq!(response.status(), StatusCode::NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,37 @@
|
|||||||
use axum::{http::StatusCode, Router};
|
use axum::http::StatusCode;
|
||||||
use http_body_util::BodyExt;
|
use http_body_util::BodyExt;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use utils::testing::{make_get_request, make_post_request};
|
use utils::testing::{make_get_request, make_post_request};
|
||||||
|
|
||||||
pub(super) async fn test_post_users(app: Router) {
|
use crate::api::main::setup;
|
||||||
let response = make_post_request(app, "/users", r#"{"username": "test"}"#.to_owned()).await;
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_post_users() {
|
||||||
|
let app = setup().await;
|
||||||
|
let response = make_post_request(
|
||||||
|
app.router,
|
||||||
|
"/users",
|
||||||
|
r#"{"username": "test"}"#.to_owned(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
assert_eq!(response.status(), StatusCode::CREATED);
|
assert_eq!(response.status(), StatusCode::CREATED);
|
||||||
|
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
assert_eq!(&body[..], br#"{"id":1,"username":"test"}"#);
|
assert_eq!(&body[..], br#"{"id":1,"username":"test"}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn test_post_users_error(app: Router) {
|
#[tokio::test]
|
||||||
let response = make_post_request(app, "/users", r#"{"username": "1"}"#.to_owned()).await;
|
pub(super) async fn test_post_users_error() {
|
||||||
|
let app = setup().await;
|
||||||
|
let response = make_post_request(
|
||||||
|
app.router,
|
||||||
|
"/users",
|
||||||
|
r#"{"username": "1"}"#.to_owned(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||||
|
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
@@ -27,8 +45,18 @@ pub(super) async fn test_post_users_error(app: Router) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn test_get_users(app: Router) {
|
#[tokio::test]
|
||||||
let response = make_get_request(app, "/users").await;
|
pub async fn test_get_users() {
|
||||||
|
let app = setup().await;
|
||||||
|
make_post_request(
|
||||||
|
app.router.clone(),
|
||||||
|
"/users",
|
||||||
|
r#"{"username": "test"}"#.to_owned(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let response = make_get_request(app.router, "/users", None).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
let body = response.into_body().collect().await.unwrap().to_bytes();
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
||||||
|
@@ -1,33 +1,51 @@
|
|||||||
use axum::{body::Body, http::Request, response::Response, Router};
|
use axum::{body::Body, http::Request, response::Response, Router};
|
||||||
use tower::ServiceExt;
|
use tower::ServiceExt;
|
||||||
|
|
||||||
pub async fn make_get_request(app: Router, url: &str) -> Response {
|
pub async fn make_get_request(app: Router, url: &str, user_id: Option<i32>) -> Response {
|
||||||
app.oneshot(Request::builder().uri(url).body(Body::empty()).unwrap())
|
let mut builder = Request::builder()
|
||||||
|
.uri(url)
|
||||||
|
.header("Content-Type", "application/json");
|
||||||
|
|
||||||
|
if let Some(user_id) = user_id {
|
||||||
|
builder = builder.header("x-test-user-id", user_id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.oneshot(builder.body(Body::empty()).unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn make_post_request(app: Router, url: &str, body: String) -> Response {
|
pub async fn make_post_request(
|
||||||
app.oneshot(
|
app: Router,
|
||||||
Request::builder()
|
url: &str,
|
||||||
|
body: String,
|
||||||
|
user_id: Option<i32>,
|
||||||
|
) -> Response {
|
||||||
|
let mut builder = Request::builder()
|
||||||
.method("POST")
|
.method("POST")
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json");
|
||||||
.body(Body::from(body))
|
|
||||||
.unwrap(),
|
if let Some(user_id) = user_id {
|
||||||
)
|
builder = builder.header("x-test-user-id", user_id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.oneshot(builder.body(Body::from(body)).unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn make_delete_request(app: Router, url: &str) -> Response {
|
pub async fn make_delete_request(app: Router, url: &str, user_id: Option<i32>) -> Response {
|
||||||
app.oneshot(
|
let mut builder = Request::builder()
|
||||||
Request::builder()
|
|
||||||
.method("DELETE")
|
.method("DELETE")
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.body(Body::empty())
|
.header("Content-Type", "application/json");
|
||||||
.unwrap(),
|
|
||||||
)
|
if let Some(user_id) = user_id {
|
||||||
|
builder = builder.header("x-test-user-id", user_id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.oneshot(builder.body(Body::empty()).unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user