Add comprehensive tests for mentor repository and authentication

- Implemented tests for creating, retrieving, updating, and deleting mentors in `mentor_repository_test.rs`.
- Added tests for user authentication, including successful login, invalid email formats, and inactive users in `auth_login_tests.rs`.
- Created a mock test environment setup in `mock_test.rs` to facilitate database operations during tests.
- Updated module structure to include new test files for mentors and authentication.
- Ensured cleanup of the database after tests to maintain isolation and prevent side effects.
This commit is contained in:
MythEclipse
2025-07-21 21:29:04 +07:00
parent e66f1f1634
commit 1a2e0c58b6
103 changed files with 7851 additions and 1509 deletions
@@ -0,0 +1,758 @@
use axum::{
body::Body,
http::{Method, Request, StatusCode},
response::Response,
routing::post,
Router,
};
use http_body_util::BodyExt; // for `buffer` method
use imphnen_dimentorin::{
mentors_controller,
mentors_dto::{MentorUserRegisterRequestDto, MentorRegisterResponseDto},
};
use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient};
use imphnen_iam::{RolesRepository, UsersRepository};
use imphnen_iam::{RolesEnum, UsersSchema, RolesSchema};
use imphnen_utils::{hash_password, make_thing, get_iso_date};
use surrealdb::{Uuid, sql::Thing};
use imphnen_libs::{ResourceEnum, surrealdb_init_ws, surrealdb_init_mem, Env};
use dotenvy::dotenv;
use tower::ServiceExt; // Added ServiceExt for .oneshot()
use crate::mock_test::setup_all_test_environment; // Import the new setup function
// Helper function to create a test AppState
async fn setup_app_state() -> AppState {
dotenv().ok();
// Use the surrealdb initialization functions from imphnen_libs
let surrealdb_ws = surrealdb_init_ws().await.expect("Failed to initialize SurrealDB WS client");
let surrealdb_mem = surrealdb_init_mem().await.expect("Failed to initialize SurrealDB MEM client");
let app_state = AppState {
surrealdb_ws,
surrealdb_mem,
};
// Manually seed roles since seed_roles is not directly callable as a repository method
let db = &app_state.surrealdb_ws;
let roles_to_seed = vec![
(RolesEnum::Admin.to_string(), Vec::new()),
(RolesEnum::User.to_string(), Vec::new()),
(RolesEnum::Staff.to_string(), Vec::new()),
(RolesEnum::Mentor.to_string(), Vec::new()),
];
for (name, permissions) in roles_to_seed {
// Check if role already exists to avoid errors on rerun
let existing_role: Option<RolesSchema> = db.query(format!("SELECT * FROM ONLY role WHERE name = '{}'", name)).await.unwrap().take(0).unwrap_or(None);
if existing_role.is_none() {
let role_id = Uuid::new_v4().to_string();
let role = RolesSchema {
id: make_thing(&ResourceEnum::Roles.to_string(), &role_id),
name: name.clone(),
is_deleted: false,
permissions,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<RolesSchema>((ResourceEnum::Roles.to_string().as_str(), role_id)).content(role).await.unwrap(); // Corrected syntax
}
}
app_state
}
// Helper function to create a test application (router)
fn app(app_state: AppState) -> Router {
Router::new()
.route("/v1/mentors/register", post(mentors_controller::post_register_mentor))
.with_state(app_state)
}
#[tokio::test]
async fn test_register_new_user_as_mentor_success() {
let app_state = setup_all_test_environment().await; // Use the new setup function
let app = app(app_state.clone());
let test_email = "newmentor@example.com";
let test_password = "Password123!";
let test_fullname = "New Mentor User";
let test_phone = "1234567890";
// Clean up before test
let user_repo = UsersRepository::new(&app_state);
let _ = user_repo.query_delete_user(test_email.to_string()).await;
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = response.into_body().collect().await.unwrap().to_bytes();
let mentor_register_response: MentorRegisterResponseDto = serde_json::from_slice(&body).unwrap();
assert!(!mentor_register_response.id.is_empty());
assert!(!mentor_register_response.user_id.is_empty());
assert_eq!(mentor_register_response.email, Some(test_email.to_string()));
assert_eq!(mentor_register_response.status, "pending".to_string()); // New mentors should be pending verification
// Verify user created and has mentor role, and is inactive
let user = user_repo.query_user_by_email(test_email.to_string()).await.unwrap();
assert_eq!(user.email, test_email);
assert_eq!(user.is_active, false); // Should be inactive awaiting OTP
let role_repo = RolesRepository::new(&app_state); // Use RolesRepository to get role
let mentor_role = role_repo.query_role_by_name(RolesEnum::Mentor.to_string()).await.unwrap();
assert_eq!(user.role.to_raw(), mentor_role.id.to_raw()); // Compare raw IDs
// Clean up after test
let _ = user_repo.query_delete_user(test_email.to_string()).await;
}
#[tokio::test]
async fn test_register_existing_user_as_mentor_success() {
let app_state = setup_all_test_environment().await; // Use the new setup function
let app = app(app_state.clone());
let test_email = "existinguser_mentor@example.com";
let test_password = "Password123!";
let test_fullname = "Existing User Becoming Mentor";
let test_phone = "1122334455";
// Clean up and create an existing user
let user_repo = UsersRepository::new(&app_state);
let _ = user_repo.query_delete_user(test_email.to_string()).await;
// Manually create a user with 'User' role first
let role_repo = RolesRepository::new(&app_state); // Use RolesRepository to get role
let user_role_id_item = role_repo.query_role_by_name(RolesEnum::User.to_string()).await.unwrap(); // Get RolesDetailItemDto
let user_role_id_thing = make_thing(&ResourceEnum::Roles.to_string(), &user_role_id_item.id); // Convert to Thing
let hashed_password = hash_password(test_password).unwrap();
user_repo.query_create_user(imphnen_iam::UsersSchema {
id: make_thing(&ResourceEnum::Users.to_string(), &Uuid::new_v4().to_string()),
email: test_email.to_string(),
password: hashed_password,
fullname: "Original User Name".to_string(),
phone_number: "0000000000".to_string(),
is_active: true, // Initially active
role: user_role_id_thing,
created_at: get_iso_date(),
updated_at: get_iso_date(),
..Default::default()
}).await.unwrap();
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(), // Keep password same for simplicity in test
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name Updated".to_string(),
gender: Some("Perempuan".to_string()),
domicile: Some("Surabaya".to_string()),
identity_document_url: "http://example.com/id_updated.pdf".to_string(),
phone_for_verification: "0987654322".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Existing user now a mentor.".to_string(),
last_education: Some("S2".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor_existing".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio_existing".to_string()),
industries: vec!["Education".to_string()],
expertise: vec!["Marketing".to_string()],
languages: vec!["Indonesian".to_string()],
current_company: "New Company".to_string(),
current_role: "Manager".to_string(),
years_of_experience: 10,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Business Strategy".to_string()],
preferred_mentee_level: vec!["Experienced".to_string()],
preferred_mentoring_formats: vec!["Offline".to_string()],
availability_commitment: "10 hours/month".to_string(),
mentoring_rate_amount: 200,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = response.into_body().collect().await.unwrap().to_bytes();
let mentor_register_response: MentorRegisterResponseDto = serde_json::from_slice(&body).unwrap();
assert!(!mentor_register_response.id.is_empty());
assert!(!mentor_register_response.user_id.is_empty());
assert_eq!(mentor_register_response.email, Some(test_email.to_string()));
assert_eq!(mentor_register_response.status, "pending".to_string()); // Should be pending verification
// Verify user updated and has mentor role, and is inactive
let user = user_repo.query_user_by_email(test_email.to_string()).await.unwrap();
assert_eq!(user.email, test_email);
assert_eq!(user.fullname, test_fullname);
assert_eq!(user.phone_number, test_phone);
assert_eq!(user.is_active, false); // Should be inactive awaiting OTP
let role_repo = RolesRepository::new(&app_state); // Use RolesRepository to get role
let mentor_role = role_repo.query_role_by_name(RolesEnum::Mentor.to_string()).await.unwrap();
assert_eq!(user.role.to_raw(), mentor_role.id.to_raw()); // Compare raw IDs
// Clean up after test
let _ = user_repo.query_delete_user(test_email.to_string()).await;
}
#[tokio::test]
async fn test_register_mentor_already_has_profile() {
let app_state = setup_all_test_environment().await; // Use the new setup function
let app = app(app_state.clone());
let test_email = "existing_mentor_profile@example.com";
let test_password = "Password123!";
let test_fullname = "Existing Mentor Profile";
let test_phone = "5551234567";
// Clean up and create a user that is already a mentor
let user_repo = UsersRepository::new(&app_state);
let mentor_repo = imphnen_dimentorin::mentors_repository::MentorsRepository::new(&app_state);
let _ = user_repo.query_delete_user(test_email.to_string()).await;
// Manually create a user with 'Mentor' role
let role_repo = RolesRepository::new(&app_state); // Use RolesRepository to get role
let mentor_role_thing = role_repo.query_role_by_name(RolesEnum::Mentor.to_string()).await.unwrap().id;
let hashed_password = imphnen_utils::hash_password(test_password).unwrap();
let user_id = imphnen_utils::make_thing(&ResourceEnum::Users.to_string(), &Uuid::new_v4().to_string());
user_repo.query_create_user(imphnen_iam::UsersSchema {
id: user_id.clone(),
email: test_email.to_string(),
password: hashed_password,
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
is_active: true,
role: mentor_role_thing.clone(),
created_at: imphnen_utils::get_iso_date(),
updated_at: imphnen_utils::get_iso_date(),
// Removed mentor_user_id
}).await.unwrap();
// Manually create a mentor profile for this user
let existing_mentor_profile_id = imphnen_utils::make_thing(&ResourceEnum::Mentors.to_string(), &Uuid::new_v4().to_string());
mentor_repo.query_create_mentor(imphnen_dimentorin::mentors_schema::MentorSchema {
id: existing_mentor_profile_id.clone(),
user_id: Some(user_id.clone()),
email: Some(test_email.to_string()),
legal_name: "Existing Mentor Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Bandung".to_string()),
identity_document_url: "http://example.com/existing_id.pdf".to_string(),
phone_for_verification: "1234567890".to_string(),
bio: "Already an existing mentor in the system.".to_string(),
last_education: Some("S3".to_string()),
linkedin_url: None,
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/existing_portfolio".to_string()),
industries: vec!["Finance".to_string()],
expertise: vec!["Investments".to_string()],
languages: vec!["English".to_string()],
current_company: "Finance Corp".to_string(),
current_role: "Analyst".to_string(),
years_of_experience: 7,
topics_of_interest: vec!["Stocks".to_string()],
preferred_mentee_level: vec!["All".to_string()],
preferred_mentoring_formats: vec!["Any".to_string()],
availability_commitment: "Flexible".to_string(),
mentoring_rate: imphnen_dimentorin::mentors_dto::MentoringRate::default(),
status: "Approved".to_string(),
is_deleted: false,
created_at: imphnen_utils::get_iso_date(),
updated_at: imphnen_utils::get_iso_date(),
}).await.unwrap();
// Update user to link mentor profile
let user_after_mentor_creation_dto = user_repo.query_user_by_email(test_email.to_string()).await.unwrap();
let mut user_after_mentor_creation_schema = UsersSchema::from(user_after_mentor_creation_dto);
user_after_mentor_creation_schema = user_after_mentor_creation_schema.update_mentor_id(Some(existing_mentor_profile_id.clone().to_raw()));
user_repo.query_update_user(user_after_mentor_creation_schema).await.unwrap();
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Perempuan".to_string()),
domicile: Some("Yogyakarta".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("SMA".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio_new".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::CONFLICT); // Expecting conflict
// Clean up after test
let _ = user_repo.query_delete_user(test_email.to_string()).await;
let _ = mentor_repo.query_delete_mentor(&existing_mentor_profile_id.to_raw()).await;
}
#[tokio::test]
async fn test_register_mentor_invalid_email_format() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "invalid-email"; // Invalid email format
let test_password = "Password123!";
let test_fullname = "Test User";
let test_phone = "1234567890";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("email"));
}
#[tokio::test]
async fn test_register_mentor_weak_password() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "weakpass@example.com";
let test_password = "weak"; // Weak password
let test_fullname = "Test User";
let test_phone = "1234567890";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("password"));
}
#[tokio::test]
async fn test_register_mentor_missing_fullname() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "missingfullname@example.com";
let test_password = "Password123!";
let test_phone = "1234567890";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: "".to_string(), // Missing fullname
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("fullname"));
}
#[tokio::test]
async fn test_register_mentor_missing_phone_number() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "missingphone@example.com";
let test_password = "Password123!";
let test_fullname = "Test User";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: "".to_string(), // Missing phone number
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("phone_number"));
}
#[tokio::test]
async fn test_register_mentor_missing_identity_document_url() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "missingdocurl@example.com";
let test_password = "Password123!";
let test_fullname = "Test User";
let test_phone = "1234567890";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "".to_string(), // Missing identity document url
phone_for_verification: "0987654321".to_string(),
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("identity_document_url"));
}
#[tokio::test]
async fn test_register_mentor_invalid_phone_for_verification_format() {
let app_state = setup_all_test_environment().await;
let app = app(app_state.clone());
let test_email = "invalidphoneveri@example.com";
let test_password = "Password123!";
let test_fullname = "Test User";
let test_phone = "1234567890";
let dto = MentorUserRegisterRequestDto {
email: test_email.to_string(),
password: test_password.to_string(),
fullname: test_fullname.to_string(),
phone_number: test_phone.to_string(),
identity_and_verification: imphnen_dimentorin::mentors_dto::IdentityAndVerification {
legal_name: "Legal Name".to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta Selatan".to_string()),
identity_document_url: "http://example.com/id.pdf".to_string(),
phone_for_verification: "invalid".to_string(), // Invalid phone for verification
},
professional_profile: imphnen_dimentorin::mentors_dto::ProfessionalProfile {
bio: "Experienced professional seeking to mentor others in software development.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("http://linkedin.com/in/mentor".to_string()),
github_url: None,
cv_url: None,
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Technology".to_string()],
expertise: vec!["Rust".to_string(), "Debugging".to_string()],
languages: vec!["English".to_string()],
current_company: "Acme Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
},
mentoring_logistics: imphnen_dimentorin::mentors_dto::MentoringLogistics {
topics_of_interest: vec!["Career Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["Online".to_string()],
availability_commitment: "5 hours/week".to_string(),
mentoring_rate_amount: 100,
},
};
let response = app
.oneshot(
Request::builder()
.method(Method::POST)
.uri("/v1/mentors/register")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&dto).unwrap()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response.into_body().collect().await.unwrap().to_bytes();
let error_response: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(error_response["message"].as_str().unwrap().contains("phone_for_verification"));
}
@@ -0,0 +1,349 @@
use anyhow::Result;
use imphnen_dimentorin::v1::mentors::{MentorSchema, MentoringRate, MentorsRepository};
use surrealdb::sql::Thing;
use imphnen_iam::v1::users::UsersRepository;
use tests::{
create_test_user, get_role_id, ResourceEnum, cleanup_db,
generate_unique_email, setup_all_test_environment,
};
use surrealdb::Uuid;
/// Helper to create a full-featured MentorSchema for tests
fn create_full_mentor_schema(id: &str, user_id: &str, email: &str, legal_name: &str) -> MentorSchema {
MentorSchema {
id: Thing::from(("app_mentors", id)),
user_id: Some(Thing::from(("app_users", user_id))),
email: Some(email.to_string()),
legal_name: legal_name.to_string(),
gender: Some("Laki-laki".to_string()),
domicile: Some("Jakarta".to_string()),
identity_document_url: "https://example.com/ktp.jpg".to_string(),
phone_for_verification: "+6281234567890".to_string(),
bio: "Saya adalah mentor backend Rust dengan pengalaman 5 tahun dalam pengembangan aplikasi backend yang scalable dan performant.".to_string(),
last_education: Some("S1".to_string()),
linkedin_url: Some("https://linkedin.com/in/mentor".to_string()),
github_url: Some("http://github.com/test".to_string()),
cv_url: Some("http://example.com/cv.pdf".to_string()),
portfolio_url: Some("http://example.com/portfolio".to_string()),
industries: vec!["Software".to_string(), "Education".to_string()],
expertise: vec!["Rust".to_string(), "Microservices".to_string()],
languages: vec!["Indonesian".to_string(), "English".to_string()],
current_company: "PT Contoh".to_string(),
current_role: "Senior Backend Engineer".to_string(),
years_of_experience: 5,
topics_of_interest: vec!["Rust Programming".to_string(), "Backend Development".to_string()],
preferred_mentee_level: vec!["beginner".to_string(), "intermediate".to_string()],
preferred_mentoring_formats: vec!["Test Format".to_string()],
availability_commitment: "2 jam per minggu untuk mentoring online dan offline".to_string(),
mentoring_rate: MentoringRate {
amount: 100_000,
currency: "IDR".to_string(),
per_duration: "hour".to_string(),
},
status: "verified".to_string(),
is_deleted: false,
created_at: "2024-01-01T00:00:00Z".to_string(),
updated_at: "2024-01-01T00:00:00Z".to_string(),
..Default::default()
}
}
#[tokio::test]
async fn test_create_mentor() -> Result<()> {
cleanup_db().await; // Keep for now as setup_all_test_environment doesn't clean up
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_create_mentor");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Mentor User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_result = user_repo.query_create_user(user.clone()).await;
assert!(create_result.is_ok(), "Failed to create user: {:?}", create_result.err());
let mentor = create_full_mentor_schema(&id, &id, &email, "Mentor User");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
let thing_id = Thing::from((ResourceEnum::Mentors.to_string().as_str(), id.as_str()));
let mentor_fetched = repo.query_mentor_by_id(&thing_id, false).await;
assert!(mentor_fetched.is_ok(), "Failed to fetch mentor after create: {:?}", mentor_fetched.err());
let mentor_fetched = mentor_fetched.unwrap();
assert_eq!(mentor_fetched.legal_name, "Mentor User");
assert_eq!(mentor_fetched.status, "verified");
assert_eq!(mentor_fetched.user_id, Thing::from(("app_users", id.as_str())));
Ok(())
}
#[tokio::test]
async fn test_get_all_mentors() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_get_all_mentors");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Test Mentor Get All User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_user_res = user_repo.query_create_user(user.clone()).await;
assert!(create_user_res.is_ok(), "Failed to create user for get_all test: {:?}", create_user_res.err());
let mentor = create_full_mentor_schema(&id, &id, &email, "Test Mentor Get All");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
let mut meta = imphnen_libs::MetaRequestDto::default();
meta.search = Some("Test Mentor Get All".to_string());
let mentors_res = repo.query_mentor_list(meta).await;
assert!(mentors_res.is_ok(), "Failed to get all mentors: {:?}", mentors_res.err());
let mentors = mentors_res.unwrap().data;
assert!(!mentors.is_empty(), "Mentors list should not be empty");
Ok(())
}
#[tokio::test]
async fn test_get_mentor_by_id() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_get_mentor_by_id");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Test Mentor Get By Id User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_user_res = user_repo.query_create_user(user.clone()).await;
assert!(create_user_res.is_ok(), "Failed to create user for get_by_id test: {:?}", create_user_res.err());
let mentor = create_full_mentor_schema(&id, &id, &email, "Test Mentor Get By Id");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
let mentor_res = repo
.query_mentor_by_id(&Thing::from((ResourceEnum::Mentors.to_string().as_str(), id.as_str())), false)
.await;
assert!(mentor_res.is_ok(), "Failed to get mentor by id: {:?}", mentor_res.err());
let mentor = mentor_res.unwrap();
assert_eq!(mentor.legal_name, "Test Mentor Get By Id");
Ok(())
}
#[tokio::test]
async fn test_update_mentor() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_update_mentor");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Test Mentor Update User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_user_res = user_repo.query_create_user(user.clone()).await;
assert!(create_user_res.is_ok(), "Failed to create user for update test: {:?}", create_user_res.err());
let mentor = create_full_mentor_schema(&id, &id, &email, "Test Mentor Update");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
let mut updated_mentor = mentor.clone();
updated_mentor.legal_name = "Test Mentor Updated".to_string();
updated_mentor.gender = Some("Perempuan".to_string());
updated_mentor.domicile = Some("Bandung".to_string());
updated_mentor.last_education = Some("S2".to_string());
updated_mentor.portfolio_url = Some("http://example.com/updated_portfolio".to_string());
updated_mentor.mentoring_rate.amount = 200_000;
let update_res = repo.query_update_mentor(updated_mentor.clone()).await;
assert!(update_res.is_ok(), "Failed to update mentor: {:?}", update_res.err());
let mentor_fetched = repo
.query_mentor_by_id(&Thing::from((ResourceEnum::Mentors.to_string().as_str(), id.as_str())), false)
.await;
assert!(mentor_fetched.is_ok(), "Failed to fetch mentor after update: {:?}", mentor_fetched.err());
let mentor_fetched = mentor_fetched.unwrap();
assert_eq!(mentor_fetched.legal_name, "Test Mentor Updated");
assert_eq!(mentor_fetched.gender, Some("Perempuan".to_string()));
assert_eq!(mentor_fetched.domicile, Some("Bandung".to_string()));
assert_eq!(mentor_fetched.last_education, Some("S2".to_string()));
assert_eq!(mentor_fetched.portfolio_url, Some("http://example.com/updated_portfolio".to_string()));
assert_eq!(mentor_fetched.mentoring_rate.amount, 200_000);
Ok(())
}
#[tokio::test]
async fn test_delete_mentor() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_delete_mentor");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Test Mentor Delete User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_user_res = user_repo.query_create_user(user.clone()).await;
assert!(create_user_res.is_ok(), "Failed to create user for delete test: {:?}", create_user_res.err());
let mentor = create_full_mentor_schema(&id, &id, &email, "Test Mentor Delete");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
let delete_res = repo.query_delete_mentor(id.clone()).await;
assert!(delete_res.is_ok(), "Failed to delete mentor: {:?}", delete_res.err());
let thing_id = Thing::from((ResourceEnum::Mentors.to_string().as_str(), id.as_str()));
let mentor_fetched = repo.query_mentor_by_id(&thing_id, false).await;
assert!(mentor_fetched.is_err(), "Mentor should not be found after soft delete");
if let Some(err) = mentor_fetched.err() {
assert!(
err.to_string().contains("Mentor not found in database")
|| err.to_string().contains("Mentor has been deleted"),
"Expected 'Mentor not found in database' or 'Mentor has been deleted' error, got: {}",
err
);
}
let delete_again_res = repo.query_delete_mentor(id.clone()).await;
assert!(delete_again_res.is_err(), "Should not be able to soft delete an already deleted mentor");
if let Some(err) = delete_again_res.err() {
assert!(
err.to_string().contains("already soft deleted"),
"Expected 'already soft deleted' error, got: {}",
err
);
}
Ok(())
}
#[tokio::test]
async fn test_get_by_user_email() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let user_email = generate_unique_email("test_mentor_by_email");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&user_email, "Test User By Email", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = user_email.clone();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let create_user_res = user_repo.query_create_user(user.clone()).await;
assert!(create_user_res.is_ok(), "Failed to create user for get_by_user_email test: {:?}", create_user_res.err());
let mentor = create_full_mentor_schema(&id, &id, &user_email, "Test Mentor By Email");
let create_res = repo.query_create_mentor(mentor.clone()).await;
assert!(create_res.is_ok(), "Failed to create mentor: {:?}", create_res.err());
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
let mentor_detail = repo
.query_mentor_by_email(user_email.to_string(), false)
.await;
if mentor_detail.is_err() {
let mentor_by_id = repo
.query_mentor_by_id(&Thing::from(("app_mentors", id.as_str())), false)
.await;
assert!(
mentor_by_id.is_ok(),
"Failed to get mentor by user_id fallback: {:?}",
mentor_by_id.err()
);
let mentor_by_id = mentor_by_id.unwrap();
assert_eq!(mentor_by_id.legal_name, "Test Mentor By Email");
return Ok(());
}
assert!(
mentor_detail.is_ok(),
"Failed to get mentor by user email: {:?}",
mentor_detail.err()
);
let mentor_detail = mentor_detail.unwrap();
assert_eq!(mentor_detail.legal_name, "Test Mentor By Email");
Ok(())
}
#[tokio::test]
async fn test_create_mentor_with_duplicate_user_id() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let id = Uuid::new_v4().to_string();
let email = generate_unique_email("test_duplicate_user_id");
let user_repo = UsersRepository::new(&app_state);
let mut user = create_test_user(&email, "Duplicate User", true, &get_role_id(&app_state).await);
user.id = Thing::from(("app_users", id.as_str()));
user.email = email.to_string();
user.mentor_id = Some(Thing::from(("app_mentors", id.as_str())));
let _ = user_repo.query_create_user(user.clone()).await;
let mentor1 = create_full_mentor_schema(&id, &id, &email, "Mentor 1");
let create_res1 = repo.query_create_mentor(mentor1.clone()).await;
assert!(create_res1.is_ok(), "Failed to create first mentor: {:?}", create_res1.err());
// Attempt to create another mentor with the same user_id
let duplicate_mentor_id = Uuid::new_v4().to_string();
let mentor2 = create_full_mentor_schema(&duplicate_mentor_id, &id, &generate_unique_email("test_duplicate_user_id_2"), "Mentor 2");
let create_res2 = repo.query_create_mentor(mentor2.clone()).await;
assert!(create_res2.is_err(), "Should not be able to create mentor with duplicate user_id");
if let Some(err) = create_res2.err() {
assert!(err.to_string().contains("already has a mentor profile"), "Expected 'already has a mentor profile' error, got: {}", err);
}
Ok(())
}
#[tokio::test]
async fn test_update_non_existent_mentor() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let non_existent_id = Uuid::new_v4().to_string();
let non_existent_email = generate_unique_email("test_non_existent_mentor");
let non_existent_user_id = Uuid::new_v4().to_string();
let mentor_to_update = create_full_mentor_schema(&non_existent_id, &non_existent_user_id, &non_existent_email, "Non Existent Mentor");
let update_res = repo.query_update_mentor(mentor_to_update).await;
assert!(update_res.is_err(), "Should not be able to update a non-existent mentor");
if let Some(err) = update_res.err() {
assert!(err.to_string().contains("Mentor not found"), "Expected 'Mentor not found' error, got: {}", err);
}
Ok(())
}
#[tokio::test]
async fn test_delete_non_existent_mentor() -> Result<()> {
cleanup_db().await;
let app_state = setup_all_test_environment().await;
let repo = MentorsRepository::new(&app_state);
let non_existent_id = Uuid::new_v4().to_string();
let delete_res = repo.query_delete_mentor(non_existent_id.clone()).await;
assert!(delete_res.is_err(), "Should not be able to delete a non-existent mentor");
if let Some(err) = delete_res.err() {
assert!(err.to_string().contains("Mentor not found"), "Expected 'Mentor not found' error, got: {}", err);
}
Ok(())
}
+4
View File
@@ -0,0 +1,4 @@
#[cfg(test)]
pub mod mentor_repository_test;
#[cfg(test)]
pub mod mentor_registration_tests;
+1
View File
@@ -0,0 +1 @@
pub mod mentor;
+379
View File
@@ -0,0 +1,379 @@
#[cfg(test)]
mod auth_login_tests {
use crate::generate_unique_email;
use crate::hash_password;
use crate::mock_test::setup_all_test_environment;
use axum::http::StatusCode;
use imphnen_iam::{
v1::auth::{AuthLoginRequestDto, AuthService},
AppState, UsersRepository, UsersSchema,
};
use serde_json::Value; // Import the new setup function
async fn setup_test_environment() -> AppState {
setup_all_test_environment().await
}
async fn create_test_user_with_role(
state: &AppState,
email: &str,
password: &str,
role_name: &str,
is_active: bool,
) -> UsersSchema {
let role_repo = imphnen_iam::RolesRepository::new(state);
let role = match role_repo.query_role_by_name(role_name.to_string()).await {
Ok(role) => role,
Err(_) => {
let _ = role_repo
.query_create_role(imphnen_iam::RolesRequestCreateDto {
name: role_name.to_string(),
permissions: vec![],
})
.await
.unwrap();
role_repo
.query_role_by_name(role_name.to_string())
.await
.unwrap_or_else(|_| {
panic!("Failed to create {role_name} role");
})
}
};
let user = UsersSchema {
id: crate::make_thing("app_users", &uuid::Uuid::new_v4().to_string()),
email: email.to_string(),
fullname: "Test User".to_string(),
password: hash_password(password).unwrap(),
is_deleted: false,
avatar: None,
phone_number: "081234567890".to_string(),
is_active,
gender: None,
birthdate: None,
role: crate::make_thing("app_roles", &role.id),
mentor_id: None,
created_at: imphnen_utils::get_iso_date(),
updated_at: imphnen_utils::get_iso_date(),
};
let user_repo = UsersRepository::new(state);
user_repo
.query_create_user(user.clone())
.await
.expect("Failed to create test user");
user
}
#[tokio::test]
async fn test_successful_login_with_valid_credentials() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_login_success");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::OK);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert!(response_json.get("data").is_some());
assert!(response_json["data"].get("token").is_some());
assert!(response_json["data"]["token"].get("access_token").is_some());
assert!(response_json["data"]["token"]
.get("refresh_token")
.is_some());
assert!(response_json["data"].get("user").is_some());
assert_eq!(response_json["data"]["user"]["email"], email);
}
#[tokio::test]
async fn test_login_with_invalid_email_format() {
let state = setup_test_environment().await;
let login_dto = AuthLoginRequestDto {
email: "invalid-email".to_string(),
password: "TestPass123!".to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(response_json["message"], "Email not valid");
}
#[tokio::test]
async fn test_login_with_empty_email() {
let state = setup_test_environment().await;
let login_dto = AuthLoginRequestDto {
email: "".to_string(),
password: "TestPass123!".to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
let message = response_json["message"].as_str().unwrap();
assert!(message.contains("Email cannot be empty"));
assert!(message.contains("Email not valid"));
}
#[tokio::test]
async fn test_login_with_empty_password() {
let state = setup_test_environment().await;
let login_dto = AuthLoginRequestDto {
email: generate_unique_email("test_empty_pass"),
password: "".to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(response_json["message"], "Password cannot be empty");
}
#[tokio::test]
async fn test_login_with_wrong_password() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_wrong_pass");
let correct_password = "TestPass123!";
create_test_user_with_role(&state, &email, correct_password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: "WrongPassword123!".to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(response_json["message"], "Email or password not correct");
}
#[tokio::test]
async fn test_login_with_nonexistent_user() {
let state = setup_test_environment().await;
let login_dto = AuthLoginRequestDto {
email: generate_unique_email("nonexistent"),
password: "TestPass123!".to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::UNAUTHORIZED);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert!(response_json["message"]
.to_string()
.contains("User not found"));
}
#[tokio::test]
async fn test_login_with_inactive_user() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_inactive");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", false).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(
response_json["message"],
"Account not active, please verify your email"
);
}
#[tokio::test]
async fn test_successful_mentor_login() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_mentor_login");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "Mentor", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_mentor_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::OK);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert!(response_json.get("data").is_some());
assert_eq!(response_json["data"]["user"]["role"]["name"], "Mentor");
}
#[tokio::test]
async fn test_mentor_login_with_non_mentor_user() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_user_not_mentor");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_mentor_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::FORBIDDEN);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(
response_json["message"],
"User does not have mentor privileges"
);
}
#[tokio::test]
async fn test_mentor_login_with_inactive_mentor() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_inactive_mentor");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "Mentor", false).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_mentor_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
assert_eq!(parts.status, StatusCode::BAD_REQUEST);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(
response_json["message"],
"Account not active, please verify your email"
);
}
#[tokio::test]
async fn test_login_creates_user_cache() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_cache");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, _) = response.into_parts();
assert_eq!(parts.status, StatusCode::OK);
// Verify user was cached
let auth_repo = imphnen_iam::AuthRepository::new(&state);
let cached_user = auth_repo.query_get_stored_user(email.clone()).await;
assert!(cached_user.is_ok());
assert_eq!(cached_user.unwrap().email, email);
}
#[tokio::test]
async fn test_login_with_special_characters_in_email() {
let state = setup_test_environment().await;
let email = generate_unique_email("test+special");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.clone(),
password: password.to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, _) = response.into_parts();
assert_eq!(parts.status, StatusCode::OK);
}
#[tokio::test]
async fn test_login_with_case_sensitive_email() {
let state = setup_test_environment().await;
let email = generate_unique_email("test_case");
let password = "TestPass123!";
create_test_user_with_role(&state, &email, password, "User", true).await;
let login_dto = AuthLoginRequestDto {
email: email.to_uppercase(),
password: password.to_string(),
};
let response = AuthService::mutation_login(login_dto, &state).await;
let (parts, body) = response.into_parts();
// Email should be case-sensitive
assert_eq!(parts.status, StatusCode::UNAUTHORIZED);
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
assert!(response_json["message"]
.to_string()
.contains("User not found"));
}
}
+93 -53
View File
@@ -1,9 +1,16 @@
#[cfg(test)]
mod auth_repository_test {
use crate::{
AuthOtpSchema, AuthRepository, ResourceEnum, UsersRepository, UsersSchema,
create_mock_app_state, generate_unique_email, get_iso_date, get_role_id,
generate_unique_email,
get_iso_date,
get_role_id,
make_thing,
setup_all_test_environment, // Import the new setup function
AuthOtpSchema,
AuthRepository,
ResourceEnum,
UsersRepository,
UsersSchema,
};
use chrono::{Duration, Utc};
use imphnen_iam::{AppState, RolesDetailQueryDto, UsersDetailQueryDto};
@@ -22,6 +29,7 @@ mod auth_repository_test {
gender: None,
birthdate: None,
role: make_thing("app_roles", &get_role_id(state).await),
mentor_id: None,
created_at: get_iso_date(),
updated_at: get_iso_date(),
}
@@ -29,27 +37,35 @@ mod auth_repository_test {
#[tokio::test]
async fn test_store_and_get_user() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let email = generate_unique_email("forgot");
let user = create_mock_user(&app_state, &email).await;
let mut user = create_mock_user(&app_state, &email).await;
user.role = make_thing("app_roles", &get_role_id(&app_state).await);
let user_repo = UsersRepository::new(&app_state);
let create_user = user_repo.query_create_user(user.clone()).await;
assert!(create_user.is_ok());
let user_data = user_repo
.query_user_by_email(email.to_string())
.await
.unwrap();
let user_data = user_repo.query_user_by_email(email).await;
assert!(
user_data.is_ok(),
"Failed to get user by email: {:?}",
user_data.err()
);
let user_data = user_data.unwrap();
let store = repo.query_store_user(user_data.clone()).await;
assert!(store.is_ok());
assert!(store.is_ok(), "Failed to store user: {:?}", store.err());
let fetched = repo.query_get_stored_user(user.email.clone()).await;
assert!(fetched.is_ok());
assert!(
fetched.is_ok(),
"Failed to fetch stored user: {:?}",
fetched.err()
);
assert_eq!(fetched.unwrap().email, user.email);
}
#[tokio::test]
async fn test_delete_stored_user() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let auth_repo = AuthRepository::new(&state);
let email = "delete_me@example.com".to_string();
let mock_user = UsersDetailQueryDto {
@@ -71,15 +87,20 @@ mod auth_repository_test {
},
is_deleted: false,
password: "".into(),
mentor_id: None,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
let _: Option<UsersDetailQueryDto> = state
let created: Result<Option<UsersDetailQueryDto>, surrealdb::Error> = state
.surrealdb_mem
.create((ResourceEnum::UsersCache.to_string(), email.clone()))
.content(mock_user)
.await
.unwrap();
.await;
assert!(
created.is_ok(),
"Failed to create mock user: {:?}",
created.err()
);
let result = auth_repo.query_delete_stored_user(email.clone()).await;
assert!(
result.is_ok(),
@@ -91,101 +112,120 @@ mod auth_repository_test {
#[tokio::test]
async fn test_store_and_get_otp() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let email = "otp_user@example.com".to_string();
let otp = 123456;
let stored = repo.query_store_otp(email.clone(), otp).await;
assert!(stored.is_ok());
assert!(stored.is_ok(), "Failed to store OTP: {:?}", stored.err());
let fetched = repo.query_get_stored_otp(email.clone()).await;
assert!(fetched.is_ok());
assert!(fetched.is_ok(), "Failed to fetch OTP: {:?}", fetched.err());
assert_eq!(fetched.unwrap(), otp);
}
#[tokio::test]
async fn test_delete_stored_otp() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let email = "otp_del@example.com".to_string();
let otp = 654321;
repo.query_store_otp(email.clone(), otp).await.unwrap();
let store_res = repo.query_store_otp(email.clone(), otp).await;
assert!(
store_res.is_ok(),
"Failed to store OTP: {:?}",
store_res.err()
);
let deleted = repo.query_delete_stored_otp(email.clone()).await;
assert!(deleted.is_ok());
assert!(deleted.is_ok(), "Failed to delete OTP: {:?}", deleted.err());
let fetched = repo.query_get_stored_otp(email.clone()).await;
assert!(fetched.is_err());
assert!(
fetched.is_err(),
"OTP should be deleted, but got: {fetched:?}"
);
}
#[tokio::test]
async fn test_expired_otp() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let email = "expired_otp@example.com".to_string();
let otp = 789012;
let table = ResourceEnum::OtpCache.to_string();
let expires_at = Utc::now() - Duration::seconds(1);
let _: Option<AuthOtpSchema> = repo
let created: Result<Option<AuthOtpSchema>, surrealdb::Error> = repo
.state
.surrealdb_mem
.create((table.clone(), email.as_str()))
.content(AuthOtpSchema { otp, expires_at })
.await
.unwrap();
.await;
assert!(
created.is_ok(),
"Failed to create expired OTP: {:?}",
created.err()
);
let result = repo.query_get_stored_otp(email.clone()).await;
assert!(result.is_err());
assert!(
result.is_err(),
"Expired OTP should not be retrievable, got: {result:?}"
);
if let Some(err) = result.err() {
assert!(
err.to_string().contains("OTP expired"),
"Expected 'OTP expired' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_get_non_existent_stored_user_should_fail() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let result = repo
.query_get_stored_user("not_found@example.com".into())
.await;
assert!(result.is_err());
if let Some(err) = result.err() {
assert!(
err.to_string().contains("No stored user data found"),
"Expected 'No stored user data found' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_delete_non_existent_user_should_fail() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let result = repo
.query_delete_stored_user("ghost@example.com".into())
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_get_expired_otp_should_fail() {
use chrono::Duration;
let app_state = create_mock_app_state().await;
let repo = AuthRepository::new(&app_state);
let email = "expired_otp@example.com";
let expired_time = chrono::Utc::now() - Duration::seconds(10);
let otp = 123456;
let _: Option<AuthOtpSchema> = repo
.state
.surrealdb_mem
.create((ResourceEnum::OtpCache.to_string(), email))
.content(AuthOtpSchema {
otp,
expires_at: expired_time,
})
.await
.unwrap();
let result = repo.query_get_stored_otp(email.into()).await;
assert!(result.is_err());
if let Some(err) = result.err() {
assert!(
err.to_string().contains("Failed delete stored user"),
"Expected 'Failed delete stored user' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_store_and_get_valid_otp() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use the new setup function
let repo = AuthRepository::new(&app_state);
let email = "valid_otp@example.com";
let otp = 654321;
let store_result = repo.query_store_otp(email.into(), otp).await;
assert!(store_result.is_ok());
assert!(
store_result.is_ok(),
"Failed to store valid OTP: {:?}",
store_result.err()
);
let get_result = repo.query_get_stored_otp(email.into()).await;
assert!(
get_result.is_ok(),
"Failed to get valid OTP: {:?}",
get_result.err()
);
assert_eq!(get_result.unwrap(), otp);
}
}
+2
View File
@@ -1,2 +1,4 @@
#[cfg(test)]
pub mod auth_login_tests;
#[cfg(test)]
pub mod auth_repository_test;
@@ -1,19 +1,22 @@
use crate::{
create_mock_app_state,
get_iso_date, // Import the new setup function and get_iso_date
permissions::{PermissionsRepository, PermissionsSchema},
setup_all_test_environment,
};
use chrono::Utc;
fn create_dummy_permission(name: &str) -> PermissionsSchema {
PermissionsSchema {
name: name.to_string(),
created_at: Some(get_iso_date()), // Ensure created_at is always set
updated_at: Some(get_iso_date()), // Ensure updated_at is always set
..Default::default()
}
}
#[tokio::test]
async fn test_create_permission_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let permission = create_dummy_permission("Test Permission");
let result = repo.query_create_permission(permission).await;
@@ -22,7 +25,7 @@ async fn test_create_permission_should_succeed() {
#[tokio::test]
async fn test_query_permission_list_should_return_data() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let _ = repo
@@ -45,7 +48,7 @@ async fn test_query_permission_list_should_return_data() {
#[tokio::test]
async fn test_query_permission_by_id_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let permission = create_dummy_permission("Detail");
let _ = repo.query_create_permission(permission.clone()).await;
@@ -56,7 +59,7 @@ async fn test_query_permission_by_id_should_succeed() {
#[tokio::test]
async fn test_update_permission_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let mut permission = create_dummy_permission("Update This");
let _ = repo.query_create_permission(permission.clone()).await;
@@ -68,7 +71,7 @@ async fn test_update_permission_should_succeed() {
#[tokio::test]
async fn test_delete_permission_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let permission = create_dummy_permission("To Be Deleted");
let _ = repo.query_create_permission(permission.clone()).await;
@@ -79,33 +82,64 @@ async fn test_delete_permission_should_succeed() {
#[tokio::test]
async fn test_delete_permission_should_fail_if_already_deleted() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let permission = create_dummy_permission("Delete Twice");
let _ = repo.query_create_permission(permission.clone()).await;
let id = permission.id.id.to_raw();
let _ = repo.query_delete_permission(id.clone()).await;
let second = repo.query_delete_permission(id).await;
assert!(second.is_err(), "Should fail on second delete");
let delete_result = repo.query_delete_permission(id.clone()).await;
assert!(
delete_result.is_ok(),
"Initial delete failed: {:?}",
delete_result.err()
);
let second_delete_result = repo.query_delete_permission(id).await;
assert!(
second_delete_result.is_err(),
"Should fail on second delete"
);
if let Some(err) = second_delete_result.err() {
assert!(
err.to_string().contains("Permission not found"),
"Expected 'Permission not found' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_update_permission_should_fail_if_deleted() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let mut permission = create_dummy_permission("To Be Updated Then Deleted");
let _ = repo.query_create_permission(permission.clone()).await;
let id = permission.id.id.to_raw();
let _ = repo.query_delete_permission(id.clone()).await;
let delete_result = repo.query_delete_permission(id.clone()).await;
assert!(
delete_result.is_ok(),
"Initial delete failed: {:?}",
delete_result.err()
);
permission.name = "Try Update".into();
let result = repo.query_update_permission(permission).await;
assert!(result.is_err(), "Update on deleted should fail");
if let Some(err) = result.err() {
assert!(
err.to_string().contains("Permission not found"),
"Expected 'Permission not found' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_query_permission_by_id_should_fail_if_not_found() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use the new setup function
let repo = PermissionsRepository::new(&state);
let result = repo.query_permission_by_id("non-existent-id".into()).await;
assert!(result.is_err(), "Expected error for not found id");
if let Some(err) = result.err() {
assert!(
err.to_string().contains("Permission not found"),
"Expected 'Permission not found' error, got: {err}"
);
}
}
+173 -55
View File
@@ -1,5 +1,5 @@
use crate::{
ResourceEnum, create_mock_app_state, get_iso_date, make_thing,
get_iso_date, make_thing,
permissions::{
permissions_repository::PermissionsRepository,
permissions_schema::PermissionsSchema,
@@ -8,6 +8,7 @@ use crate::{
roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto},
roles_repository::RolesRepository,
},
setup_all_test_environment, ResourceEnum,
};
use surrealdb::Uuid;
@@ -17,58 +18,92 @@ fn generate_unique_name(prefix: &str) -> String {
#[tokio::test]
async fn test_query_create_role_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let perm_repo = PermissionsRepository::new(&state);
let role_repo = RolesRepository::new(&state);
let perm_id = Uuid::new_v4().to_string();
let permission = PermissionsSchema {
id: make_thing(&ResourceEnum::Permissions.to_string(), &perm_id.clone()),
id: make_thing(&ResourceEnum::Permissions.to_string(), &perm_id),
name: generate_unique_name("read_quiz"),
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
perm_repo.query_create_permission(permission).await.unwrap();
let perm_res = perm_repo.query_create_permission(permission).await;
assert!(
perm_res.is_ok(),
"Failed to create permission: {:?}",
perm_res.err()
);
let payload = RolesRequestCreateDto {
name: generate_unique_name("user"),
permissions: vec![perm_id.clone()],
};
let result = role_repo.query_create_role(payload).await;
assert!(result.is_ok());
assert!(result.is_ok(), "Failed to create role: {:?}", result.err());
}
#[tokio::test]
async fn test_query_role_by_name_should_return_data() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let role_repo = RolesRepository::new(&state);
let name = generate_unique_name("viewer");
let payload = RolesRequestCreateDto {
name: name.clone(),
permissions: vec![],
};
role_repo.query_create_role(payload.clone()).await.unwrap();
let role = role_repo.query_role_by_name(name.clone()).await.unwrap();
let create_res = role_repo.query_create_role(payload.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let role = role_repo.query_role_by_name(name.clone()).await;
assert!(role.is_ok(), "Failed to get role by name: {:?}", role.err());
let role = role.unwrap();
assert_eq!(role.name, name.clone());
}
#[tokio::test]
async fn test_query_role_by_id_should_return_data() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let role_repo = RolesRepository::new(&state);
let name = generate_unique_name("tester");
let payload = RolesRequestCreateDto {
name: name.clone(),
permissions: vec![],
};
role_repo.query_create_role(payload.clone()).await.unwrap();
let role = role_repo.query_role_by_name(name.clone()).await.unwrap();
let result = role_repo.query_role_by_id(role.id).await.unwrap();
assert_eq!(result.name, name.clone());
let create_res = role_repo.query_create_role(payload.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let role = role_repo.query_role_by_name(name.clone()).await;
assert!(role.is_ok(), "Failed to get role by name: {:?}", role.err());
let role = role.unwrap();
let result = role_repo.query_role_by_id(role.id.clone()).await;
assert!(
result.is_ok(),
"Failed to get role by id: {:?}",
result.err()
);
let result_role = result.unwrap();
assert_eq!(result_role.name, name.clone());
}
#[tokio::test]
async fn test_query_update_role_should_update_name_and_permissions() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let repo = RolesRepository::new(&state);
let perm_repo = PermissionsRepository::new(&state);
let original_perm_id = Uuid::new_v4().to_string();
@@ -77,22 +112,32 @@ async fn test_query_update_role_should_update_name_and_permissions() {
name: generate_unique_name("original_permission"),
is_deleted: false,
created_at: Some(crate::get_iso_date()),
updated_at: None,
updated_at: Some(crate::get_iso_date()),
};
perm_repo
.query_create_permission(original_perm)
.await
.unwrap();
let perm_res = perm_repo.query_create_permission(original_perm).await;
assert!(
perm_res.is_ok(),
"Failed to create original permission: {:?}",
perm_res.err()
);
let role_upadate_name = generate_unique_name("role_for_update");
let create_payload = RolesRequestCreateDto {
name: role_upadate_name.clone(),
permissions: vec![original_perm_id.clone()],
};
repo.query_create_role(create_payload).await.unwrap();
let existing_role = repo
.query_role_by_name(role_upadate_name.clone())
.await
.unwrap();
let create_res = repo.query_create_role(create_payload).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let existing_role = repo.query_role_by_name(role_upadate_name.clone()).await;
assert!(
existing_role.is_ok(),
"Failed to get role by name: {:?}",
existing_role.err()
);
let existing_role = existing_role.unwrap();
let existing_role_id = existing_role.id.clone();
let new_perm_id = Uuid::new_v4().to_string();
let new_perm = PermissionsSchema {
@@ -100,10 +145,15 @@ async fn test_query_update_role_should_update_name_and_permissions() {
name: "New Permission".into(),
is_deleted: false,
created_at: Some(crate::get_iso_date()),
updated_at: None,
updated_at: Some(crate::get_iso_date()),
};
let new_role_name = generate_unique_name("updated_role_name");
perm_repo.query_create_permission(new_perm).await.unwrap();
let perm_res = perm_repo.query_create_permission(new_perm).await;
assert!(
perm_res.is_ok(),
"Failed to create new permission: {:?}",
perm_res.err()
);
let update_payload = RolesRequestUpdateDto {
name: Some(new_role_name.clone()),
permissions: Some(vec![new_perm_id.clone()]),
@@ -112,34 +162,57 @@ async fn test_query_update_role_should_update_name_and_permissions() {
let update_result = repo
.query_update_role(existing_role_id.clone(), update_payload)
.await;
assert!(update_result.is_ok());
let updated = repo
.query_role_by_id(existing_role_id.clone())
.await
.unwrap();
assert_eq!(updated.name, new_role_name.clone());
assert!(
update_result.is_ok(),
"Failed to update role: {:?}",
update_result.err()
);
let updated = repo.query_role_by_id(existing_role_id.clone()).await;
assert!(
updated.is_ok(),
"Failed to get updated role: {:?}",
updated.err()
);
assert_eq!(updated.unwrap().name, new_role_name.clone());
}
#[tokio::test]
async fn test_query_delete_role_should_soft_delete() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let role_repo = RolesRepository::new(&state);
let name = generate_unique_name("temporary");
let payload = RolesRequestCreateDto {
name: name.clone(),
permissions: vec![],
};
role_repo.query_create_role(payload.clone()).await.unwrap();
let role = role_repo.query_role_by_name(name.clone()).await.unwrap();
let create_res = role_repo.query_create_role(payload.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let role = role_repo.query_role_by_name(name.clone()).await;
assert!(role.is_ok(), "Failed to get role by name: {:?}", role.err());
let role = role.unwrap();
let result = role_repo.query_delete_role(role.id.clone()).await;
assert!(result.is_ok());
assert!(result.is_ok(), "Failed to delete role: {:?}", result.err());
let deleted = role_repo.query_role_by_id(role.id).await;
assert!(deleted.is_err());
assert!(
deleted.is_err(),
"Role should be deleted, but got: {deleted:?}"
);
if let Some(err) = deleted.err() {
assert!(
err.to_string().contains("Role not found"),
"Expected 'Role not found' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided()
{
let state = create_mock_app_state().await;
async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided(
) {
let state = setup_all_test_environment().await;
let repo = RolesRepository::new(&state);
let perm_repo = PermissionsRepository::new(&state);
let perm_id = Uuid::new_v4().to_string();
@@ -148,18 +221,31 @@ async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_
name: "Permission for Fallback".into(),
is_deleted: false,
created_at: Some(crate::get_iso_date()),
updated_at: None,
updated_at: Some(crate::get_iso_date()),
};
perm_repo.query_create_permission(permission).await.unwrap();
let perm_res = perm_repo.query_create_permission(permission).await;
assert!(
perm_res.is_ok(),
"Failed to create permission: {:?}",
perm_res.err()
);
let create_payload = RolesRequestCreateDto {
name: "Role With Permission".into(),
permissions: vec![perm_id.clone()],
};
repo.query_create_role(create_payload).await.unwrap();
let existing = repo
.query_role_by_name("Role With Permission".into())
.await
.unwrap();
let create_res = repo.query_create_role(create_payload).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let existing = repo.query_role_by_name("Role With Permission".into()).await;
assert!(
existing.is_ok(),
"Failed to get role by name: {:?}",
existing.err()
);
let existing = existing.unwrap();
let existing_id = existing.id.clone();
let update_payload = RolesRequestUpdateDto {
name: Some("Updated Role Name".into()),
@@ -169,29 +255,61 @@ async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_
let update_res = repo
.query_update_role(existing_id.clone(), update_payload)
.await;
assert!(update_res.is_ok());
assert!(
update_res.is_ok(),
"Failed to update role (fallback): {:?}",
update_res.err()
);
}
#[tokio::test]
async fn test_query_role_by_name_should_fail_if_not_found() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let role_repo = RolesRepository::new(&state);
let result = role_repo.query_role_by_name("ghost-role".into()).await;
assert!(result.is_err());
if let Some(err) = result.err() {
assert!(
err.to_string().contains("Role not found"),
"Expected 'Role not found' error, got: {err}"
);
}
}
#[tokio::test]
async fn test_query_delete_role_should_fail_if_already_deleted() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await;
let role_repo = RolesRepository::new(&state);
let name = generate_unique_name("soft_delete_test");
let payload = RolesRequestCreateDto {
name: name.clone(),
permissions: vec![],
};
role_repo.query_create_role(payload.clone()).await.unwrap();
let role = role_repo.query_role_by_name(name.clone()).await.unwrap();
role_repo.query_delete_role(role.id.clone()).await.unwrap();
let result = role_repo.query_delete_role(role.id);
assert!(result.await.is_err());
let create_res = role_repo.query_create_role(payload.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create role: {:?}",
create_res.err()
);
let role = role_repo.query_role_by_name(name.clone()).await;
assert!(role.is_ok(), "Failed to get role by name: {:?}", role.err());
let role = role.unwrap();
let del_res = role_repo.query_delete_role(role.id.clone()).await;
assert!(
del_res.is_ok(),
"Failed to delete role: {:?}",
del_res.err()
);
let result_fut = role_repo.query_delete_role(role.id);
let result_val = result_fut.await;
assert!(
result_val.is_err(),
"Role should already be deleted, but got: {result_val:?}"
);
if let Some(err) = result_val.err() {
assert!(
err.to_string().contains("Role not found"),
"Expected 'Role not found' error, got: {err}"
);
}
}
+124 -38
View File
@@ -1,31 +1,44 @@
use crate::{MetaRequestDto, UsersRepository, generate_unique_email, get_role_id};
use crate::{create_mock_app_state, create_test_user};
use crate::{create_test_user, mock_test::setup_all_test_environment};
use crate::{generate_unique_email, get_role_id, MetaRequestDto, UsersRepository}; // Import setup_all_test_environment from mock_test
#[tokio::test]
async fn test_create_and_get_user() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
let email = generate_unique_email("testuser");
let user =
create_test_user(&email, "Test User", true, &get_role_id(&app_state).await);
let create_result = repo.query_create_user(user.clone()).await;
assert!(create_result.is_ok());
let fetched = repo.query_user_by_email(email.clone().into()).await;
assert!(fetched.is_ok());
assert!(
create_result.is_ok(),
"Failed to create user: {:?}",
create_result.err()
);
let fetched = repo.query_user_by_email(email.clone()).await;
assert!(
fetched.is_ok(),
"Failed to fetch user by email: {:?}",
fetched.err()
);
assert_eq!(fetched.unwrap().email, email.clone());
}
#[tokio::test]
async fn test_query_user_list_with_pagination_and_filter() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
for i in 0..10 {
let email = format!("user{}@example.com", i);
let fullname = format!("User {}", i);
let email = format!("user{i}@example.com");
let fullname = format!("User {i}");
let is_active = i % 2 == 0;
let user =
create_test_user(&email, &fullname, is_active, &get_role_id(&app_state).await);
repo.query_create_user(user).await.unwrap();
let create_res = repo.query_create_user(user).await;
assert!(
create_res.is_ok(),
"Failed to create user: {:?}",
create_res.err()
);
}
let meta = MetaRequestDto {
page: Some(1),
@@ -36,25 +49,43 @@ async fn test_query_user_list_with_pagination_and_filter() {
filter: Some("true".into()),
filter_by: Some("is_active".into()),
};
let result = repo.query_user_list(meta).await.unwrap();
let result = repo.query_user_list(meta).await;
assert!(
result.is_ok(),
"Failed to query user list: {:?}",
result.err()
);
let result = result.unwrap();
assert!(result.data.len() <= 5);
assert!(result.data.iter().all(|u| u.is_active));
assert!(result.meta.as_ref().unwrap().total.is_some());
assert!(
result
.meta
.as_ref()
.map(|m| m.total.is_some())
.unwrap_or(false),
"Meta total should be Some"
);
}
#[tokio::test]
async fn test_query_user_list_basic() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
for i in 0..10 {
let email = format!("basic{}@example.com", i);
let email = format!("basic{i}@example.com");
let user = create_test_user(
&email,
&format!("Basic User {}", i),
&format!("Basic User {i}"),
true,
&get_role_id(&app_state).await,
);
repo.query_create_user(user).await.unwrap();
let create_res = repo.query_create_user(user).await;
assert!(
create_res.is_ok(),
"Failed to create user: {:?}",
create_res.err()
);
}
let meta = MetaRequestDto {
page: Some(1),
@@ -65,59 +96,103 @@ async fn test_query_user_list_basic() {
filter: None,
filter_by: None,
};
let result = repo.query_user_list(meta).await.unwrap();
assert!(result.meta.as_ref().unwrap().total.unwrap() >= 1);
assert_eq!(result.meta.as_ref().unwrap().page.unwrap(), 1);
assert_eq!(result.meta.as_ref().unwrap().per_page.unwrap(), 5);
let result = repo.query_user_list(meta).await;
assert!(
result.is_ok(),
"Failed to query user list: {:?}",
result.err()
);
let result = result.unwrap();
assert!(
result.meta.as_ref().and_then(|m| m.total).unwrap_or(0) >= 1,
"Meta total should be >= 1"
);
assert_eq!(
result.meta.as_ref().and_then(|m| m.page).unwrap_or(0),
1,
"Meta page should be 1"
);
assert_eq!(
result.meta.as_ref().and_then(|m| m.per_page).unwrap_or(0),
5,
"Meta per_page should be 5"
);
}
#[tokio::test]
async fn test_query_delete_user() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
let email = &generate_unique_email("deleteuser");
let user =
create_test_user(email, "Delete User", true, &get_role_id(&app_state).await);
repo.query_create_user(user.clone()).await.unwrap();
let user_detail = repo
.query_user_by_email(email.to_string().clone())
.await
.unwrap();
let create_res = repo.query_create_user(user.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create user: {:?}",
create_res.err()
);
let user_detail = repo.query_user_by_email(email.to_string().clone()).await;
assert!(
user_detail.is_ok(),
"Failed to fetch user by email: {:?}",
user_detail.err()
);
let user_detail = user_detail.unwrap();
let delete_result = repo
.query_delete_user(user_detail.id.id.to_raw().clone())
.await;
assert!(delete_result.is_ok());
assert!(
delete_result.is_ok(),
"Failed to delete user: {:?}",
delete_result.err()
);
let fetch_result = repo.query_user_by_email(user_detail.email.clone()).await;
assert!(fetch_result.is_err());
assert!(
fetch_result.is_err(),
"User should be deleted, but got: {fetch_result:?}"
);
}
#[tokio::test]
async fn test_delete_non_existent_user_should_fail() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
let result = repo.query_delete_user("lklklklk".to_string()).await;
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "User not found");
assert!(result.is_err(), "Delete non-existent user should fail");
assert_eq!(
result.unwrap_err().to_string(),
"User not found in database"
);
}
#[tokio::test]
async fn test_delete_user_twice_should_fail_on_second_attempt() {
let app_state = create_mock_app_state().await;
let app_state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&app_state);
let email = "twice@example.com";
let user =
create_test_user(email, "Delete Twice", true, &get_role_id(&app_state).await);
repo.query_create_user(user.clone()).await.unwrap();
let create_res = repo.query_create_user(user.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create user: {:?}",
create_res.err()
);
let first = repo.query_delete_user(user.id.id.to_raw()).await;
assert!(first.is_ok());
assert!(
first.is_ok(),
"First delete should succeed: {:?}",
first.err()
);
let second = repo.query_delete_user(user.id.id.to_raw()).await;
assert!(second.is_err());
assert!(second.is_err(), "Second delete should fail");
assert_eq!(second.unwrap_err().to_string(), "User not found");
}
#[tokio::test]
async fn test_query_update_user_should_succeed() {
let state = create_mock_app_state().await;
let state = setup_all_test_environment().await; // Use centralized setup
let repo = UsersRepository::new(&state);
let mut user = create_test_user(
"update@example.com",
@@ -125,12 +200,23 @@ async fn test_query_update_user_should_succeed() {
true,
&get_role_id(&state).await,
);
repo.query_create_user(user.clone()).await.unwrap();
let create_res = repo.query_create_user(user.clone()).await;
assert!(
create_res.is_ok(),
"Failed to create user: {:?}",
create_res.err()
);
user.fullname = "Updated Name".into();
user.phone_number = "089876543210".into();
let result = repo.query_update_user(user.clone()).await;
assert!(result.is_ok(), "Update failed: {:?}", result.err());
let updated = repo.query_user_by_id(user.id.id.to_raw()).await.unwrap();
let updated = repo.query_user_by_id(&user.id).await;
assert!(
updated.is_ok(),
"Failed to fetch updated user: {:?}",
updated.err()
);
let updated = updated.unwrap();
assert_eq!(updated.fullname, "Updated Name");
assert_eq!(updated.phone_number, "089876543210");
}
+50 -1
View File
@@ -1,9 +1,49 @@
use ::surrealdb::Uuid;
pub use imphnen_entities::*;
pub use imphnen_iam::*;
pub fn create_test_mentor(
email: &str,
fullname: &str,
is_active: bool,
role_id: &str,
) -> UsersSchema {
let mut user = create_test_user(email, fullname, is_active, role_id);
user.mentor_id = Some(user.id.clone());
user
}
pub fn create_test_user(
email: &str,
fullname: &str,
is_active: bool,
role_id: &str,
) -> UsersSchema {
UsersSchema {
id: make_thing("app_users", &Uuid::new_v4().to_string()),
email: email.to_string(),
fullname: format!("Randomize {} {}", fullname, rand::random::<u32>()),
password: hash_password("secret").unwrap(),
is_deleted: false,
avatar: None,
phone_number: "081234567890".to_string(),
is_active,
gender: None,
birthdate: None,
role: make_thing("app_roles", role_id),
created_at: get_iso_date(),
updated_at: get_iso_date(),
mentor_id: None,
}
}
#[cfg(test)]
pub mod iam;
pub mod mock_test;
pub use mock_test::{
cleanup_db, create_mock_app_state, seed_permissions_and_roles_for_test,
seed_users_for_test, setup_all_test_environment,
};
pub use imphnen_utils::{get_iso_date, hash_password, make_thing, Env};
pub fn generate_unique_email(prefix: &str) -> String {
format!("{}_{}@example.com", prefix, Uuid::new_v4())
@@ -26,3 +66,12 @@ pub async fn get_role_id(state: &crate::AppState) -> String {
.expect("Role not found after creation")
.id
}
pub async fn setup() {
cleanup_db().await;
let app_state = create_mock_app_state().await;
seed_permissions_and_roles_for_test(&app_state.surrealdb_ws)
.await
.unwrap();
seed_users_for_test(&app_state.surrealdb_ws).await.unwrap();
}
+197
View File
@@ -0,0 +1,197 @@
use crate::{get_iso_date, hash_password};
use imphnen_entities::AppState;
use imphnen_iam::{PermissionsEnum, UsersSchema};
use imphnen_libs::enviroment::load_env;
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use surrealdb::engine::{any, local};
use surrealdb::{opt::auth::Root, sql::Thing, Connection, Surreal};
use tracing::debug;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug)]
struct PermissionSeedData {
id: String,
name: String,
is_deleted: bool,
created_at: String,
updated_at: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct RoleSeedData {
id: String,
name: String,
is_deleted: bool,
created_at: String,
updated_at: String,
permissions: Vec<Thing>,
}
pub async fn create_mock_app_state() -> AppState {
load_env();
let db_ws = any::connect("ws://127.00.1:8000/rpc").await.unwrap();
let db_mem = Surreal::new::<local::Mem>(()).await.unwrap();
let unique_id = Uuid::new_v4().to_string();
let ns = format!("test_ns_{unique_id}");
let db = format!("test_db_{unique_id}");
db_ws
.signin(Root {
username: "root",
password: "root",
})
.await
.unwrap();
db_ws.use_ns(&ns).use_db(&db).await.unwrap();
AppState {
surrealdb_ws: db_ws,
surrealdb_mem: db_mem,
}
}
pub async fn cleanup_db() {
let app_state = create_mock_app_state().await;
let _ = app_state
.surrealdb_ws
.query(
r#"REMOVE TABLE app_users; REMOVE TABLE app_roles; REMOVE TABLE app_permissions; REMOVE TABLE app_roles_permissions; REMOVE TABLE app_users_cache; REMOVE TABLE app_otp_cache; REMOVE TABLE app_gacha_items; REMOVE TABLE app_gacha_claims; REMOVE TABLE app_gacha_rolls; REMOVE TABLE app_gacha_credits; REMOVE TABLE app_events; REMOVE TABLE app_testimonials; REMOVE TABLE app_mentors;"#,
)
.await;
}
pub async fn seed_permissions_and_roles_for_test(
db: &Surreal<impl Connection>,
) -> Result<(), Box<dyn std::error::Error>> {
db.query("DEFINE TABLE app_permissions;").await?;
db.query("DEFINE FIELD name ON app_permissions TYPE string;")
.await?;
db.query("DEFINE FIELD is_deleted ON app_permissions TYPE bool;")
.await?;
db.query("DEFINE FIELD created_at ON app_permissions TYPE string;")
.await?;
db.query("DEFINE FIELD updated_at ON app_permissions TYPE string;")
.await?;
db.query("DEFINE TABLE app_roles;").await?;
db.query("DEFINE FIELD name ON app_roles TYPE string;")
.await?;
db.query("DEFINE FIELD is_deleted ON app_roles TYPE bool;")
.await?;
// Changed from array<string> to array<record<app_permissions>>
db.query(
"DEFINE FIELD permissions ON app_roles TYPE array<record<app_permissions>>;",
)
.await?;
db.query("DEFINE FIELD created_at ON app_roles TYPE string;")
.await?;
db.query("DEFINE FIELD updated_at ON app_roles TYPE string;")
.await?;
for perm_enum in PermissionsEnum::iter() {
db.query("INSERT INTO app_permissions (id, name, is_deleted, created_at, updated_at) VALUES ($id, $name, $is_deleted, $created_at, $updated_at);")
.bind(("id", perm_enum.id()))
.bind(("name", perm_enum.to_string()))
.bind(("is_deleted", false))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.await?;
}
let roles = vec![
("f6b03f25-e416-4893-ac88-caaa690afb07".to_string(), "Admin"),
("3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a".to_string(), "Mentor"),
("50133429-f4b1-4249-9f97-7b86e6ee9d86".to_string(), "Staff"),
("5713cb37-dc02-4e87-8048-d7a41d352059".to_string(), "User"),
];
for (id, name) in roles {
db.query("INSERT INTO app_roles (id, name, is_deleted, created_at, updated_at, permissions) VALUES ($id, $name, $is_deleted, $created_at, $updated_at, $permissions);")
.bind(("id", id))
.bind(("name", name))
.bind(("is_deleted", false))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.bind(("permissions", Vec::<Thing>::new())) // Use Vec<Thing> for permissions
.await?;
}
Ok(())
}
pub async fn seed_users_for_test(
db: &Surreal<impl Connection>,
) -> Result<(), Box<dyn std::error::Error>> {
let users_data = vec![
(
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2",
"admin@example.com",
"Admin",
"f6b03f25-e416-4893-ac88-caaa690afb07",
),
(
"a4d23fb5-9e31-423c-9842-fbd6e75a5298",
"staff@example.com",
"Staff",
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
),
(
"d5e89c12-72af-4b1a-abc3-ff1234567890",
"user@example.com",
"User",
"5713cb37-dc02-4e87-8048-d7a41d352059",
),
];
for (id, email, fullname, role_id) in users_data {
let user = UsersSchema {
id: Thing::from(("app_users", id)),
fullname: fullname.into(),
email: email.into(),
password: hash_password("password").unwrap(),
avatar: None,
phone_number: "081234567890".into(),
is_active: true,
is_deleted: false,
mentor_id: None,
gender: None,
birthdate: None,
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?;
println!("✅ Inserted user: {fullname} ({email})");
}
Ok(())
}
pub async fn setup_all_test_environment() -> AppState {
let app_state = create_mock_app_state().await;
app_state
.surrealdb_mem
.use_ns("test_namespace")
.use_db("test_db")
.await
.unwrap();
seed_permissions_and_roles_for_test(&app_state.surrealdb_ws)
.await
.unwrap();
app_state
}