chore: update query list
This commit is contained in:
@@ -102,6 +102,14 @@ For production deployment:
|
|||||||
|
|
||||||
Adjust the port and environment variables as needed.
|
Adjust the port and environment variables as needed.
|
||||||
|
|
||||||
|
## How to Run the Tests
|
||||||
|
|
||||||
|
1. **Run the tests**:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
1. **Fork the repository** and clone it locally.
|
1. **Fork the repository** and clone it locally.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl GachaClaimItemDto {
|
|||||||
Self {
|
Self {
|
||||||
id: dto.id.id.to_raw(),
|
id: dto.id.id.to_raw(),
|
||||||
user: UsersDetailItemDto::from(&dto.user),
|
user: UsersDetailItemDto::from(&dto.user),
|
||||||
item: GachaItemDto::from(&dto.item),
|
item: GachaItemDto::from(dto.item.clone()),
|
||||||
is_deleted: dto.is_deleted,
|
is_deleted: dto.is_deleted,
|
||||||
created_at: dto.created_at.clone(),
|
created_at: dto.created_at.clone(),
|
||||||
updated_at: dto.updated_at.clone(),
|
updated_at: dto.updated_at.clone(),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub struct GachaItemDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GachaItemDto {
|
impl GachaItemDto {
|
||||||
pub fn from(dto: &GachaItemSchema) -> Self {
|
pub fn from(dto: GachaItemSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: dto.id.id.to_raw(),
|
id: dto.id.id.to_raw(),
|
||||||
name: dto.name.clone(),
|
name: dto.name.clone(),
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use super::GachaItemSchema;
|
use super::GachaItemSchema;
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, GachaItemDto, MetaRequestDto, ResourceEnum, ResponseListSuccessDto,
|
AppState, GachaItemDto, MetaRequestDto, ResourceEnum, ResponseListSuccessDto,
|
||||||
get_id, make_thing, query_list_with_meta,
|
get_id, make_thing,
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
use imphnen_iam::QueryListBuilder;
|
||||||
|
|
||||||
pub struct GachaItemRepository<'a> {
|
pub struct GachaItemRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
@@ -19,24 +20,23 @@ impl<'a> GachaItemRepository<'a> {
|
|||||||
meta: MetaRequestDto,
|
meta: MetaRequestDto,
|
||||||
) -> Result<ResponseListSuccessDto<Vec<GachaItemDto>>> {
|
) -> Result<ResponseListSuccessDto<Vec<GachaItemDto>>> {
|
||||||
let raw_result: ResponseListSuccessDto<Vec<GachaItemSchema>> =
|
let raw_result: ResponseListSuccessDto<Vec<GachaItemSchema>> =
|
||||||
query_list_with_meta(
|
QueryListBuilder::new(
|
||||||
&self.state.surrealdb_ws,
|
&self.state.surrealdb_ws,
|
||||||
&ResourceEnum::GachaItems.to_string(),
|
&ResourceEnum::GachaItems.to_string(),
|
||||||
&meta,
|
&meta,
|
||||||
vec!["is_deleted = false".into()],
|
|
||||||
None,
|
|
||||||
"name",
|
|
||||||
Some(vec!["*"]),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
|
.with_condition("is_deleted = false")
|
||||||
|
.search_field("name")
|
||||||
|
.select_fields(vec!["*"])
|
||||||
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
let transformed_data = raw_result
|
let data = raw_result
|
||||||
.data
|
.data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|gacha_item| GachaItemDto::from(&gacha_item))
|
.map(GachaItemDto::from)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(ResponseListSuccessDto {
|
Ok(ResponseListSuccessDto {
|
||||||
data: transformed_data,
|
data,
|
||||||
meta: raw_result.meta,
|
meta: raw_result.meta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ impl GachaItemService {
|
|||||||
let repo = GachaItemRepository::new(state);
|
let repo = GachaItemRepository::new(state);
|
||||||
match repo.query_gacha_item_by_id(id).await {
|
match repo.query_gacha_item_by_id(id).await {
|
||||||
Ok(item) => success_response(ResponseSuccessDto {
|
Ok(item) => success_response(ResponseSuccessDto {
|
||||||
data: GachaItemDto::from(&item),
|
data: GachaItemDto::from(item),
|
||||||
}),
|
}),
|
||||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ impl GachaRollItemDto {
|
|||||||
pub fn from(dto: &GachaRollQueryDto) -> Self {
|
pub fn from(dto: &GachaRollQueryDto) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: dto.id.id.to_raw(),
|
id: dto.id.id.to_raw(),
|
||||||
item: GachaItemDto::from(&dto.item),
|
item: GachaItemDto::from(dto.item.clone()),
|
||||||
weight: dto.weight.to_string(),
|
weight: dto.weight.to_string(),
|
||||||
quantity: dto.quantity,
|
quantity: dto.quantity,
|
||||||
is_deleted: dto.is_deleted,
|
is_deleted: dto.is_deleted,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use super::{PermissionsItemDto, PermissionsSchema};
|
use super::{PermissionsItemDto, PermissionsSchema};
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
|
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id, make_thing,
|
||||||
make_thing, query_list_with_meta,
|
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use imphnen_utils::{DetailQueryBuilder, extract_id};
|
use imphnen_utils::{DetailQueryBuilder, QueryListBuilder, extract_id};
|
||||||
|
|
||||||
pub struct PermissionsRepository<'a> {
|
pub struct PermissionsRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
@@ -20,22 +19,23 @@ impl<'a> PermissionsRepository<'a> {
|
|||||||
meta: MetaRequestDto,
|
meta: MetaRequestDto,
|
||||||
) -> Result<ResponseListSuccessDto<Vec<PermissionsItemDto>>> {
|
) -> Result<ResponseListSuccessDto<Vec<PermissionsItemDto>>> {
|
||||||
let raw_result: ResponseListSuccessDto<Vec<PermissionsSchema>> =
|
let raw_result: ResponseListSuccessDto<Vec<PermissionsSchema>> =
|
||||||
query_list_with_meta(
|
QueryListBuilder::new(
|
||||||
&self.state.surrealdb_ws,
|
&self.state.surrealdb_ws,
|
||||||
&ResourceEnum::Permissions.to_string(),
|
&ResourceEnum::Permissions.to_string(),
|
||||||
&meta,
|
&meta,
|
||||||
vec!["is_deleted = false".into()],
|
|
||||||
None,
|
|
||||||
"name",
|
|
||||||
Some(vec!["*"]),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
|
.with_condition("is_deleted = false")
|
||||||
|
.search_field("name")
|
||||||
|
.select_fields(vec!["*"])
|
||||||
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let transformed_data = raw_result
|
let transformed_data = raw_result
|
||||||
.data
|
.data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|permission| PermissionsSchema::list(&permission))
|
.map(|permission| PermissionsSchema::list(&permission))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(ResponseListSuccessDto {
|
Ok(ResponseListSuccessDto {
|
||||||
data: transformed_data,
|
data: transformed_data,
|
||||||
meta: raw_result.meta,
|
meta: raw_result.meta,
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ use super::{
|
|||||||
RolesRequestUpdateDto, RolesSchema,
|
RolesRequestUpdateDto, RolesSchema,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, MetaRequestDto, PermissionsItemDto, ResourceEnum,
|
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id, make_thing,
|
||||||
ResponseListSuccessDto, extract_id, get_id, make_thing, query_list_with_meta,
|
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use imphnen_utils::DetailQueryBuilder;
|
use imphnen_utils::{DetailQueryBuilder, QueryListBuilder};
|
||||||
use surrealdb::Uuid;
|
use surrealdb::Uuid;
|
||||||
use surrealdb::sql::Thing;
|
use surrealdb::sql::Thing;
|
||||||
|
|
||||||
@@ -24,36 +23,23 @@ impl<'a> RolesRepository<'a> {
|
|||||||
&self,
|
&self,
|
||||||
meta: MetaRequestDto,
|
meta: MetaRequestDto,
|
||||||
) -> Result<ResponseListSuccessDto<Vec<RolesListItemDto>>> {
|
) -> Result<ResponseListSuccessDto<Vec<RolesListItemDto>>> {
|
||||||
let mut conditions = vec!["is_deleted = false".into()];
|
let result: ResponseListSuccessDto<Vec<RolesSchema>> = QueryListBuilder::new(
|
||||||
if let Some(_search) = meta.search.as_deref().filter(|s| !s.is_empty()) {
|
|
||||||
conditions.push("string::contains(name ?? '', $search)".into());
|
|
||||||
}
|
|
||||||
if let (Some(filter_by), Some(filter_val)) =
|
|
||||||
(meta.filter_by.as_ref(), meta.filter.as_ref())
|
|
||||||
{
|
|
||||||
if !filter_val.is_empty() {
|
|
||||||
conditions.push(format!("{} = $filter", filter_by));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let raw_result: ResponseListSuccessDto<Vec<RolesSchema>> = query_list_with_meta(
|
|
||||||
&self.state.surrealdb_ws,
|
&self.state.surrealdb_ws,
|
||||||
&ResourceEnum::Roles.to_string(),
|
&ResourceEnum::Roles.to_string(),
|
||||||
&meta,
|
&meta,
|
||||||
conditions,
|
|
||||||
None,
|
|
||||||
"name",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
|
.with_condition("is_deleted = false")
|
||||||
|
.search_field("name")
|
||||||
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
let data = raw_result
|
let data = result
|
||||||
.data
|
.data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|role| RolesSchema::list(&role))
|
.map(|role| RolesSchema::list(&role))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(ResponseListSuccessDto {
|
Ok(ResponseListSuccessDto {
|
||||||
data,
|
data,
|
||||||
meta: raw_result.meta,
|
meta: result.meta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,16 +51,8 @@ impl<'a> RolesRepository<'a> {
|
|||||||
let builder = DetailQueryBuilder::new(ResourceEnum::Roles.to_string())
|
let builder = DetailQueryBuilder::new(ResourceEnum::Roles.to_string())
|
||||||
.with_where("name")
|
.with_where("name")
|
||||||
.where_value(name.clone())
|
.where_value(name.clone())
|
||||||
.with_select_fields(vec![
|
.with_select_fields(vec!["*"])
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"permissions",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"is_deleted",
|
|
||||||
])
|
|
||||||
.with_fetch("permissions");
|
.with_fetch("permissions");
|
||||||
|
|
||||||
let sql = builder.build();
|
let sql = builder.build();
|
||||||
let result: Option<RolesDetailQueryDto> = builder
|
let result: Option<RolesDetailQueryDto> = builder
|
||||||
.apply_bindings(db.query(sql).bind(("name", name)))
|
.apply_bindings(db.query(sql).bind(("name", name)))
|
||||||
@@ -84,38 +62,14 @@ impl<'a> RolesRepository<'a> {
|
|||||||
Some(r) if !r.is_deleted => r,
|
Some(r) if !r.is_deleted => r,
|
||||||
_ => bail!("Role not found"),
|
_ => bail!("Role not found"),
|
||||||
};
|
};
|
||||||
let permissions = role
|
Ok(RolesDetailItemDto::from(&role))
|
||||||
.permissions
|
|
||||||
.into_iter()
|
|
||||||
.map(|perm| PermissionsItemDto {
|
|
||||||
id: extract_id(&perm.id),
|
|
||||||
name: perm.name,
|
|
||||||
created_at: perm.created_at,
|
|
||||||
updated_at: perm.updated_at,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(RolesDetailItemDto {
|
|
||||||
id: extract_id(&role.id),
|
|
||||||
name: role.name,
|
|
||||||
is_deleted: role.is_deleted,
|
|
||||||
permissions,
|
|
||||||
created_at: role.created_at,
|
|
||||||
updated_at: role.updated_at,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn query_role_by_id(&self, id: String) -> Result<RolesDetailItemDto> {
|
pub async fn query_role_by_id(&self, id: String) -> Result<RolesDetailItemDto> {
|
||||||
let db = &self.state.surrealdb_ws;
|
let db = &self.state.surrealdb_ws;
|
||||||
let builder = DetailQueryBuilder::new(ResourceEnum::Roles.to_string())
|
let builder = DetailQueryBuilder::new(ResourceEnum::Roles.to_string())
|
||||||
.with_id(&id)
|
.with_id(&id)
|
||||||
.with_select_fields(vec![
|
.with_select_fields(vec!["*"])
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"is_deleted",
|
|
||||||
"permissions",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
])
|
|
||||||
.with_fetch("permissions");
|
.with_fetch("permissions");
|
||||||
let sql = builder.build();
|
let sql = builder.build();
|
||||||
let result: Option<RolesDetailQueryDto> =
|
let result: Option<RolesDetailQueryDto> =
|
||||||
@@ -124,24 +78,7 @@ impl<'a> RolesRepository<'a> {
|
|||||||
Some(r) if !r.is_deleted => r,
|
Some(r) if !r.is_deleted => r,
|
||||||
_ => bail!("Role not found"),
|
_ => bail!("Role not found"),
|
||||||
};
|
};
|
||||||
let permissions = role
|
Ok(RolesDetailItemDto::from(&role))
|
||||||
.permissions
|
|
||||||
.into_iter()
|
|
||||||
.map(|perm| PermissionsItemDto {
|
|
||||||
id: extract_id(&perm.id),
|
|
||||||
name: perm.name,
|
|
||||||
created_at: perm.created_at,
|
|
||||||
updated_at: perm.updated_at,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(RolesDetailItemDto {
|
|
||||||
id: extract_id(&role.id),
|
|
||||||
name: role.name,
|
|
||||||
is_deleted: role.is_deleted,
|
|
||||||
permissions,
|
|
||||||
created_at: role.created_at,
|
|
||||||
updated_at: role.updated_at,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn query_create_role(
|
pub async fn query_create_role(
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use super::{UsersDetailQueryDto, UsersListItemDto, UsersListQueryDto, UsersSchema};
|
use super::{UsersDetailQueryDto, UsersListItemDto, UsersListQueryDto, UsersSchema};
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
|
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id, make_thing,
|
||||||
make_thing, query_list_with_meta,
|
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use imphnen_utils::DetailQueryBuilder;
|
use imphnen_utils::{DetailQueryBuilder, QueryListBuilder};
|
||||||
use surrealdb::{Surreal, engine::remote::ws::Client};
|
use surrealdb::{Surreal, engine::remote::ws::Client};
|
||||||
|
|
||||||
pub struct UsersRepository<'a> {
|
pub struct UsersRepository<'a> {
|
||||||
@@ -35,23 +34,23 @@ impl<'a> UsersRepository<'a> {
|
|||||||
&self,
|
&self,
|
||||||
meta: MetaRequestDto,
|
meta: MetaRequestDto,
|
||||||
) -> Result<ResponseListSuccessDto<Vec<UsersListItemDto>>> {
|
) -> Result<ResponseListSuccessDto<Vec<UsersListItemDto>>> {
|
||||||
let db = &self.state.surrealdb_ws;
|
let result: ResponseListSuccessDto<Vec<UsersListQueryDto>> =
|
||||||
let result = query_list_with_meta::<UsersListQueryDto>(
|
QueryListBuilder::new(
|
||||||
db,
|
&self.state.surrealdb_ws,
|
||||||
&ResourceEnum::Users.to_string(),
|
&ResourceEnum::Users.to_string(),
|
||||||
&meta,
|
&meta,
|
||||||
vec!["is_deleted = false".into()],
|
)
|
||||||
None,
|
.with_condition("is_deleted = false")
|
||||||
"fullname",
|
.search_field("fullname")
|
||||||
Some(vec!["*"]),
|
.select_fields(vec!["*"])
|
||||||
Some(vec!["role", "role.permissions"]),
|
.fetch_fields(vec!["role", "role.permissions"])
|
||||||
)
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
let data = result
|
let data = result
|
||||||
.data
|
.data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(UsersListQueryDto::from)
|
.map(UsersListQueryDto::from)
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
Ok(ResponseListSuccessDto {
|
Ok(ResponseListSuccessDto {
|
||||||
data,
|
data,
|
||||||
meta: result.meta,
|
meta: result.meta,
|
||||||
|
|||||||
+101
-63
@@ -1,78 +1,116 @@
|
|||||||
use super::{ListQueryBuilder, bind_filter_value};
|
|
||||||
use crate::{CountResult, MetaRequestDto, MetaResponseDto, ResponseListSuccessDto};
|
use crate::{CountResult, MetaRequestDto, MetaResponseDto, ResponseListSuccessDto};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use surrealdb::{Surreal, engine::remote::ws::Client};
|
use surrealdb::{Surreal, engine::remote::ws::Client};
|
||||||
|
|
||||||
pub async fn query_list_with_meta<T>(
|
pub struct QueryListBuilder<'a> {
|
||||||
db: &Surreal<Client>,
|
db: &'a Surreal<Client>,
|
||||||
table: &str,
|
table: &'a str,
|
||||||
meta: &MetaRequestDto,
|
meta: &'a MetaRequestDto,
|
||||||
conditions: Vec<String>,
|
conditions: Vec<String>,
|
||||||
custom_select: Option<String>,
|
search_field: String,
|
||||||
search_field: &str,
|
select_fields: Option<Vec<&'a str>>,
|
||||||
select_fields: Option<Vec<&str>>,
|
fetch_fields: Option<Vec<&'a str>>,
|
||||||
fetch_fields: Option<Vec<&str>>,
|
}
|
||||||
) -> Result<ResponseListSuccessDto<Vec<T>>>
|
|
||||||
where
|
|
||||||
T: DeserializeOwned + Serialize,
|
|
||||||
{
|
|
||||||
let page = meta.page.unwrap_or(1).max(1);
|
|
||||||
let per_page = meta.per_page.unwrap_or(10).max(1);
|
|
||||||
let start = (page - 1) * per_page;
|
|
||||||
|
|
||||||
let sql = custom_select.unwrap_or_else(|| {
|
impl<'a> QueryListBuilder<'a> {
|
||||||
ListQueryBuilder::from_meta(
|
pub fn new(
|
||||||
|
db: &'a Surreal<Client>,
|
||||||
|
table: &'a str,
|
||||||
|
meta: &'a MetaRequestDto,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
db,
|
||||||
table,
|
table,
|
||||||
meta,
|
meta,
|
||||||
search_field,
|
conditions: vec![],
|
||||||
select_fields,
|
search_field: "name".to_string(),
|
||||||
fetch_fields,
|
select_fields: None,
|
||||||
|
fetch_fields: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search_field(mut self, field: &'a str) -> Self {
|
||||||
|
self.search_field = field.to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_fields(mut self, fields: Vec<&'a str>) -> Self {
|
||||||
|
self.select_fields = Some(fields);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_fields(mut self, fields: Vec<&'a str>) -> Self {
|
||||||
|
self.fetch_fields = Some(fields);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_condition(mut self, condition: &str) -> Self {
|
||||||
|
self.conditions.push(condition.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn build<T>(self) -> Result<ResponseListSuccessDto<Vec<T>>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned + Serialize,
|
||||||
|
{
|
||||||
|
let page = self.meta.page.unwrap_or(1).max(1);
|
||||||
|
let per_page = self.meta.per_page.unwrap_or(10).max(1);
|
||||||
|
let start = (page - 1) * per_page;
|
||||||
|
|
||||||
|
let sql = crate::ListQueryBuilder::from_meta(
|
||||||
|
self.table,
|
||||||
|
self.meta,
|
||||||
|
&self.search_field,
|
||||||
|
self.select_fields,
|
||||||
|
self.fetch_fields,
|
||||||
)
|
)
|
||||||
.build()
|
.build();
|
||||||
});
|
|
||||||
|
|
||||||
let mut query_exec = db.query(sql);
|
let mut query_exec = self.db.query(sql);
|
||||||
if let Some(search) = &meta.search {
|
if let Some(search) = &self.meta.search {
|
||||||
if !search.is_empty() {
|
if !search.is_empty() {
|
||||||
query_exec = query_exec.bind(("search", search.to_lowercase().clone()));
|
query_exec = query_exec.bind(("search", search.to_lowercase()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if let Some(filter_val) = &self.meta.filter {
|
||||||
if let Some(filter_val) = &meta.filter {
|
query_exec = crate::bind_filter_value(query_exec, filter_val.clone());
|
||||||
query_exec = bind_filter_value(query_exec, filter_val.clone());
|
|
||||||
}
|
|
||||||
query_exec = query_exec
|
|
||||||
.bind(("per_page", per_page))
|
|
||||||
.bind(("start", start));
|
|
||||||
|
|
||||||
let raw: Vec<T> = query_exec.await?.take(0)?;
|
|
||||||
|
|
||||||
let mut count_query = db.query(format!(
|
|
||||||
"SELECT count() FROM {} {}",
|
|
||||||
table,
|
|
||||||
if conditions.is_empty() {
|
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
format!("WHERE {}", conditions.join(" AND "))
|
|
||||||
}
|
}
|
||||||
));
|
query_exec = query_exec
|
||||||
if let Some(search) = &meta.search {
|
.bind(("per_page", per_page))
|
||||||
if !search.is_empty() {
|
.bind(("start", start));
|
||||||
count_query = count_query.bind(("search", search.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(filter_val) = &meta.filter {
|
|
||||||
count_query = bind_filter_value(count_query, filter_val.clone());
|
|
||||||
}
|
|
||||||
let count_result: Vec<CountResult> = count_query.await?.take(0)?;
|
|
||||||
let total = count_result.first().map(|c| c.count);
|
|
||||||
|
|
||||||
Ok(ResponseListSuccessDto {
|
let raw: Vec<T> = query_exec.await?.take(0)?;
|
||||||
data: raw,
|
|
||||||
meta: Some(MetaResponseDto {
|
let mut count_query = self.db.query(format!(
|
||||||
page: Some(page),
|
"SELECT count() FROM {} {}",
|
||||||
per_page: Some(per_page),
|
self.table,
|
||||||
total,
|
if self.conditions.is_empty() {
|
||||||
}),
|
"".into()
|
||||||
})
|
} else {
|
||||||
|
format!("WHERE {}", self.conditions.join(" AND "))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
if let Some(search) = &self.meta.search {
|
||||||
|
if !search.is_empty() {
|
||||||
|
count_query = count_query.bind(("search", search.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(filter_val) = &self.meta.filter {
|
||||||
|
count_query = crate::bind_filter_value(count_query, filter_val.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let count_result: Vec<CountResult> = count_query.await?.take(0)?;
|
||||||
|
let total = count_result.first().map(|c| c.count);
|
||||||
|
|
||||||
|
Ok(ResponseListSuccessDto {
|
||||||
|
data: raw,
|
||||||
|
meta: Some(MetaResponseDto {
|
||||||
|
page: Some(page),
|
||||||
|
per_page: Some(per_page),
|
||||||
|
total,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod permissions_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod permissions_repository_test;
|
pub mod permissions_repository_test;
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
create_mock_app_state, permissions_router,
|
|
||||||
v1::{
|
|
||||||
permissions_dto::PermissionsRequestDto,
|
|
||||||
permissions_repository::PermissionsRepository,
|
|
||||||
},
|
|
||||||
AppState,
|
|
||||||
};
|
|
||||||
use axum::{http::StatusCode, Extension, Router};
|
|
||||||
use axum_test::TestServer;
|
|
||||||
|
|
||||||
pub fn create_test_app(state: AppState) -> TestServer {
|
|
||||||
let app = Router::new()
|
|
||||||
.nest("/v1/permissions", permissions_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
TestServer::new(app).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_dummy_permission(
|
|
||||||
repo: &PermissionsRepository<'_>,
|
|
||||||
name: &str,
|
|
||||||
) -> String {
|
|
||||||
let dto = crate::v1::permissions_schema::PermissionsSchema {
|
|
||||||
name: name.into(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let _ = repo.query_create_permission(dto.clone()).await.unwrap();
|
|
||||||
let found = repo.query_permission_by_name(name.into()).await.unwrap();
|
|
||||||
found.id.id.to_raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_dummy_permission(repo: &PermissionsRepository<'_>, id: String) {
|
|
||||||
let _ = repo.query_delete_permission(id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_permission_should_return_201() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = PermissionsRepository::new(&state);
|
|
||||||
let server = create_test_app(state.clone());
|
|
||||||
let payload = PermissionsRequestDto {
|
|
||||||
name: "Create Permission".into(),
|
|
||||||
};
|
|
||||||
let res = server.post("/v1/permissions/create").json(&payload).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::CREATED);
|
|
||||||
let id = repo
|
|
||||||
.query_permission_by_name(payload.name.clone())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.id
|
|
||||||
.id
|
|
||||||
.to_raw();
|
|
||||||
delete_dummy_permission(&repo, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_permission_list_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.get("/v1/permissions?page=1&per_page=10").await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_permission_by_id_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = PermissionsRepository::new(&state);
|
|
||||||
let id = create_dummy_permission(&repo, "Get Permission").await;
|
|
||||||
let server = create_test_app(state.clone());
|
|
||||||
let res = server.get(&format!("/v1/permissions/detail/{}", id)).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
delete_dummy_permission(&repo, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_permission_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = PermissionsRepository::new(&state);
|
|
||||||
let id = create_dummy_permission(&repo, "To Update").await;
|
|
||||||
let server = create_test_app(state.clone());
|
|
||||||
let payload = PermissionsRequestDto {
|
|
||||||
name: "Updated Permission".into(),
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/permissions/update/{}", id.clone()))
|
|
||||||
.json(&payload)
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
delete_dummy_permission(&repo, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_permission_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = PermissionsRepository::new(&state);
|
|
||||||
let id = create_dummy_permission(&repo, "To Delete").await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server
|
|
||||||
.delete(&format!("/v1/permissions/delete/{}", id))
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_duplicate_permission_should_return_409() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = PermissionsRepository::new(&state);
|
|
||||||
let name = "Duplicate Permission";
|
|
||||||
let id = create_dummy_permission(&repo, name).await;
|
|
||||||
let server = create_test_app(state.clone());
|
|
||||||
let payload = PermissionsRequestDto { name: name.into() };
|
|
||||||
let res = server.post("/v1/permissions/create").json(&payload).await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::CONFLICT);
|
|
||||||
delete_dummy_permission(&repo, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_nonexistent_permission_should_return_404() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.delete("/v1/permissions/delete/nonexistent-id").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_permission_with_empty_name_should_return_400() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let payload = PermissionsRequestDto { name: "".into() };
|
|
||||||
let res = server.post("/v1/permissions/create").json(&payload).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_nonexistent_permission_should_return_404() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let payload = PermissionsRequestDto {
|
|
||||||
name: "Nonexistent".into(),
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put("/v1/permissions/update/non-exist")
|
|
||||||
.json(&payload)
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_permission_by_invalid_id_should_return_404() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.get("/v1/permissions/detail/invalid-id").await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_permission_list_with_invalid_pagination_should_return_400() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.get("/v1/permissions?page=abc&per_page=xyz").await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,2 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod roles_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod roles_repository_test;
|
pub mod roles_repository_test;
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
use crate::AppState;
|
|
||||||
use crate::{
|
|
||||||
RolesRequestCreateDto, RolesRequestUpdateDto, create_mock_app_state, roles_router,
|
|
||||||
v1::roles_repository::RolesRepository,
|
|
||||||
};
|
|
||||||
use axum::Extension;
|
|
||||||
use axum::Router;
|
|
||||||
use axum::http::StatusCode;
|
|
||||||
use axum_test::TestServer;
|
|
||||||
|
|
||||||
pub fn create_test_app(state: AppState) -> TestServer {
|
|
||||||
let app = Router::new()
|
|
||||||
.nest("/v1/roles", roles_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
|
|
||||||
TestServer::new(app).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_dummy_role(state: AppState, id: String) {
|
|
||||||
let repo = RolesRepository::new(&state);
|
|
||||||
let _ = repo.query_delete_role(id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_role_list_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/roles", roles_router())
|
|
||||||
.layer(axum::Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/roles?page=1&per_page=10").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_post_create_role_should_return_201() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/roles", roles_router())
|
|
||||||
.layer(axum::Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = RolesRequestCreateDto {
|
|
||||||
name: "Test Role #1".into(),
|
|
||||||
permissions: vec![],
|
|
||||||
};
|
|
||||||
let res = server.post("/v1/roles/create").json(&payload).await;
|
|
||||||
let repo = RolesRepository::new(&state);
|
|
||||||
assert_eq!(res.status_code(), StatusCode::CREATED);
|
|
||||||
let id = repo
|
|
||||||
.query_role_by_name(payload.name.clone())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.id;
|
|
||||||
delete_dummy_role(state, id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_put_update_role_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = RolesRepository::new(&state);
|
|
||||||
let _ = repo
|
|
||||||
.query_create_role(RolesRequestCreateDto {
|
|
||||||
name: "Role To Update".into(),
|
|
||||||
permissions: vec![],
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let existing = repo
|
|
||||||
.query_role_by_name("Role To Update".into())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let id = existing.id.clone();
|
|
||||||
let server = create_test_app(state.clone());
|
|
||||||
let payload = RolesRequestUpdateDto {
|
|
||||||
name: Some("Updated Role".into()),
|
|
||||||
permissions: Some(vec![]),
|
|
||||||
overwrite: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/roles/update/{}", id))
|
|
||||||
.json(&payload)
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
let id = repo
|
|
||||||
.query_role_by_name(payload.name.clone().unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.id;
|
|
||||||
delete_dummy_role(state.clone(), id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_role_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let repo = RolesRepository::new(&state);
|
|
||||||
let _ = repo
|
|
||||||
.query_create_role(RolesRequestCreateDto {
|
|
||||||
name: "Role To Delete".into(),
|
|
||||||
permissions: vec![],
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let existing = repo
|
|
||||||
.query_role_by_name("Role To Delete".into())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let id = existing.id;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.delete(&format!("/v1/roles/delete/{}", id)).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_post_create_role_with_empty_name_should_return_400() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let payload = RolesRequestCreateDto {
|
|
||||||
name: "".into(),
|
|
||||||
permissions: vec![],
|
|
||||||
};
|
|
||||||
let res = server.post("/v1/roles/create").json(&payload).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_put_update_nonexistent_role_should_return_404() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let payload = RolesRequestUpdateDto {
|
|
||||||
name: Some("Does Not Exist".into()),
|
|
||||||
permissions: Some(vec![]),
|
|
||||||
overwrite: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put("/v1/roles/update/nonexistent-id")
|
|
||||||
.json(&payload)
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_nonexistent_role_should_return_404() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.delete("/v1/roles/delete/nonexistent-id").await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_role_list_with_invalid_page_params_should_return_400() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let res = server.get("/v1/roles?page=abc&per_page=xyz").await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_duplicate_role_should_return_409() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let server = create_test_app(state);
|
|
||||||
let payload = RolesRequestCreateDto {
|
|
||||||
name: "Admin".into(),
|
|
||||||
permissions: vec![],
|
|
||||||
};
|
|
||||||
let res = server.post("/v1/roles/create").json(&payload).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::CONFLICT);
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use surrealdb::Uuid;
|
use surrealdb::Uuid;
|
||||||
use surrealdb::sql::Thing;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_create_role_should_succeed() {
|
async fn test_query_create_role_should_succeed() {
|
||||||
@@ -19,15 +18,15 @@ async fn test_query_create_role_should_succeed() {
|
|||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let perm_id = Uuid::new_v4().to_string();
|
let perm_id = Uuid::new_v4().to_string();
|
||||||
let permission = PermissionsSchema {
|
let permission = PermissionsSchema {
|
||||||
id: Thing::from((ResourceEnum::Permissions.to_string(), perm_id.clone())),
|
id: make_thing(&ResourceEnum::Permissions.to_string(), &perm_id.clone()),
|
||||||
name: "Read Quiz".into(),
|
name: "Read Quiz".into(),
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
created_at: Some(get_iso_date()),
|
created_at: Some(get_iso_date()),
|
||||||
updated_at: None,
|
updated_at: Some(get_iso_date()),
|
||||||
};
|
};
|
||||||
perm_repo.query_create_permission(permission).await.unwrap();
|
perm_repo.query_create_permission(permission).await.unwrap();
|
||||||
let payload = RolesRequestCreateDto {
|
let payload = RolesRequestCreateDto {
|
||||||
name: "Student".into(),
|
name: "User".into(),
|
||||||
permissions: vec![perm_id.clone()],
|
permissions: vec![perm_id.clone()],
|
||||||
};
|
};
|
||||||
let result = role_repo.query_create_role(payload).await;
|
let result = role_repo.query_create_role(payload).await;
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod users_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod users_repository_test;
|
pub mod users_repository_test;
|
||||||
|
|||||||
@@ -1,602 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
RolesEnum, RolesRepository, UsersActiveInactiveRequestDto, UsersCreateRequestDto,
|
|
||||||
UsersRepository, UsersUpdateRequestDto, users_router,
|
|
||||||
};
|
|
||||||
use axum::{Extension, http::StatusCode};
|
|
||||||
use axum_test::TestServer;
|
|
||||||
use imphnen_utils::create_mock_app_state;
|
|
||||||
use surrealdb::Uuid;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_user_list_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users?page=1&per_page=10").await;
|
|
||||||
let status = res.status_code();
|
|
||||||
assert_eq!(status, StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_fail_with_invalid_per_page() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users?page=1&per_page=0").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_fail_with_invalid_page() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_ignore_invalid_sort_field() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&sort_by=invalid_field")
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_with_search_no_match_should_return_empty() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&search=nonexistinguserxyz")
|
|
||||||
.await;
|
|
||||||
let body: serde_json::Value = res.json();
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
assert_eq!(body["data"].as_array().unwrap().len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_return_empty_on_invalid_filter() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&filter_by=is_active&filter=maybe")
|
|
||||||
.await;
|
|
||||||
let body: serde_json::Value = res.json();
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
assert_eq!(body["data"].as_array().unwrap().len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_user_list_with_search_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&search=maulana")
|
|
||||||
.await;
|
|
||||||
let status = res.status_code();
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(status, StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_user_should_return_201() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Create Data #1".into(),
|
|
||||||
email: format!("test-{}@create.com", Uuid::new_v4()).into(),
|
|
||||||
role_id,
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
let res = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::CREATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_user_detail_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id;
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Detail User".into(),
|
|
||||||
email: "detail@test.com".into(),
|
|
||||||
role_id,
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
let user_id = user.id.id.to_raw();
|
|
||||||
let res = server.get(&format!("/v1/users/detail/{}", user_id)).await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_user_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.expect("Failed to get role");
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Test Delete Me".into(),
|
|
||||||
email: "test@delete-me.com".into(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
role_id: role.id.clone(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
let create_res = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
assert_eq!(create_res.status_code(), StatusCode::CREATED);
|
|
||||||
let detail_res = repo.query_user_by_email(payload.email.clone()).await;
|
|
||||||
assert!(detail_res.is_ok(), "User not found after creation");
|
|
||||||
let user = detail_res.unwrap();
|
|
||||||
let user_id_for_delete = user.id.id.to_raw();
|
|
||||||
let delete_res = server
|
|
||||||
.delete(&format!("/v1/users/delete/{}", user_id_for_delete))
|
|
||||||
.await;
|
|
||||||
assert_eq!(delete_res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_activate_user_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id;
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Inactive User".into(),
|
|
||||||
email: format!("inactive-{}@test.com", Uuid::new_v4()).into(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
role_id,
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: false,
|
|
||||||
};
|
|
||||||
let res_create = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
dbg!(res_create.text());
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
dbg!(user.fullname);
|
|
||||||
let user_id = user.id.id.to_raw();
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/users/activate/{}", user_id))
|
|
||||||
.json(&UsersActiveInactiveRequestDto { is_active: true })
|
|
||||||
.await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_user_should_return_200() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let unique_email = format!("update_{}@test.com", Uuid::new_v4());
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Old Name".into(),
|
|
||||||
email: unique_email.clone(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
role_id: role_id.clone(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
let create_res = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
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 update_payload = UsersUpdateRequestDto {
|
|
||||||
fullname: "Updated Name".into(),
|
|
||||||
email: payload.email.clone(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
role_id: user.role.id.id.to_raw(),
|
|
||||||
is_active: true,
|
|
||||||
gender: Some("Laki-laki".into()),
|
|
||||||
birthdate: Some("2000-01-01".into()),
|
|
||||||
avatar: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/users/update/{}", user_id))
|
|
||||||
.json(&update_payload)
|
|
||||||
.await;
|
|
||||||
let status = res.status_code();
|
|
||||||
let body = res.text();
|
|
||||||
assert_eq!(status, StatusCode::OK, "Response body: {}", body);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_user_should_fail_if_email_taken() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let email = format!("test_{}@example.com", Uuid::new_v4());
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "User Satu".into(),
|
|
||||||
email: email.clone(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
role_id,
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
let res1 = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
assert_eq!(res1.status_code(), StatusCode::CREATED);
|
|
||||||
let res2 = server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let body2 = res2.text();
|
|
||||||
assert_eq!(res2.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
assert!(body2.contains("User already exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_user_detail_should_fail_if_not_found() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users/detail/non-existent-id").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_delete_user_should_fail_if_already_deleted() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "To Be Deleted".into(),
|
|
||||||
email: "delete-me@example.com".into(),
|
|
||||||
role_id,
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
let id = user.id.id.to_raw();
|
|
||||||
let res1 = server.delete(&format!("/v1/users/delete/{}", id)).await;
|
|
||||||
assert_eq!(res1.status_code(), StatusCode::OK);
|
|
||||||
let res2 = server.delete(&format!("/v1/users/delete/{}", id)).await;
|
|
||||||
assert_eq!(res2.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res2.text();
|
|
||||||
assert!(body.contains("User already deleted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_user_should_fail_if_user_not_found() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let update_payload = UsersUpdateRequestDto {
|
|
||||||
fullname: "Does Not Exist".into(),
|
|
||||||
email: "nonexistent@test.com".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
role_id,
|
|
||||||
is_active: true,
|
|
||||||
gender: Some("Laki-laki".into()),
|
|
||||||
birthdate: Some("2000-01-01".into()),
|
|
||||||
avatar: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put("/v1/users/update/non-existent-id")
|
|
||||||
.json(&update_payload)
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_activate_user_should_fail_if_user_not_found() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersActiveInactiveRequestDto { is_active: true };
|
|
||||||
let res = server
|
|
||||||
.put("/v1/users/activate/non-existent-id")
|
|
||||||
.json(&payload)
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_create_user_should_fail_if_payload_invalid() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let invalid_payload = serde_json::json!({});
|
|
||||||
let res = server.post("/v1/users/create").json(&invalid_payload).await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::UNPROCESSABLE_ENTITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_fail_with_invalid_pagination() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users?page=0&per_page=0").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(
|
|
||||||
body.contains("Invalid pagination") || body.contains("per_page"),
|
|
||||||
"Expected pagination error, got: {body}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_should_fallback_on_invalid_order() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&sort_by=email&order=invalid")
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_list_users_with_invalid_filter_by_should_return_empty() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server
|
|
||||||
.get("/v1/users?page=1&per_page=10&filter_by=unknown_field&filter=value")
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::OK);
|
|
||||||
let body: serde_json::Value = res.json();
|
|
||||||
assert_eq!(body["data"].as_array().unwrap().len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_user_detail_should_fail_with_invalid_id_format() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users/detail/!@#invalid-id").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found") || body.contains("not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_user_detail_should_fail_if_user_not_found() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let res = server.get("/v1/users/detail/random-id-123456").await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_user_detail_should_fail_if_user_is_soft_deleted() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
role_id,
|
|
||||||
fullname: "Soft Deleted".into(),
|
|
||||||
email: "softdeleted@test.com".into(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
let user_id = user.id.id.to_raw();
|
|
||||||
let _ = repo.query_delete_user(user_id.clone()).await.unwrap();
|
|
||||||
let res = server.get(&format!("/v1/users/detail/{}", user_id)).await;
|
|
||||||
dbg!(res.text());
|
|
||||||
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_user_should_fail_if_user_is_deleted() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
role_id: role_id.clone(),
|
|
||||||
fullname: "To Be Deleted".into(),
|
|
||||||
email: "deleteduser@test.com".into(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
let user_id = user.id.id.to_raw();
|
|
||||||
let _ = repo.query_delete_user(user_id.clone()).await.unwrap();
|
|
||||||
let update_payload = UsersUpdateRequestDto {
|
|
||||||
role_id: role_id.clone(),
|
|
||||||
fullname: "Should Fail".into(),
|
|
||||||
email: "deleteduser@test.com".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
gender: Some("Laki-laki".into()),
|
|
||||||
birthdate: Some("2000-01-01".into()),
|
|
||||||
avatar: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/users/update/{}", user_id))
|
|
||||||
.json(&update_payload)
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(body.contains("User already deleted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_update_user_should_fail_if_payload_invalid() {
|
|
||||||
let state = create_mock_app_state().await;
|
|
||||||
let role_repo = RolesRepository::new(&state);
|
|
||||||
let role = role_repo
|
|
||||||
.query_role_by_name(RolesEnum::User.to_string())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let role_id = role.id.clone();
|
|
||||||
let repo = UsersRepository::new(&state);
|
|
||||||
let app = axum::Router::new()
|
|
||||||
.nest("/v1/users", users_router())
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
let server = TestServer::new(app).unwrap();
|
|
||||||
let payload = UsersCreateRequestDto {
|
|
||||||
fullname: "Invalid Payload".into(),
|
|
||||||
email: "invalid@test.com".into(),
|
|
||||||
role_id: role_id.clone(),
|
|
||||||
password: "Password1!".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
};
|
|
||||||
server.post("/v1/users/create").json(&payload).await;
|
|
||||||
let user = repo.query_user_by_email(payload.email).await.unwrap();
|
|
||||||
let user_id = user.id.id.to_raw();
|
|
||||||
let update_payload = UsersUpdateRequestDto {
|
|
||||||
fullname: "".into(),
|
|
||||||
email: "invalid@test.com".into(),
|
|
||||||
phone_number: "081234567890".into(),
|
|
||||||
is_active: true,
|
|
||||||
role_id: role_id.clone(),
|
|
||||||
gender: Some("Laki-laki".into()),
|
|
||||||
birthdate: Some("2000-01-01".into()),
|
|
||||||
avatar: None,
|
|
||||||
};
|
|
||||||
let res = server
|
|
||||||
.put(&format!("/v1/users/update/{}", user_id))
|
|
||||||
.json(&update_payload)
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status_code(), StatusCode::BAD_REQUEST);
|
|
||||||
let body = res.text();
|
|
||||||
assert!(
|
|
||||||
body.contains("Fullname") || body.contains("at least have 2 character"),
|
|
||||||
"Gagal validasi fullname: {body}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -24,9 +24,7 @@ async fn test_create_and_get_user() {
|
|||||||
create_test_user(&email, "Test User", true, &get_role_id(&app_state).await);
|
create_test_user(&email, "Test User", true, &get_role_id(&app_state).await);
|
||||||
let create_result = repo.query_create_user(user.clone()).await;
|
let create_result = repo.query_create_user(user.clone()).await;
|
||||||
assert!(create_result.is_ok());
|
assert!(create_result.is_ok());
|
||||||
let fetched = repo
|
let fetched = repo.query_user_by_email(email.clone().into()).await;
|
||||||
.query_user_by_email("testuser@example.com".into())
|
|
||||||
.await;
|
|
||||||
assert!(fetched.is_ok());
|
assert!(fetched.is_ok());
|
||||||
assert_eq!(fetched.unwrap().email, email.clone());
|
assert_eq!(fetched.unwrap().email, email.clone());
|
||||||
}
|
}
|
||||||
@@ -91,7 +89,7 @@ async fn test_query_user_list_basic() {
|
|||||||
async fn test_query_delete_user() {
|
async fn test_query_delete_user() {
|
||||||
let app_state = create_mock_app_state().await;
|
let app_state = create_mock_app_state().await;
|
||||||
let repo = UsersRepository::new(&app_state);
|
let repo = UsersRepository::new(&app_state);
|
||||||
let email = "deleteuser@example.com";
|
let email = &generate_unique_email("deleteuser");
|
||||||
let user =
|
let user =
|
||||||
create_test_user(email, "Delete User", true, &get_role_id(&app_state).await);
|
create_test_user(email, "Delete User", true, &get_role_id(&app_state).await);
|
||||||
repo.query_create_user(user.clone()).await.unwrap();
|
repo.query_create_user(user.clone()).await.unwrap();
|
||||||
@@ -128,7 +126,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 already deleted");
|
assert_eq!(second.unwrap_err().to_string(), "User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
Reference in New Issue
Block a user