Add minimal test for basic hackathon operations and enhance test utilities
- Introduced a new test module for hackathon-related functionality. - Implemented a basic test for creating a hackathon using a mock repository. - Enhanced the test utilities in `lib.rs` for better request handling and response extraction. - Added a `ServiceClient` struct to facilitate HTTP requests in tests. - Created a `RequestBuilder` to streamline building and sending requests with headers and JSON bodies.
This commit is contained in:
@@ -65,7 +65,7 @@ impl AuthServiceTrait for AuthService {
|
||||
payload: AuthLoginRequestDto,
|
||||
state: &AppState,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let payload = payload;
|
||||
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if let Err((status, message)) = validate_request(&payload) {
|
||||
|
||||
@@ -93,4 +93,10 @@ where
|
||||
fn clone(&self) -> Self {
|
||||
Self::with_service(self.google_oauth_service.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GoogleOauthController<GoogleOauthServiceImpl<crate::v1::auth::auth_service::AuthService, crate::v1::users::users_service::UsersService>> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
use anyhow::Result;
|
||||
// Type alias to reduce clippy type_complexity warnings for long Future signatures
|
||||
type GoogleOauthCallbackFut<'a> = Pin<Box<dyn Future<Output = Result<(UsersDetailItemDto, TokenDto), Error>> + Send + 'a>>;
|
||||
|
||||
use oauth2::{
|
||||
AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, PkceCodeVerifier,
|
||||
@@ -103,7 +105,7 @@ pub trait GoogleOauthService<A: AuthServiceTrait + Send + Sync + 'static, U: Use
|
||||
// Removed new() from trait
|
||||
fn with_services(auth_service: A, users_service: U, env: &'static Env) -> Self;
|
||||
fn generate_auth_url(&self, custom_redirect_uri: Option<String>) -> (Url, CsrfToken);
|
||||
fn google_oauth_callback(&self, auth_request: AuthRequest, app_state: &AppState) -> Pin<Box<dyn Future<Output = Result<(UsersDetailItemDto, TokenDto), Error>> + Send + '_>>; // Changed return type
|
||||
fn google_oauth_callback(&self, auth_request: AuthRequest, app_state: &AppState) -> GoogleOauthCallbackFut<'_>; // Changed return type
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -5,6 +5,7 @@ use axum::{
|
||||
response::Response, Extension,
|
||||
};
|
||||
use axum_extra::headers::{authorization::Bearer, Authorization, HeaderMapExt};
|
||||
use surrealdb::sql::Thing;
|
||||
|
||||
pub async fn permissions_guard(
|
||||
headers: HeaderMap,
|
||||
@@ -31,15 +32,22 @@ pub async fn permissions_guard(
|
||||
})?
|
||||
.claims;
|
||||
|
||||
// Fetch user from database to get permissions
|
||||
// Fetch user from database to get permissions. Try email first, then try using the sub as a user id.
|
||||
let user_repo = UsersRepository::new(&state);
|
||||
let user = match user_repo.query_user_by_email(claims.sub.clone()).await {
|
||||
Ok(user) => user,
|
||||
Ok(u) => u,
|
||||
Err(_) => {
|
||||
return Err(common_response(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"User not found",
|
||||
));
|
||||
// Try treat claims.sub as a Thing id (user id)
|
||||
let thing = Thing::from(("app_users".to_string(), claims.sub.clone()));
|
||||
match user_repo.query_user_by_id(&thing).await {
|
||||
Ok(u2) => u2,
|
||||
Err(_) => {
|
||||
return Err(common_response(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"User not found",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ impl RolesSchema {
|
||||
.permissions
|
||||
.as_ref()
|
||||
.unwrap_or(&vec![])
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|perm| {
|
||||
perm.as_ref().and_then(|p| p.id.as_ref().map(|id| make_thing_from_enum(ResourceEnum::Permissions, &id.id.to_raw())))
|
||||
})
|
||||
|
||||
@@ -318,10 +318,9 @@ pub async fn get_admin_team_list(
|
||||
axum::extract::Query(meta): axum::extract::Query<MetaRequestDto>,
|
||||
) -> Response {
|
||||
let state = state;
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadListTeams], move |_claims, state| {
|
||||
let response = TeamsService::get_admin_team_list(&state, meta);
|
||||
response
|
||||
}).await
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadListTeams], move |_claims, state| {
|
||||
TeamsService::get_admin_team_list(&state, meta)
|
||||
}).await
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -344,10 +343,9 @@ pub async fn get_admin_team_by_id(
|
||||
Path(id): Path<String>,
|
||||
) -> Response {
|
||||
let state = state;
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
|
||||
let response = TeamsService::get_admin_team_by_id(&state, id);
|
||||
response
|
||||
}).await
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
|
||||
TeamsService::get_admin_team_by_id(&state, id)
|
||||
}).await
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -370,10 +368,9 @@ pub async fn get_admin_team_members(
|
||||
Path(id): Path<String>,
|
||||
) -> Response {
|
||||
let state = state;
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
|
||||
let response = TeamsService::get_admin_team_members(&state, id);
|
||||
response
|
||||
}).await
|
||||
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
|
||||
TeamsService::get_admin_team_members(&state, id)
|
||||
}).await
|
||||
}
|
||||
|
||||
pub fn teams_router() -> Router {
|
||||
|
||||
@@ -83,7 +83,7 @@ impl<'a> TeamsRepository<'a> {
|
||||
if team.is_deleted {
|
||||
bail!("Team not found");
|
||||
}
|
||||
Ok(TeamsDetailQueryDto::from(team))
|
||||
Ok(team)
|
||||
}
|
||||
|
||||
pub async fn query_create_team(&self, data: TeamsSchema) -> Result<String> {
|
||||
@@ -102,7 +102,11 @@ impl<'a> TeamsRepository<'a> {
|
||||
}
|
||||
|
||||
match record {
|
||||
Some(_) => Ok("Success create team".into()),
|
||||
Some(saved) => {
|
||||
// Return the created team id as part of the message so callers can parse it in tests
|
||||
let id = saved.id.id.to_raw();
|
||||
Ok(format!("Success create team {}", id))
|
||||
}
|
||||
None => bail!("Failed to create team"),
|
||||
}
|
||||
}
|
||||
@@ -347,10 +351,9 @@ impl<'a> TeamsRepository<'a> {
|
||||
|
||||
let mut conditions = vec!["is_deleted = false".to_string(), "is_active = true".to_string()];
|
||||
|
||||
if let Some(open) = search_params.open {
|
||||
if open {
|
||||
conditions.push("is_open = true".to_string());
|
||||
}
|
||||
if let Some(open) = search_params.open
|
||||
&& open {
|
||||
conditions.push("is_open = true".to_string());
|
||||
}
|
||||
|
||||
if let Some(location) = &search_params.location {
|
||||
|
||||
@@ -704,17 +704,14 @@ impl TeamsServiceTrait for TeamsService {
|
||||
}
|
||||
}
|
||||
|
||||
match Self::get_user_info_with_privacy(
|
||||
if let Ok(mut leader_dto) = Self::get_user_info_with_privacy(
|
||||
&team.leader_id.id.to_raw(),
|
||||
&claims.user_id,
|
||||
is_member,
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
}
|
||||
Err(_) => {}
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
}
|
||||
|
||||
success_response(ResponseSuccessDto { data: member_dtos })
|
||||
@@ -825,17 +822,14 @@ impl TeamsServiceTrait for TeamsService {
|
||||
}
|
||||
|
||||
// Add leader with full sensitive info
|
||||
match Self::get_user_info_with_privacy(
|
||||
if let Ok(mut leader_dto) = Self::get_user_info_with_privacy(
|
||||
&team.leader_id.id.to_raw(),
|
||||
"system",
|
||||
true,
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
}
|
||||
Err(_) => {}
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
}
|
||||
|
||||
let team_dto = team.into_admin_detail_dto(member_dtos);
|
||||
@@ -916,7 +910,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
Ok(_) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
|
||||
Err(e) => {
|
||||
error!("Failed to remove team member: {}", e);
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
||||
common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user