feat: success test
This commit is contained in:
@@ -38,6 +38,8 @@ async fn test_list_users_should_fail_with_invalid_page() {
|
|||||||
.layer(Extension(state));
|
.layer(Extension(state));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let res = server.get("/v1/users?page=0&per_page=10").await;
|
let res = server.get("/v1/users?page=0&per_page=10").await;
|
||||||
|
dbg!(res.text());
|
||||||
|
dbg!(res.status_code());
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ async fn test_create_user_should_return_201() {
|
|||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let payload = UsersCreateRequestDto {
|
let payload = UsersCreateRequestDto {
|
||||||
fullname: "Create Data #1".into(),
|
fullname: "Create Data #1".into(),
|
||||||
email: "test@create.com".into(),
|
email: format!("test-{}@create.com", Uuid::new_v4()).into(),
|
||||||
role_id,
|
role_id,
|
||||||
password: "Password1!".into(),
|
password: "Password1!".into(),
|
||||||
student_type: "general".into(),
|
student_type: "general".into(),
|
||||||
@@ -247,9 +249,10 @@ async fn test_update_user_should_return_200() {
|
|||||||
.nest("/v1/users", users_router())
|
.nest("/v1/users", users_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
|
let unique_email = format!("update_{}@test.com", Uuid::new_v4());
|
||||||
let payload = UsersCreateRequestDto {
|
let payload = UsersCreateRequestDto {
|
||||||
fullname: "Old Name".into(),
|
fullname: "Old Name".into(),
|
||||||
email: "update@test.com".into(),
|
email: unique_email.clone(),
|
||||||
password: "Password1!".into(),
|
password: "Password1!".into(),
|
||||||
role_id: role_id.clone(),
|
role_id: role_id.clone(),
|
||||||
student_type: "general".into(),
|
student_type: "general".into(),
|
||||||
@@ -258,15 +261,16 @@ async fn test_update_user_should_return_200() {
|
|||||||
referral_code: None,
|
referral_code: None,
|
||||||
referred_by: None,
|
referred_by: None,
|
||||||
};
|
};
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
let create_res = server.post("/v1/users/create").json(&payload).await;
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
assert_eq!(create_res.status_code(), StatusCode::CREATED);
|
||||||
|
let user = repo.query_user_by_email(unique_email).await.unwrap();
|
||||||
let user_id = user.id.id.to_raw();
|
let user_id = user.id.id.to_raw();
|
||||||
let update_payload = UsersUpdateRequestDto {
|
let update_payload = UsersUpdateRequestDto {
|
||||||
fullname: "Updated Name".into(),
|
fullname: "Updated Name".into(),
|
||||||
email: "update@test.com".into(),
|
email: payload.email.clone(),
|
||||||
student_type: "general".into(),
|
student_type: "general".into(),
|
||||||
phone_number: "081234567890".into(),
|
phone_number: "081234567890".into(),
|
||||||
role_id: role_id.clone(),
|
role_id: user.role.id.id.to_raw(),
|
||||||
is_active: true,
|
is_active: true,
|
||||||
referral_code: None,
|
referral_code: None,
|
||||||
referred_by: None,
|
referred_by: None,
|
||||||
@@ -281,7 +285,8 @@ async fn test_update_user_should_return_200() {
|
|||||||
.json(&update_payload)
|
.json(&update_payload)
|
||||||
.await;
|
.await;
|
||||||
let status = res.status_code();
|
let status = res.status_code();
|
||||||
assert_eq!(status, StatusCode::OK);
|
let body = res.text();
|
||||||
|
assert_eq!(status, StatusCode::OK, "Response body: {}", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -297,9 +302,10 @@ async fn test_create_user_should_fail_if_email_taken() {
|
|||||||
.nest("/v1/users", users_router())
|
.nest("/v1/users", users_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
|
let email = format!("test_{}@example.com", Uuid::new_v4());
|
||||||
let payload = UsersCreateRequestDto {
|
let payload = UsersCreateRequestDto {
|
||||||
fullname: "User Satu".into(),
|
fullname: "User Satu".into(),
|
||||||
email: "taken@example.com".into(),
|
email: email.clone(),
|
||||||
password: "Password1!".into(),
|
password: "Password1!".into(),
|
||||||
student_type: "general".into(),
|
student_type: "general".into(),
|
||||||
role_id,
|
role_id,
|
||||||
@@ -362,7 +368,7 @@ async fn test_delete_user_should_fail_if_already_deleted() {
|
|||||||
let res2 = server.delete(&format!("/v1/users/delete/{}", id)).await;
|
let res2 = server.delete(&format!("/v1/users/delete/{}", id)).await;
|
||||||
assert_eq!(res2.status_code(), StatusCode::BAD_REQUEST);
|
assert_eq!(res2.status_code(), StatusCode::BAD_REQUEST);
|
||||||
let body = res2.text();
|
let body = res2.text();
|
||||||
assert!(body.contains("User not found"));
|
assert!(body.contains("User already deleted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -531,6 +537,7 @@ async fn test_user_detail_should_fail_if_user_is_soft_deleted() {
|
|||||||
let user_id = user.id.id.to_raw();
|
let user_id = user.id.id.to_raw();
|
||||||
let _ = repo.query_delete_user(user_id.clone()).await.unwrap();
|
let _ = repo.query_delete_user(user_id.clone()).await.unwrap();
|
||||||
let res = server.get(&format!("/v1/users/detail/{}", user_id)).await;
|
let res = server.get(&format!("/v1/users/detail/{}", user_id)).await;
|
||||||
|
dbg!(res.text());
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
||||||
let body = res.text();
|
let body = res.text();
|
||||||
assert!(body.contains("User not found"));
|
assert!(body.contains("User not found"));
|
||||||
@@ -586,7 +593,7 @@ async fn test_update_user_should_fail_if_user_is_deleted() {
|
|||||||
.await;
|
.await;
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
||||||
let body = res.text();
|
let body = res.text();
|
||||||
assert!(body.contains("User not found"));
|
assert!(body.contains("User already deleted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
PermissionsItemDto, PermissionsItemDtoRaw, ResourceEnum, ResponseListSuccessDto,
|
PermissionsItemDto, PermissionsItemDtoRaw, ResourceEnum, ResponseListSuccessDto,
|
||||||
RolesItemDto, RolesItemDtoRaw,
|
RolesItemDto, RolesItemDtoRaw,
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
|
||||||
pub struct UsersRepository<'a> {
|
pub struct UsersRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
@@ -45,7 +45,14 @@ impl<'a> UsersRepository<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let limit = meta.per_page.unwrap_or(10);
|
let limit = meta.per_page.unwrap_or(10);
|
||||||
let start = (meta.page.unwrap_or(1) - 1) * limit;
|
let page = meta.page.unwrap_or(1);
|
||||||
|
|
||||||
|
// Validate pagination parameters explicitly
|
||||||
|
if page < 1 || limit < 1 {
|
||||||
|
return Err(anyhow!("Invalid pagination parameters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = (page - 1) * limit;
|
||||||
|
|
||||||
let select_query = format!(
|
let select_query = format!(
|
||||||
"
|
"
|
||||||
@@ -98,7 +105,7 @@ impl<'a> UsersRepository<'a> {
|
|||||||
referral_code: user.referral_code,
|
referral_code: user.referral_code,
|
||||||
student_type: user.student_type,
|
student_type: user.student_type,
|
||||||
is_active: user.is_active,
|
is_active: user.is_active,
|
||||||
role: user.role.unwrap_or_else(|| "-".into()), // Handle role safely
|
role: user.role.unwrap_or_else(|| "-".into()),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@@ -247,6 +254,7 @@ impl<'a> UsersRepository<'a> {
|
|||||||
let merged = UsersSchema {
|
let merged = UsersSchema {
|
||||||
password: existing.password,
|
password: existing.password,
|
||||||
created_at: existing.created_at,
|
created_at: existing.created_at,
|
||||||
|
role: make_thing("app_roles", &existing.role.id),
|
||||||
..data.clone()
|
..data.clone()
|
||||||
};
|
};
|
||||||
let record: Option<UsersSchema> = db.update(record_key).merge(merged).await?;
|
let record: Option<UsersSchema> = db.update(record_key).merge(merged).await?;
|
||||||
@@ -264,7 +272,7 @@ impl<'a> UsersRepository<'a> {
|
|||||||
let db = &self.state.surrealdb_ws;
|
let db = &self.state.surrealdb_ws;
|
||||||
let user = self.query_user_by_email(email.clone()).await?;
|
let user = self.query_user_by_email(email.clone()).await?;
|
||||||
if user.is_deleted {
|
if user.is_deleted {
|
||||||
bail!("User not found");
|
bail!("User already deleted");
|
||||||
}
|
}
|
||||||
let record_key = get_id(&user.id)?;
|
let record_key = get_id(&user.id)?;
|
||||||
let record: Option<UsersSchema> = db
|
let record: Option<UsersSchema> = db
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ async fn test_delete_user_twice_should_fail_on_second_attempt() {
|
|||||||
assert!(first.is_ok());
|
assert!(first.is_ok());
|
||||||
let second = repo.query_delete_user(user.id.id.to_raw()).await;
|
let second = repo.query_delete_user(user.id.id.to_raw()).await;
|
||||||
assert!(second.is_err());
|
assert!(second.is_err());
|
||||||
assert_eq!(second.unwrap_err().to_string(), "User not found");
|
assert_eq!(second.unwrap_err().to_string(), "User already deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ impl UsersService {
|
|||||||
pub async fn get_user_by_id(state: &AppState, id: String) -> Response {
|
pub async fn get_user_by_id(state: &AppState, id: String) -> Response {
|
||||||
let repo = UsersRepository::new(state);
|
let repo = UsersRepository::new(state);
|
||||||
match repo.query_user_by_id(id).await {
|
match repo.query_user_by_id(id).await {
|
||||||
Ok(user) => success_response(ResponseSuccessDto {
|
Ok(user) if !user.is_deleted => success_response(ResponseSuccessDto {
|
||||||
data: UsersDetailItemDto {
|
data: UsersDetailItemDto {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
@@ -54,6 +54,7 @@ impl UsersService {
|
|||||||
birthdate: user.birthdate,
|
birthdate: user.birthdate,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
Ok(_) => common_response(StatusCode::NOT_FOUND, "User not found"),
|
||||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-20
@@ -1,7 +1,30 @@
|
|||||||
use najm_course_api::{get_iso_date, make_thing, Env};
|
use najm_course_api::{get_iso_date, hash_password, Env};
|
||||||
use serde_json::json;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};
|
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, sql::Thing, Surreal};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct UsersSchema {
|
||||||
|
pub id: Thing,
|
||||||
|
pub fullname: String,
|
||||||
|
pub email: String,
|
||||||
|
pub password: String,
|
||||||
|
pub avatar: Option<String>,
|
||||||
|
pub phone_number: String,
|
||||||
|
pub referral_code: Option<String>,
|
||||||
|
pub referred_by: Option<String>,
|
||||||
|
pub identity_number: Option<String>,
|
||||||
|
pub is_active: bool,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub student_type: String,
|
||||||
|
pub religion: Option<String>,
|
||||||
|
pub gender: Option<String>,
|
||||||
|
pub birthdate: Option<String>,
|
||||||
|
pub is_profile_completed: bool,
|
||||||
|
pub role: Thing,
|
||||||
|
pub created_at: String,
|
||||||
|
pub updated_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
@@ -15,6 +38,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
db.use_ns(env.surrealdb_namespace)
|
db.use_ns(env.surrealdb_namespace)
|
||||||
.use_db(env.surrealdb_dbname)
|
.use_db(env.surrealdb_dbname)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let users = vec![
|
let users = vec![
|
||||||
(
|
(
|
||||||
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2",
|
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2",
|
||||||
@@ -35,26 +59,37 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (id, email, name, role_id) in users {
|
|
||||||
db.query("CREATE type::thing('app_users', $id) CONTENT $data")
|
for (id, email, fullname, role_id) in users {
|
||||||
.bind(("id", id))
|
let user = UsersSchema {
|
||||||
.bind((
|
id: Thing::from(("app_users", id)),
|
||||||
"data",
|
fullname: fullname.into(),
|
||||||
json!({
|
email: email.into(),
|
||||||
"email": email,
|
password: hash_password("password").unwrap(),
|
||||||
"name": name,
|
avatar: None,
|
||||||
"phone_number": "081234567890",
|
phone_number: "081234567890".into(),
|
||||||
"student_type": "TNI",
|
referral_code: None,
|
||||||
"role": make_thing("app_roles", role_id),
|
referred_by: None,
|
||||||
"is_deleted": false,
|
identity_number: None,
|
||||||
"created_at": get_iso_date(),
|
is_active: true,
|
||||||
"updated_at": get_iso_date(),
|
is_deleted: false,
|
||||||
}),
|
student_type: "TNI".into(),
|
||||||
))
|
religion: None,
|
||||||
|
gender: None,
|
||||||
|
birthdate: None,
|
||||||
|
is_profile_completed: false,
|
||||||
|
role: Thing::from(("app_roles", role_id)),
|
||||||
|
created_at: get_iso_date(),
|
||||||
|
updated_at: get_iso_date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
db.create::<Option<UsersSchema>>(("app_users", id))
|
||||||
|
.content(user)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!("✅ Inserted user: {} ({})", name, email);
|
println!("✅ Inserted user: {} ({})", fullname, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("✅ Semua users berhasil disimpan ke SurrealDB!");
|
println!("✅ Semua users berhasil disimpan ke SurrealDB!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user