From 789b20c278e18f8f0232f1b665502a7add486fa3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 11 Oct 2025 15:33:52 +0700 Subject: [PATCH] tests(hackathon): align tests with schema changes and fix clippy warnings --- .../hackathon/hackathon_controller_test.rs | 1614 ++++++++--------- .../hackathon/hackathon_repository_test.rs | 1440 +++++++-------- tests/src/hackathon/hackathon_service_test.rs | 1472 +++++++-------- 3 files changed, 2263 insertions(+), 2263 deletions(-) diff --git a/tests/src/hackathon/hackathon_controller_test.rs b/tests/src/hackathon/hackathon_controller_test.rs index 8d23205..a80a80d 100644 --- a/tests/src/hackathon/hackathon_controller_test.rs +++ b/tests/src/hackathon/hackathon_controller_test.rs @@ -1,808 +1,808 @@ -#[cfg(test)] -mod tests { - use crate::{generate_unique_email, get_role_id, UsersRepository}; - use axum::http::StatusCode; - - use imphnen_hackathon::v1::hackathon::hackathon_dto::{ - HackathonCreateRequestDto, HackathonSubmissionCreateRequestDto, - }; - use imphnen_hackathon::v1::hackathon::HackathonRepository; - use imphnen_iam::v1::teams::teams_dto::{TeamsCreateRequestDto}; - use imphnen_iam::v1::teams::teams_repository::TeamsRepository; - use imphnen_iam::v1::teams::teams_schema::TeamMembersSchema; - - use chrono::{Utc, Duration}; - use serde_json::json; - - - #[tokio::test] - async fn test_create_hackathon() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - - // Create test organizer - let email = generate_unique_email("hackathon_organizer_controller"); - let role_id = get_role_id("mentor", &app.state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon request - let hackathon_request = HackathonCreateRequestDto { - name: "Test Hackathon Controller".to_string(), - description: "Hackathon created via controller test".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - // Send create request - let response = app.service.post("/api/v1/hackathons") - .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) - .json(&hackathon_request) - .await - .unwrap(); - - // For debugging: capture status then extract body so we can print server error details - let status = response.status(); - let body = crate::get_response_body(response).await; - eprintln!("[DEBUG] create_hackathon status: {}", status); - eprintln!("[DEBUG] create_hackathon body: {}", body); - - // Verify response - assert_eq!(status, StatusCode::CREATED); - assert_eq!(body["message"], "Success create hackathon"); - assert_eq!(body["data"]["name"], "Test Hackathon Controller"); - assert_eq!(body["data"]["description"], "Hackathon created via controller test"); - - // Clean up - let hackathon_id = body["data"]["id"].as_str().unwrap().to_string(); - let repo = HackathonRepository::new(&app.state); - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_get_hackathon_by_id() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test organizer and hackathon - let email = generate_unique_email("hackathon_get_controller"); - let role_id = get_role_id("mentor", &app.state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - let hackathon_request = HackathonCreateRequestDto { - name: "Get Hackathon Test".to_string(), - description: "Test hackathon for get by ID".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = created.id.id.to_raw(); - - // Send get request - let response = app.service.get(format!("/api/v1/hackathons/{}", hackathon_id)) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["data"]["name"], "Get Hackathon Test"); - assert_eq!(body["data"]["description"], "Test hackathon for get by ID"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_update_hackathon() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test organizer and hackathon - let email = generate_unique_email("hackathon_update_controller"); - let role_id = get_role_id("mentor", &app.state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - let hackathon_request = HackathonCreateRequestDto { - name: "Original Hackathon Controller".to_string(), - description: "Original description".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = create_result.id.id.to_raw(); - - // Prepare update payload using the public update DTO - let update_payload = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonUpdateRequestDto { - name: Some("Updated Hackathon Controller".to_string()), - description: Some("Updated description via controller".to_string()), - start_date: None, - end_date: None, - registration_deadline: None, - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: None, - }; - - // Send update request - let response = app.service.put(format!("/api/v1/hackathons/{}", hackathon_id)) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) - .json(&update_payload) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["message"], "Success update hackathon"); - assert_eq!(body["data"]["name"], "Updated Hackathon Controller"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_delete_hackathon() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test organizer and hackathon - let email = generate_unique_email("hackathon_delete_controller"); - let role_id = get_role_id("mentor", &app.state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - let hackathon_request = HackathonCreateRequestDto { - name: "Hackathon to Delete Controller".to_string(), - description: "Hackathon for deletion test".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = create_result.id.id.to_raw(); - - // Send delete request - let response = app.service.delete(format!("/api/v1/hackathons/{}", hackathon_id)) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["message"], "Success delete hackathon"); - - // Verify hackathon is deleted - let get_response = app.service.get(format!("/api/v1/hackathons/{}", hackathon_id)) - .await - .unwrap(); - assert_eq!(get_response.status(), StatusCode::NOT_FOUND); - - // Clean up - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_submit_project_as_user() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test users and hackathon - let organizer_email = generate_unique_email("hackathon_submit_organizer_controller"); - let participant_email = generate_unique_email("hackathon_submit_participant_controller"); - - let role_id = get_role_id("mentee", &app.state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - let hackathon_request = HackathonCreateRequestDto { - name: "User Submission Test Controller".to_string(), - description: "Test hackathon for user submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = create_result.id.id.to_raw(); - - // Submit project request - let submission_dto = HackathonSubmissionCreateRequestDto { - project_name: "My Rust Project Controller".to_string(), - description: "A cool Rust project for the hackathon".to_string(), - repository_url: Some("https://github.com/user/my-rust-project".to_string()), - demo_url: Some("https://my-rust-project.com".to_string()), - slides_url: None, - technologies: vec![], - }; - - // Send submission request (use team path; for single-user tests we pass participant id as team_id) - let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) - .json(&submission_dto) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::CREATED); - let body = crate::get_response_body(response).await; - assert_eq!(body["message"], "Success submit project"); - assert_eq!(body["data"]["project_name"], "My Rust Project Controller"); - assert_eq!(body["data"]["status"], "Draft"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_submit_project_as_team() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let teams_repo = TeamsRepository::new(&app.state); - let hackathon_repo = HackathonRepository::new(&app.state); - - // Create test users, team, and hackathon - let organizer_email = generate_unique_email("hackathon_team_submit_organizer_controller"); - let member1_email = generate_unique_email("team_member1_submit_controller"); - let member2_email = generate_unique_email("team_member2_submit_controller"); - - let role_id = get_role_id("mentee", &app.state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); - let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let member1_result = users_repo.query_create_user(member1_data.clone()).await; - let member2_result = users_repo.query_create_user(member2_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(member1_result.is_ok(), "Failed to create member1"); - assert!(member2_result.is_ok(), "Failed to create member2"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); - let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); - - // Create team - let team_request = TeamsCreateRequestDto { - name: "Hackathon Team Controller".to_string(), - description: Some("Team for hackathon submissions".to_string()), - is_open: Some(false), - max_members: Some(5), - skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), - location: Some("Remote".to_string()), - website_url: None, - github_url: None, - avatar: None, - member_emails: vec![], - }; - - let team_schema = imphnen_iam::TeamsSchema::create(team_request, member1.id.id.to_raw()); - let team_create_result = teams_repo.query_create_team(team_schema).await.unwrap(); - let team_id = team_create_result.split_whitespace().last().unwrap().to_string(); - - // Add team members - let member2_schema = TeamMembersSchema::create( - team_id.clone(), - member2.id.id.to_raw(), - Some("member".to_string()) - ); - let add_member_result = teams_repo.query_add_team_member(member2_schema).await; - assert!(add_member_result.is_ok(), "Failed to add team member"); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Team Submission Test Controller".to_string(), - description: "Test hackathon for team submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = hackathon_repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = create_result.id.id.to_raw(); - - // Submit team project request - let team_submission_dto = HackathonSubmissionCreateRequestDto { - project_name: "Our Team Rust Project Controller".to_string(), - description: "A collaborative Rust project by our team".to_string(), - repository_url: Some("https://github.com/team/our-rust-project".to_string()), - demo_url: Some("https://our-team-project.com".to_string()), - slides_url: Some("https://docs.google.com/presentation/d/12345".to_string()), - technologies: vec![], - }; - - // Send team submission request (use teams path) - let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, team_id)) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&member1.id.id.to_raw()).await)) - .json(&team_submission_dto) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::CREATED); - let body = crate::get_response_body(response).await; - assert_eq!(body["message"], "Success submit team project"); - assert_eq!(body["data"]["project_name"], "Our Team Rust Project Controller"); - assert_eq!(body["data"]["status"], "Draft"); - assert_eq!(body["data"]["team_id"], team_id); - - // Clean up - let _ = hackathon_repo.delete_hackathon(hackathon_id).await; - let _ = teams_repo.query_delete_team(team_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_get_hackathon_submissions() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test users and hackathon - let organizer_email = generate_unique_email("hackathon_submissions_organizer_controller"); - let participant_email = generate_unique_email("hackathon_submissions_participant_controller"); - - let role_id = get_role_id("mentee", &app.state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Submissions Test Controller".to_string(), - description: "Test hackathon for retrieving submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - let hackathon_id = create_result.id.id.to_raw(); - - // Submit multiple projects - let submission_dtos = [ - HackathonSubmissionCreateRequestDto { - project_name: "Project 1 Controller".to_string(), - description: "Description 1".to_string(), - repository_url: Some("https://github.com/user/project1".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }, - HackathonSubmissionCreateRequestDto { - project_name: "Project 2 Controller".to_string(), - description: "Description 2".to_string(), - repository_url: Some("https://github.com/user/project2".to_string()), - demo_url: Some("https://project2.com".to_string()), - slides_url: None, - technologies: vec![], - } - ]; - - for submission_dto in submission_dtos.iter() { - let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) - .json(submission_dto) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::CREATED); - } - - // Get hackathon submissions - let response = app.service.get(format!("/api/v1/hackathons/{}/submissions", hackathon_id)) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["data"].as_array().unwrap().len(), 2); - assert_eq!(body["data"][0]["project_name"], "Project 1 Controller"); - assert_eq!(body["data"][1]["project_name"], "Project 2 Controller"); - assert_eq!(body["data"][0]["status"], "Draft"); - assert_eq!(body["data"][1]["status"], "Draft"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_get_user_hackathon_submissions() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test users and hackathons - let organizer_email = generate_unique_email("hackathon_user_submissions_organizer_controller"); - let participant_email = generate_unique_email("hackathon_user_submissions_participant_controller"); - - let role_id = get_role_id("mentee", &app.state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create multiple hackathons - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Hackathon 1 Controller".to_string(), - description: "First hackathon".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Hackathon 2 Controller".to_string(), - description: "Second hackathon".to_string(), - start_date: Utc::now() + Duration::days(14), - end_date: Utc::now() + Duration::days(21), - registration_deadline: Utc::now() + Duration::days(17), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - } - ]; - - let mut hackathon_ids = Vec::new(); - - for hackathon_request in hackathon_requests.iter() { - let create_result = repo.create_hackathon(hackathon_request.clone()).await.unwrap(); - let hackathon_id = create_result.id.id.to_raw(); - hackathon_ids.push(hackathon_id); - } - - // Submit projects to different hackathons using team endpoint (single-user uses user id as team_id) - let submission_requests = [ - HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 1 Controller".to_string(), - description: "Description for hackathon 1".to_string(), - repository_url: Some("https://github.com/user/hackathon1-project".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }, - HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 2 Controller".to_string(), - description: "Description for hackathon 2".to_string(), - repository_url: Some("https://github.com/user/hackathon2-project".to_string()), - demo_url: Some("https://hackathon2-project.com".to_string()), - slides_url: None, - technologies: vec![], - } - ]; - - for (i, create_req) in submission_requests.iter().enumerate() { - let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_ids[i], participant.id.id.to_raw())) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) - .json(create_req) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::CREATED); - } - - // Get user's hackathon submissions - let response = app.service.get(format!("/api/v1/users/{}/hackathon-submissions", participant.id.id.to_raw())) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["data"].as_array().unwrap().len(), 2); - assert_eq!(body["data"][0]["project_name"], "Project for Hackathon 1 Controller"); - assert_eq!(body["data"][1]["project_name"], "Project for Hackathon 2 Controller"); - assert_eq!(body["data"][0]["status"], "Draft"); - assert_eq!(body["data"][1]["status"], "Draft"); - - // Clean up - for hackathon_id in hackathon_ids { - let _ = repo.delete_hackathon(hackathon_id).await; - } - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_search_hackathons() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test organizer and hackathons - let email = generate_unique_email("hackathon_search_controller"); - let role_id = get_role_id("mentor", &app.state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create test hackathons - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Rust Backend Hackathon Controller".to_string(), - description: "Build Rust backend projects".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: Some("Backend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "TypeScript Frontend Hackathon Controller".to_string(), - description: "Build TypeScript frontend projects".to_string(), - start_date: Utc::now() + Duration::days(14), - end_date: Utc::now() + Duration::days(21), - registration_deadline: Utc::now() + Duration::days(17), - max_participants: None, - theme: Some("Frontend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Rust Fullstack Hackathon Controller".to_string(), - description: "Build fullstack projects with Rust".to_string(), - start_date: Utc::now() + Duration::days(21), - end_date: Utc::now() + Duration::days(28), - registration_deadline: Utc::now() + Duration::days(24), - max_participants: None, - theme: Some("Fullstack".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - } - ]; - - for hackathon_request in hackathon_requests.iter() { - let create_result = repo.create_hackathon(hackathon_request.clone()).await.unwrap(); - // Store hackathon IDs for cleanup - let _ = create_result.id.id.to_raw(); - } - - // Test search with multiple parameters - let search_params = json!({ - "query": "Rust", - "category": "Backend", - "location": "Remote", - "is_featured": true, - "page": 1, - "per_page": 10 - }); - - let response = app.service.post("/api/v1/hackathons/search") - .json(&search_params) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["data"].as_array().unwrap().len(), 1); - assert_eq!(body["data"][0]["name"], "Rust Backend Hackathon Controller"); - assert!(body["data"][0]["description"].as_str().unwrap().contains("Rust")); - - // Clean up - in a real test you would store and delete all created hackathons - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_update_submission_status() { - let app = crate::get_full_test_app().await; - let users_repo = UsersRepository::new(&app.state); - let repo = HackathonRepository::new(&app.state); - - // Create test users and hackathon - let organizer_email = generate_unique_email("hackathon_status_organizer_controller"); - let participant_email = generate_unique_email("hackathon_status_participant_controller"); - - let role_id = get_role_id("mentee", &app.state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon and submission - let hackathon_request = HackathonCreateRequestDto { - name: "Submission Status Test Controller".to_string(), - description: "Test hackathon for submission status updates".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request).await.unwrap(); - let hackathon_id = create_result.id.id.to_raw(); - - let submission_dto = HackathonSubmissionCreateRequestDto { - project_name: "Test Project Controller".to_string(), - description: "Test description".to_string(), - repository_url: Some("https://github.com/user/test-project".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }; - - let submit_response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) - .json(&submission_dto) - .await - .unwrap(); - assert_eq!(submit_response.status(), StatusCode::CREATED); - - let submission_id = crate::get_response_body(submit_response).await["data"]["id"] - .as_str() - .unwrap() - .to_string(); - - // Update submission status to "Accepted" - let update_status = json!({ - "status": "Accepted", - "feedback": "Great project!" - }); - - let response = app.service.patch(format!("/api/v1/hackathons/submissions/{}/status", submission_id)) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&organizer.id.id.to_raw()).await)) - .json(&update_status) - .await - .unwrap(); - - // Verify response - assert_eq!(response.status(), StatusCode::OK); - let body = crate::get_response_body(response).await; - assert_eq!(body["message"], "Success update submission status"); - assert_eq!(body["data"]["status"], "Accepted"); - assert_eq!(body["data"]["judge_feedback"], "Great project!"); - - // Update status again to "Rejected" - let update_status2 = json!({ - "status": "Rejected", - "feedback": "Does not meet criteria" - }); - - let response2 = app.service.patch(format!("/api/v1/hackathons/submissions/{}/status", submission_id)) - .header("Authorization", format!("Bearer {}", crate::get_test_token(&organizer.id.id.to_raw()).await)) - .json(&update_status2) - .await - .unwrap(); - - // Verify second update - assert_eq!(response2.status(), StatusCode::OK); - let body2 = crate::get_response_body(response2).await; - assert_eq!(body2["message"], "Success update submission status"); - assert_eq!(body2["data"]["status"], "Rejected"); - assert_eq!(body2["data"]["judge_feedback"], "Does not meet criteria"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } +#[cfg(test)] +mod tests { + use crate::{generate_unique_email, get_role_id, UsersRepository}; + use axum::http::StatusCode; + + use imphnen_hackathon::v1::hackathon::hackathon_dto::{ + HackathonCreateRequestDto, HackathonSubmissionCreateRequestDto, + }; + use imphnen_hackathon::v1::hackathon::HackathonRepository; + use imphnen_iam::v1::teams::teams_dto::{TeamsCreateRequestDto}; + use imphnen_iam::v1::teams::teams_repository::TeamsRepository; + use imphnen_iam::v1::teams::teams_schema::TeamMembersSchema; + + use chrono::{Utc, Duration}; + use serde_json::json; + + + #[tokio::test] + async fn test_create_hackathon() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + + // Create test organizer + let email = generate_unique_email("hackathon_organizer_controller"); + let role_id = get_role_id("mentor", &app.state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon request + let hackathon_request = HackathonCreateRequestDto { + name: "Test Hackathon Controller".to_string(), + description: "Hackathon created via controller test".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + // Send create request + let response = app.service.post("/api/v1/hackathons") + .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) + .json(&hackathon_request) + .await + .unwrap(); + + // For debugging: capture status then extract body so we can print server error details + let status = response.status(); + let body = crate::get_response_body(response).await; + eprintln!("[DEBUG] create_hackathon status: {}", status); + eprintln!("[DEBUG] create_hackathon body: {}", body); + + // Verify response + assert_eq!(status, StatusCode::CREATED); + assert_eq!(body["message"], "Success create hackathon"); + assert_eq!(body["data"]["name"], "Test Hackathon Controller"); + assert_eq!(body["data"]["description"], "Hackathon created via controller test"); + + // Clean up + let hackathon_id = body["data"]["id"].as_str().unwrap().to_string(); + let repo = HackathonRepository::new(&app.state); + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_get_hackathon_by_id() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test organizer and hackathon + let email = generate_unique_email("hackathon_get_controller"); + let role_id = get_role_id("mentor", &app.state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + let hackathon_request = HackathonCreateRequestDto { + name: "Get Hackathon Test".to_string(), + description: "Test hackathon for get by ID".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = created.id.id.to_raw(); + + // Send get request + let response = app.service.get(format!("/api/v1/hackathons/{}", hackathon_id)) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["data"]["name"], "Get Hackathon Test"); + assert_eq!(body["data"]["description"], "Test hackathon for get by ID"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_update_hackathon() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test organizer and hackathon + let email = generate_unique_email("hackathon_update_controller"); + let role_id = get_role_id("mentor", &app.state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + let hackathon_request = HackathonCreateRequestDto { + name: "Original Hackathon Controller".to_string(), + description: "Original description".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = create_result.id.id.to_raw(); + + // Prepare update payload using the public update DTO + let update_payload = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonUpdateRequestDto { + name: Some("Updated Hackathon Controller".to_string()), + description: Some("Updated description via controller".to_string()), + start_date: None, + end_date: None, + registration_deadline: None, + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: None, + }; + + // Send update request + let response = app.service.put(format!("/api/v1/hackathons/{}", hackathon_id)) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) + .json(&update_payload) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["message"], "Success update hackathon"); + assert_eq!(body["data"]["name"], "Updated Hackathon Controller"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_delete_hackathon() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test organizer and hackathon + let email = generate_unique_email("hackathon_delete_controller"); + let role_id = get_role_id("mentor", &app.state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + let hackathon_request = HackathonCreateRequestDto { + name: "Hackathon to Delete Controller".to_string(), + description: "Hackathon for deletion test".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = create_result.id.id.to_raw(); + + // Send delete request + let response = app.service.delete(format!("/api/v1/hackathons/{}", hackathon_id)) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&user.id.id.to_raw()).await)) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["message"], "Success delete hackathon"); + + // Verify hackathon is deleted + let get_response = app.service.get(format!("/api/v1/hackathons/{}", hackathon_id)) + .await + .unwrap(); + assert_eq!(get_response.status(), StatusCode::NOT_FOUND); + + // Clean up + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_submit_project_as_user() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test users and hackathon + let organizer_email = generate_unique_email("hackathon_submit_organizer_controller"); + let participant_email = generate_unique_email("hackathon_submit_participant_controller"); + + let role_id = get_role_id("mentee", &app.state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + let hackathon_request = HackathonCreateRequestDto { + name: "User Submission Test Controller".to_string(), + description: "Test hackathon for user submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = create_result.id.id.to_raw(); + + // Submit project request + let submission_dto = HackathonSubmissionCreateRequestDto { + project_name: "My Rust Project Controller".to_string(), + description: "A cool Rust project for the hackathon".to_string(), + repository_url: Some("https://github.com/user/my-rust-project".to_string()), + demo_url: Some("https://my-rust-project.com".to_string()), + slides_url: None, + technologies: vec![], + }; + + // Send submission request (use team path; for single-user tests we pass participant id as team_id) + let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) + .json(&submission_dto) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::CREATED); + let body = crate::get_response_body(response).await; + assert_eq!(body["message"], "Success submit project"); + assert_eq!(body["data"]["project_name"], "My Rust Project Controller"); + assert_eq!(body["data"]["status"], "Draft"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_submit_project_as_team() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let teams_repo = TeamsRepository::new(&app.state); + let hackathon_repo = HackathonRepository::new(&app.state); + + // Create test users, team, and hackathon + let organizer_email = generate_unique_email("hackathon_team_submit_organizer_controller"); + let member1_email = generate_unique_email("team_member1_submit_controller"); + let member2_email = generate_unique_email("team_member2_submit_controller"); + + let role_id = get_role_id("mentee", &app.state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); + let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let member1_result = users_repo.query_create_user(member1_data.clone()).await; + let member2_result = users_repo.query_create_user(member2_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(member1_result.is_ok(), "Failed to create member1"); + assert!(member2_result.is_ok(), "Failed to create member2"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); + let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); + + // Create team + let team_request = TeamsCreateRequestDto { + name: "Hackathon Team Controller".to_string(), + description: Some("Team for hackathon submissions".to_string()), + is_open: Some(false), + max_members: Some(5), + skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), + location: Some("Remote".to_string()), + website_url: None, + github_url: None, + avatar: None, + member_emails: vec![], + }; + + let team_schema = imphnen_iam::TeamsSchema::create(team_request, member1.id.id.to_raw()); + let team_create_result = teams_repo.query_create_team(team_schema).await.unwrap(); + let team_id = team_create_result.split_whitespace().last().unwrap().to_string(); + + // Add team members + let member2_schema = TeamMembersSchema::create( + team_id.clone(), + member2.id.id.to_raw(), + Some("member".to_string()) + ); + let add_member_result = teams_repo.query_add_team_member(member2_schema).await; + assert!(add_member_result.is_ok(), "Failed to add team member"); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Team Submission Test Controller".to_string(), + description: "Test hackathon for team submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = hackathon_repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = create_result.id.id.to_raw(); + + // Submit team project request + let team_submission_dto = HackathonSubmissionCreateRequestDto { + project_name: "Our Team Rust Project Controller".to_string(), + description: "A collaborative Rust project by our team".to_string(), + repository_url: Some("https://github.com/team/our-rust-project".to_string()), + demo_url: Some("https://our-team-project.com".to_string()), + slides_url: Some("https://docs.google.com/presentation/d/12345".to_string()), + technologies: vec![], + }; + + // Send team submission request (use teams path) + let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, team_id)) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&member1.id.id.to_raw()).await)) + .json(&team_submission_dto) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::CREATED); + let body = crate::get_response_body(response).await; + assert_eq!(body["message"], "Success submit team project"); + assert_eq!(body["data"]["project_name"], "Our Team Rust Project Controller"); + assert_eq!(body["data"]["status"], "Draft"); + assert_eq!(body["data"]["team_id"], team_id); + + // Clean up + let _ = hackathon_repo.delete_hackathon(hackathon_id).await; + let _ = teams_repo.query_delete_team(team_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_get_hackathon_submissions() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test users and hackathon + let organizer_email = generate_unique_email("hackathon_submissions_organizer_controller"); + let participant_email = generate_unique_email("hackathon_submissions_participant_controller"); + + let role_id = get_role_id("mentee", &app.state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Submissions Test Controller".to_string(), + description: "Test hackathon for retrieving submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + let hackathon_id = create_result.id.id.to_raw(); + + // Submit multiple projects + let submission_dtos = [ + HackathonSubmissionCreateRequestDto { + project_name: "Project 1 Controller".to_string(), + description: "Description 1".to_string(), + repository_url: Some("https://github.com/user/project1".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }, + HackathonSubmissionCreateRequestDto { + project_name: "Project 2 Controller".to_string(), + description: "Description 2".to_string(), + repository_url: Some("https://github.com/user/project2".to_string()), + demo_url: Some("https://project2.com".to_string()), + slides_url: None, + technologies: vec![], + } + ]; + + for submission_dto in submission_dtos.iter() { + let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) + .json(submission_dto) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + } + + // Get hackathon submissions + let response = app.service.get(format!("/api/v1/hackathons/{}/submissions", hackathon_id)) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["data"].as_array().unwrap().len(), 2); + assert_eq!(body["data"][0]["project_name"], "Project 1 Controller"); + assert_eq!(body["data"][1]["project_name"], "Project 2 Controller"); + assert_eq!(body["data"][0]["status"], "Draft"); + assert_eq!(body["data"][1]["status"], "Draft"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_get_user_hackathon_submissions() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test users and hackathons + let organizer_email = generate_unique_email("hackathon_user_submissions_organizer_controller"); + let participant_email = generate_unique_email("hackathon_user_submissions_participant_controller"); + + let role_id = get_role_id("mentee", &app.state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create multiple hackathons + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Hackathon 1 Controller".to_string(), + description: "First hackathon".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Hackathon 2 Controller".to_string(), + description: "Second hackathon".to_string(), + start_date: Utc::now() + Duration::days(14), + end_date: Utc::now() + Duration::days(21), + registration_deadline: Utc::now() + Duration::days(17), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + } + ]; + + let mut hackathon_ids = Vec::new(); + + for hackathon_request in hackathon_requests.iter() { + let create_result = repo.create_hackathon(hackathon_request.clone()).await.unwrap(); + let hackathon_id = create_result.id.id.to_raw(); + hackathon_ids.push(hackathon_id); + } + + // Submit projects to different hackathons using team endpoint (single-user uses user id as team_id) + let submission_requests = [ + HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 1 Controller".to_string(), + description: "Description for hackathon 1".to_string(), + repository_url: Some("https://github.com/user/hackathon1-project".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }, + HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 2 Controller".to_string(), + description: "Description for hackathon 2".to_string(), + repository_url: Some("https://github.com/user/hackathon2-project".to_string()), + demo_url: Some("https://hackathon2-project.com".to_string()), + slides_url: None, + technologies: vec![], + } + ]; + + for (i, create_req) in submission_requests.iter().enumerate() { + let response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_ids[i], participant.id.id.to_raw())) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) + .json(create_req) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + } + + // Get user's hackathon submissions + let response = app.service.get(format!("/api/v1/users/{}/hackathon-submissions", participant.id.id.to_raw())) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["data"].as_array().unwrap().len(), 2); + assert_eq!(body["data"][0]["project_name"], "Project for Hackathon 1 Controller"); + assert_eq!(body["data"][1]["project_name"], "Project for Hackathon 2 Controller"); + assert_eq!(body["data"][0]["status"], "Draft"); + assert_eq!(body["data"][1]["status"], "Draft"); + + // Clean up + for hackathon_id in hackathon_ids { + let _ = repo.delete_hackathon(hackathon_id).await; + } + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_search_hackathons() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test organizer and hackathons + let email = generate_unique_email("hackathon_search_controller"); + let role_id = get_role_id("mentor", &app.state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create test hackathons + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Rust Backend Hackathon Controller".to_string(), + description: "Build Rust backend projects".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: Some("Backend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "TypeScript Frontend Hackathon Controller".to_string(), + description: "Build TypeScript frontend projects".to_string(), + start_date: Utc::now() + Duration::days(14), + end_date: Utc::now() + Duration::days(21), + registration_deadline: Utc::now() + Duration::days(17), + max_participants: None, + theme: Some("Frontend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Rust Fullstack Hackathon Controller".to_string(), + description: "Build fullstack projects with Rust".to_string(), + start_date: Utc::now() + Duration::days(21), + end_date: Utc::now() + Duration::days(28), + registration_deadline: Utc::now() + Duration::days(24), + max_participants: None, + theme: Some("Fullstack".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + } + ]; + + for hackathon_request in hackathon_requests.iter() { + let create_result = repo.create_hackathon(hackathon_request.clone()).await.unwrap(); + // Store hackathon IDs for cleanup + let _ = create_result.id.id.to_raw(); + } + + // Test search with multiple parameters + let search_params = json!({ + "query": "Rust", + "category": "Backend", + "location": "Remote", + "is_featured": true, + "page": 1, + "per_page": 10 + }); + + let response = app.service.post("/api/v1/hackathons/search") + .json(&search_params) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["data"].as_array().unwrap().len(), 1); + assert_eq!(body["data"][0]["name"], "Rust Backend Hackathon Controller"); + assert!(body["data"][0]["description"].as_str().unwrap().contains("Rust")); + + // Clean up - in a real test you would store and delete all created hackathons + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_update_submission_status() { + let app = crate::get_full_test_app().await; + let users_repo = UsersRepository::new(&app.state); + let repo = HackathonRepository::new(&app.state); + + // Create test users and hackathon + let organizer_email = generate_unique_email("hackathon_status_organizer_controller"); + let participant_email = generate_unique_email("hackathon_status_participant_controller"); + + let role_id = get_role_id("mentee", &app.state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon and submission + let hackathon_request = HackathonCreateRequestDto { + name: "Submission Status Test Controller".to_string(), + description: "Test hackathon for submission status updates".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request).await.unwrap(); + let hackathon_id = create_result.id.id.to_raw(); + + let submission_dto = HackathonSubmissionCreateRequestDto { + project_name: "Test Project Controller".to_string(), + description: "Test description".to_string(), + repository_url: Some("https://github.com/user/test-project".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }; + + let submit_response = app.service.post(format!("/api/v1/hackathons/{}/teams/{}/submissions", hackathon_id, participant.id.id.to_raw())) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&participant.id.id.to_raw()).await)) + .json(&submission_dto) + .await + .unwrap(); + assert_eq!(submit_response.status(), StatusCode::CREATED); + + let submission_id = crate::get_response_body(submit_response).await["data"]["id"] + .as_str() + .unwrap() + .to_string(); + + // Update submission status to "Accepted" + let update_status = json!({ + "status": "Accepted", + "feedback": "Great project!" + }); + + let response = app.service.patch(format!("/api/v1/hackathons/submissions/{}/status", submission_id)) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&organizer.id.id.to_raw()).await)) + .json(&update_status) + .await + .unwrap(); + + // Verify response + assert_eq!(response.status(), StatusCode::OK); + let body = crate::get_response_body(response).await; + assert_eq!(body["message"], "Success update submission status"); + assert_eq!(body["data"]["status"], "Accepted"); + assert_eq!(body["data"]["judge_feedback"], "Great project!"); + + // Update status again to "Rejected" + let update_status2 = json!({ + "status": "Rejected", + "feedback": "Does not meet criteria" + }); + + let response2 = app.service.patch(format!("/api/v1/hackathons/submissions/{}/status", submission_id)) + .header("Authorization", format!("Bearer {}", crate::get_test_token(&organizer.id.id.to_raw()).await)) + .json(&update_status2) + .await + .unwrap(); + + // Verify second update + assert_eq!(response2.status(), StatusCode::OK); + let body2 = crate::get_response_body(response2).await; + assert_eq!(body2["message"], "Success update submission status"); + assert_eq!(body2["data"]["status"], "Rejected"); + assert_eq!(body2["data"]["judge_feedback"], "Does not meet criteria"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } } \ No newline at end of file diff --git a/tests/src/hackathon/hackathon_repository_test.rs b/tests/src/hackathon/hackathon_repository_test.rs index 8d4955f..6474256 100644 --- a/tests/src/hackathon/hackathon_repository_test.rs +++ b/tests/src/hackathon/hackathon_repository_test.rs @@ -1,721 +1,721 @@ -#[cfg(test)] -mod tests { - use crate::{generate_unique_email, get_role_id, UsersRepository}; - use chrono::{Duration, Utc, Days}; - use crate::ResourceEnum; - use imphnen_hackathon::v1::hackathon::{ - HackathonCreateRequestDto, HackathonUpdateRequestDto, - HackathonSubmissionCreateRequestDto, - HackathonRepository, - }; - use imphnen_hackathon::v1::hackathon::SubmissionStatus; - use imphnen_hackathon::v1::hackathon::hackathon_schema::{ - SubmissionStatus as HackathonSubmissionStatus, - }; - use imphnen_iam::v1::teams::{TeamsCreateRequestDto, TeamsRepository, TeamMembersSchema, TeamsSchema}; - use imphnen_utils::make_thing_from_enum; - - - // Use the existing test helpers from the crate root - - #[tokio::test] - async fn test_create_and_get_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test organizer - let email = generate_unique_email("hackathon_organizer"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Rust Hackathon 2025".to_string(), - description: "Build amazing Rust projects!".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: Some("Backend".to_string()), - rules: Some("Must use Rust; Open source only".to_string()), - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - - // Get hackathon by ID - let result = repo.get_hackathon_by_id(created.id.id.to_raw()).await; - assert!(result.is_ok(), "Failed to get hackathon by ID"); - let retrieved_hackathon = result.unwrap(); - - // Validate hackathon data - assert_eq!(retrieved_hackathon.name, hackathon_request.name); - assert_eq!(retrieved_hackathon.description, hackathon_request.description); - assert_eq!(retrieved_hackathon.start_date, hackathon_request.start_date); - assert_eq!(retrieved_hackathon.end_date, hackathon_request.end_date); - assert_eq!(retrieved_hackathon.registration_deadline, hackathon_request.registration_deadline); - assert_eq!(retrieved_hackathon.theme, hackathon_request.theme); - assert_eq!(retrieved_hackathon.rules, hackathon_request.rules); - assert!(!retrieved_hackathon.is_deleted); - - // Clean up - let _ = repo.delete_hackathon(created.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_update_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test organizer - let email = generate_unique_email("hackathon_updater"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Original Hackathon".to_string(), - description: "Original description".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - - // Update hackathon using Update DTO - let updates = HackathonUpdateRequestDto { - name: Some("Updated Hackathon Title".to_string()), - description: Some("Updated description with more details".to_string()), - start_date: Some(Utc::now() + Duration::days(8)), - end_date: Some(Utc::now() + Duration::days(15)), - registration_deadline: Some(Utc::now() + Duration::days(11)), - max_participants: None, - theme: Some("Fullstack".to_string()), - rules: Some("Must use Rust or TypeScript; Open source required; Presentation required".to_string()), - prizes: None, - previous_winners: None, - organizers: Some(vec![user.id.id.to_raw()]), - }; - - let updated = repo.update_hackathon(created.id.id.to_raw(), updates).await.expect("Failed to update hackathon"); - - // Verify update - let retrieved_hackathon = repo.get_hackathon_by_id(updated.id.id.to_raw()).await.expect("Failed to get updated hackathon"); - - assert_eq!(retrieved_hackathon.name, "Updated Hackathon Title"); - assert_eq!(retrieved_hackathon.description, "Updated description with more details"); - assert_eq!(retrieved_hackathon.theme.unwrap(), "Fullstack"); - - // Clean up - let _ = repo.delete_hackathon(created.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_submit_project_as_user() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer"); - let participant_email = generate_unique_email("hackathon_participant"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "User Submission Test Hackathon".to_string(), - description: "Test hackathon for user submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - - // For user submissions create a team for the participant so we can provide a team_id - let teams_repo = TeamsRepository::new(&app_state); - let team_request = TeamsCreateRequestDto { - name: "User Individual Team".to_string(), - description: Some("Auto team for individual user".to_string()), - is_open: Some(false), - max_members: Some(1), - skills_required: None, - location: None, - website_url: None, - github_url: None, - avatar: None, - member_emails: vec![], - }; - let team_schema = imphnen_iam::v1::teams::TeamsSchema::create(team_request, participant.id.id.to_raw()); - let _ = teams_repo.query_create_team(team_schema.clone()).await.expect("Failed to create team"); - - // Submit project as user (using the team id) - let submission_req = HackathonSubmissionCreateRequestDto { - project_name: "My Rust Project".to_string(), - description: "A cool Rust project for the hackathon".to_string(), - repository_url: Some("https://github.com/user/my-rust-project".to_string()), - demo_url: Some("https://my-rust-project.com".to_string()), - slides_url: None, - technologies: vec![], - }; - - let submission = repo.create_hackathon_submission( - created.id.id.to_raw(), - team_schema.id.id.to_raw(), - submission_req, - ).await.expect("Failed to submit project"); - - // Verify submission - let retrieved_submission = repo.get_hackathon_submission_by_id(submission.id.id.to_raw()).await.expect("Failed to get submission by ID"); - - assert_eq!(retrieved_submission.hackathon_id.id.to_raw(), created.id.id.to_raw()); - assert_eq!(retrieved_submission.team_id.as_ref().unwrap().id.to_raw(), team_schema.id.id.to_raw()); - assert_eq!(retrieved_submission.project_name, Some("My Rust Project".to_string())); - assert_eq!(retrieved_submission.description, Some("A cool Rust project for the hackathon".to_string())); - assert_eq!(retrieved_submission.repository_url, Some("https://github.com/user/my-rust-project".to_string())); - assert_eq!(retrieved_submission.demo_url, Some("https://my-rust-project.com".to_string())); - assert_eq!(retrieved_submission.submission_status, Some(HackathonSubmissionStatus::Draft)); - - // Clean up - let _ = repo.delete_hackathon(created.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_submit_project_as_team() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let teams_repo = TeamsRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer"); - let member1_email = generate_unique_email("team_member1"); - let member2_email = generate_unique_email("team_member2"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); - let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let member1_result = users_repo.query_create_user(member1_data.clone()).await; - let member2_result = users_repo.query_create_user(member2_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(member1_result.is_ok(), "Failed to create member1"); - assert!(member2_result.is_ok(), "Failed to create member2"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); - let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); - - // Create team - let team_request = TeamsCreateRequestDto { - name: "Hackathon Team".to_string(), - description: Some("Team for hackathon submissions".to_string()), - is_open: Some(false), - max_members: Some(5), - skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), - location: Some("Remote".to_string()), - website_url: None, - github_url: None, - avatar: None, - member_emails: vec![], - }; - - let team_schema = TeamsSchema::create(team_request, member1.id.id.to_raw()); - let team_create_result = teams_repo.query_create_team(team_schema.clone()).await; - assert!(team_create_result.is_ok(), "Failed to create team"); - - // Add team members - let member2_schema = TeamMembersSchema::create( - team_schema.id.id.to_raw(), - member2.id.id.to_raw(), - Some("member".to_string()) - ); - let add_member_result = teams_repo.query_add_team_member(member2_schema).await; - assert!(add_member_result.is_ok(), "Failed to add team member"); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Team Submission Test Hackathon".to_string(), - description: "Test hackathon for team submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await; - assert!(create_result.is_ok(), "Failed to create hackathon"); - let hackathon_schema = create_result.unwrap(); - - // Submit project as team - let create_submission = HackathonSubmissionCreateRequestDto { - project_name: "Our Team Rust Project".to_string(), - description: "A collaborative Rust project by our team".to_string(), - repository_url: Some("https://github.com/team/our-rust-project".to_string()), - demo_url: Some("https://our-team-project.com".to_string()), - slides_url: Some("https://docs.google.com/presentation/d/12345".to_string()), - technologies: vec![], - }; - - let submit_result = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), team_schema.id.id.to_raw(), create_submission.clone()).await; - assert!(submit_result.is_ok(), "Failed to submit team project"); - let _submitted = submit_result.unwrap(); - - // Verify submission - let submission_id = _submitted.id.id.to_raw(); - let result = repo.get_hackathon_submission_by_id(submission_id.clone()).await; - assert!(result.is_ok(), "Failed to get team submission by ID"); - let retrieved_submission = result.unwrap(); - - assert_eq!(retrieved_submission.project_name, Some(create_submission.project_name.clone())); - // second comparison should clone to avoid moved value - assert_eq!(retrieved_submission.project_name, Some(create_submission.project_name.clone())); - assert_eq!(retrieved_submission.description, Some(create_submission.description.clone())); - assert_eq!(retrieved_submission.repository_url, create_submission.repository_url); - assert_eq!(retrieved_submission.demo_url, create_submission.demo_url); - assert_eq!(retrieved_submission.slides_url, create_submission.slides_url); - assert_eq!(retrieved_submission.submission_status, Some(HackathonSubmissionStatus::Draft)); - - // Clean up - let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; - let _ = teams_repo.query_delete_team(team_schema.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_get_hackathon_submissions() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer"); - let participant_email = generate_unique_email("hackathon_participant"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Submissions Test Hackathon".to_string(), - description: "Test hackathon for retrieving submissions".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request.clone()).await; - assert!(create_result.is_ok(), "Failed to create hackathon"); - let hackathon_schema = create_result.unwrap(); - - let _hackathon_thing = make_thing_from_enum(ResourceEnum::Hackathons, &hackathon_schema.id.id.to_raw()); - - // Submit multiple projects using create requests - let create_requests = [ - ("".to_string(), HackathonSubmissionCreateRequestDto { - project_name: "Project 1".to_string(), - description: "Description 1".to_string(), - repository_url: Some("https://github.com/user/project1".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }), - ("".to_string(), HackathonSubmissionCreateRequestDto { - project_name: "Project 2".to_string(), - description: "Description 2".to_string(), - repository_url: Some("https://github.com/user/project2".to_string()), - demo_url: Some("https://project2.com".to_string()), - slides_url: None, - technologies: vec![], - }), - ]; - - for (team_id, create_req) in create_requests.iter() { - let _ = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), team_id.clone(), create_req.clone()).await.expect("Failed to submit project"); - } - - // Get hackathon submissions - - let submissions_result = repo.list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_schema.id.id.to_raw()).await; - assert!(submissions_result.is_ok(), "Failed to get hackathon submissions"); - let submissions = submissions_result.unwrap().data; - - assert_eq!(submissions.len(), 2, "Should have 2 submissions"); - assert_eq!(submissions[0].project_name, Some("Project 1".to_string())); - assert_eq!(submissions[1].project_name, Some("Project 2".to_string())); - assert_eq!(submissions[0].submission_status, Some(HackathonSubmissionStatus::Draft)); - assert_eq!(submissions[1].submission_status, Some(HackathonSubmissionStatus::Draft)); - - // Clean up - let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_get_user_hackathon_submissions() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer"); - let participant_email = generate_unique_email("hackathon_participant"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create multiple hackathons - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Hackathon 1".to_string(), - description: "First hackathon".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Hackathon 2".to_string(), - description: "Second hackathon".to_string(), - start_date: Utc::now() + Duration::days(14), - end_date: Utc::now() + Duration::days(21), - registration_deadline: Utc::now() + Duration::days(17), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - } - ]; - - let mut hackathon_ids = Vec::new(); - - for hackathon_request in hackathon_requests.iter() { - let create_result = repo.create_hackathon(hackathon_request.clone()).await; - assert!(create_result.is_ok(), "Failed to create hackathon"); - let hackathon_schema = create_result.unwrap(); - hackathon_ids.push(hackathon_schema.id.id.to_raw()); - } - - // Submit projects to different hackathons - // Submit projects using the current CreateRequest DTO - let create_reqs = vec![ - (hackathon_ids[0].clone(), HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 1".to_string(), - description: "Description for hackathon 1".to_string(), - repository_url: Some("https://github.com/user/hackathon1-project".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }), - (hackathon_ids[1].clone(), HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 2".to_string(), - description: "Description for hackathon 2".to_string(), - repository_url: Some("https://github.com/user/hackathon2-project".to_string()), - demo_url: Some("https://hackathon2-project.com".to_string()), - slides_url: None, - technologies: vec![], - }), - ]; - - for (hackathon_id, create_req) in create_reqs.into_iter() { - let submit_result = repo.create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), create_req).await; - assert!(submit_result.is_ok(), "Failed to submit project"); - } - - // Get user's hackathon submissions - // Use list_submissions_by_team to fetch submissions for the participant across hackathons - let submissions_result = repo.list_submissions_by_team(imphnen_libs::MetaRequestDto::default(), participant.id.id.to_raw()).await; - assert!(submissions_result.is_ok(), "Failed to get user's hackathon submissions"); - let submissions_list = submissions_result.unwrap().data; - - assert_eq!(submissions_list.len(), 2, "Should have 2 submissions"); - assert_eq!(submissions_list[0].project_name, Some("Project for Hackathon 1".to_string())); - assert_eq!(submissions_list[1].project_name, Some("Project for Hackathon 2".to_string())); - assert_eq!(submissions_list[0].submission_status, Some(SubmissionStatus::Draft)); - assert_eq!(submissions_list[1].submission_status, Some(SubmissionStatus::Draft)); - - // Clean up - for hackathon_id in hackathon_ids { - let _ = repo.delete_hackathon(hackathon_id).await; - } - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_search_hackathons() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test organizer - let email = generate_unique_email("hackathon_search_organizer"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create test hackathons - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Rust Backend Hackathon".to_string(), - description: "Build Rust backend projects".to_string(), - start_date: Utc::now().checked_add_days(Days::new(7)).unwrap(), - end_date: Utc::now().checked_add_days(Days::new(14)).unwrap(), - registration_deadline: Utc::now().checked_add_days(Days::new(10)).unwrap(), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "TypeScript Frontend Hackathon".to_string(), - description: "Build TypeScript frontend projects".to_string(), - start_date: Utc::now().checked_add_days(Days::new(14)).unwrap(), - end_date: Utc::now().checked_add_days(Days::new(21)).unwrap(), - registration_deadline: Utc::now().checked_add_days(Days::new(17)).unwrap(), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Rust Fullstack Hackathon".to_string(), - description: "Build fullstack projects with Rust".to_string(), - start_date: Utc::now() + Duration::days(21), - end_date: Utc::now() + Duration::days(28), - registration_deadline: Utc::now() + Duration::days(24), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - } - ]; - - let mut hackathon_ids = Vec::new(); - - for hackathon_request in hackathon_requests.iter() { - let create_result = repo.create_hackathon(hackathon_request.clone()).await; - assert!(create_result.is_ok(), "Failed to create hackathon"); - let hackathon_schema = create_result.unwrap(); - hackathon_ids.push(hackathon_schema.id.id.to_raw()); - } - - // Test search with multiple parameters - // Search functionality is covered by list_hackathons + QueryListBuilder; skip exact search test here - - // Clean up - for hackathon_id in hackathon_ids { - let _ = repo.delete_hackathon(hackathon_id).await; - } - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_update_submission_status() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer"); - let participant_email = generate_unique_email("hackathon_participant"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Submission Status Test Hackathon".to_string(), - description: "Test hackathon for submission status updates".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = repo.create_hackathon(hackathon_request).await; - assert!(create_result.is_ok(), "Failed to create hackathon"); - let hackathon_schema = create_result.unwrap(); - - // Submit project - let create_req = HackathonSubmissionCreateRequestDto { - project_name: "Test Project".to_string(), - description: "Test description".to_string(), - repository_url: Some("https://github.com/user/test-project".to_string()), - demo_url: None, - slides_url: None, - technologies: vec![], - }; - let submit_result = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), String::new(), create_req).await; - assert!(submit_result.is_ok(), "Failed to submit project"); - - // Get submission ID from result - let created_submission = submit_result.unwrap(); - // created_submission is a HackathonSubmissionsSchema; get id - let submission_id = created_submission.id.id.to_raw(); - - // Submit the submission (mark as Submitted) using repository API and verify - let _submitted_schema = repo.submit_hackathon_submission(submission_id.clone()).await.expect("Failed to submit hackathon submission"); - let updated_submission = repo.get_hackathon_submission_by_id(submission_id.clone()).await.expect("Failed to get updated submission"); - assert_eq!(updated_submission.submission_status, Some(HackathonSubmissionStatus::Submitted)); - assert!(updated_submission.updated_at > updated_submission.created_at); - - // Clean up - let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_delete_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test organizer - let email = generate_unique_email("hackathon_deleter"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - // Create hackathon - let hackathon_request = HackathonCreateRequestDto { - name: "Hackathon to Delete".to_string(), - description: "Hackathon that will be deleted".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); - - // Verify hackathon exists before deletion - let exists_before = repo.get_hackathon_by_id(created.id.id.to_raw()).await.is_ok(); - assert!(exists_before, "Hackathon should exist before deletion"); - - // Delete hackathon - let delete_result = repo.delete_hackathon(created.id.id.to_raw()).await; - assert!(delete_result.is_ok(), "Failed to delete hackathon"); - assert_eq!(delete_result.unwrap(), "Hackathon deleted successfully"); - - // Verify hackathon is deleted - let exists_after = repo.get_hackathon_by_id(created.id.id.to_raw()).await.is_ok(); - assert!(!exists_after, "Hackathon should not exist after deletion"); - - // Clean up - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } +#[cfg(test)] +mod tests { + use crate::{generate_unique_email, get_role_id, UsersRepository}; + use chrono::{Duration, Utc, Days}; + use crate::ResourceEnum; + use imphnen_hackathon::v1::hackathon::{ + HackathonCreateRequestDto, HackathonUpdateRequestDto, + HackathonSubmissionCreateRequestDto, + HackathonRepository, + }; + use imphnen_hackathon::v1::hackathon::SubmissionStatus; + use imphnen_hackathon::v1::hackathon::hackathon_schema::{ + SubmissionStatus as HackathonSubmissionStatus, + }; + use imphnen_iam::v1::teams::{TeamsCreateRequestDto, TeamsRepository, TeamMembersSchema, TeamsSchema}; + use imphnen_utils::make_thing_from_enum; + + + // Use the existing test helpers from the crate root + + #[tokio::test] + async fn test_create_and_get_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test organizer + let email = generate_unique_email("hackathon_organizer"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Rust Hackathon 2025".to_string(), + description: "Build amazing Rust projects!".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: Some("Backend".to_string()), + rules: Some("Must use Rust; Open source only".to_string()), + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + + // Get hackathon by ID + let result = repo.get_hackathon_by_id(created.id.id.to_raw()).await; + assert!(result.is_ok(), "Failed to get hackathon by ID"); + let retrieved_hackathon = result.unwrap(); + + // Validate hackathon data + assert_eq!(retrieved_hackathon.name, hackathon_request.name); + assert_eq!(retrieved_hackathon.description, hackathon_request.description); + assert_eq!(retrieved_hackathon.start_date, hackathon_request.start_date); + assert_eq!(retrieved_hackathon.end_date, hackathon_request.end_date); + assert_eq!(retrieved_hackathon.registration_deadline, hackathon_request.registration_deadline); + assert_eq!(retrieved_hackathon.theme, hackathon_request.theme); + assert_eq!(retrieved_hackathon.rules, hackathon_request.rules); + assert!(!retrieved_hackathon.is_deleted); + + // Clean up + let _ = repo.delete_hackathon(created.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_update_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test organizer + let email = generate_unique_email("hackathon_updater"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Original Hackathon".to_string(), + description: "Original description".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + + // Update hackathon using Update DTO + let updates = HackathonUpdateRequestDto { + name: Some("Updated Hackathon Title".to_string()), + description: Some("Updated description with more details".to_string()), + start_date: Some(Utc::now() + Duration::days(8)), + end_date: Some(Utc::now() + Duration::days(15)), + registration_deadline: Some(Utc::now() + Duration::days(11)), + max_participants: None, + theme: Some("Fullstack".to_string()), + rules: Some("Must use Rust or TypeScript; Open source required; Presentation required".to_string()), + prizes: None, + previous_winners: None, + organizers: Some(vec![user.id.id.to_raw()]), + }; + + let updated = repo.update_hackathon(created.id.id.to_raw(), updates).await.expect("Failed to update hackathon"); + + // Verify update + let retrieved_hackathon = repo.get_hackathon_by_id(updated.id.id.to_raw()).await.expect("Failed to get updated hackathon"); + + assert_eq!(retrieved_hackathon.name, "Updated Hackathon Title"); + assert_eq!(retrieved_hackathon.description, "Updated description with more details"); + assert_eq!(retrieved_hackathon.theme.unwrap(), "Fullstack"); + + // Clean up + let _ = repo.delete_hackathon(created.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_submit_project_as_user() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer"); + let participant_email = generate_unique_email("hackathon_participant"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "User Submission Test Hackathon".to_string(), + description: "Test hackathon for user submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + + // For user submissions create a team for the participant so we can provide a team_id + let teams_repo = TeamsRepository::new(&app_state); + let team_request = TeamsCreateRequestDto { + name: "User Individual Team".to_string(), + description: Some("Auto team for individual user".to_string()), + is_open: Some(false), + max_members: Some(1), + skills_required: None, + location: None, + website_url: None, + github_url: None, + avatar: None, + member_emails: vec![], + }; + let team_schema = imphnen_iam::v1::teams::TeamsSchema::create(team_request, participant.id.id.to_raw()); + let _ = teams_repo.query_create_team(team_schema.clone()).await.expect("Failed to create team"); + + // Submit project as user (using the team id) + let submission_req = HackathonSubmissionCreateRequestDto { + project_name: "My Rust Project".to_string(), + description: "A cool Rust project for the hackathon".to_string(), + repository_url: Some("https://github.com/user/my-rust-project".to_string()), + demo_url: Some("https://my-rust-project.com".to_string()), + slides_url: None, + technologies: vec![], + }; + + let submission = repo.create_hackathon_submission( + created.id.id.to_raw(), + team_schema.id.id.to_raw(), + submission_req, + ).await.expect("Failed to submit project"); + + // Verify submission + let retrieved_submission = repo.get_hackathon_submission_by_id(submission.id.id.to_raw()).await.expect("Failed to get submission by ID"); + + assert_eq!(retrieved_submission.hackathon_id.id.to_raw(), created.id.id.to_raw()); + assert_eq!(retrieved_submission.team_id.as_ref().unwrap().id.to_raw(), team_schema.id.id.to_raw()); + assert_eq!(retrieved_submission.project_name, Some("My Rust Project".to_string())); + assert_eq!(retrieved_submission.description, Some("A cool Rust project for the hackathon".to_string())); + assert_eq!(retrieved_submission.repository_url, Some("https://github.com/user/my-rust-project".to_string())); + assert_eq!(retrieved_submission.demo_url, Some("https://my-rust-project.com".to_string())); + assert_eq!(retrieved_submission.submission_status, Some(HackathonSubmissionStatus::Draft)); + + // Clean up + let _ = repo.delete_hackathon(created.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_submit_project_as_team() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let teams_repo = TeamsRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer"); + let member1_email = generate_unique_email("team_member1"); + let member2_email = generate_unique_email("team_member2"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); + let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let member1_result = users_repo.query_create_user(member1_data.clone()).await; + let member2_result = users_repo.query_create_user(member2_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(member1_result.is_ok(), "Failed to create member1"); + assert!(member2_result.is_ok(), "Failed to create member2"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); + let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); + + // Create team + let team_request = TeamsCreateRequestDto { + name: "Hackathon Team".to_string(), + description: Some("Team for hackathon submissions".to_string()), + is_open: Some(false), + max_members: Some(5), + skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), + location: Some("Remote".to_string()), + website_url: None, + github_url: None, + avatar: None, + member_emails: vec![], + }; + + let team_schema = TeamsSchema::create(team_request, member1.id.id.to_raw()); + let team_create_result = teams_repo.query_create_team(team_schema.clone()).await; + assert!(team_create_result.is_ok(), "Failed to create team"); + + // Add team members + let member2_schema = TeamMembersSchema::create( + team_schema.id.id.to_raw(), + member2.id.id.to_raw(), + Some("member".to_string()) + ); + let add_member_result = teams_repo.query_add_team_member(member2_schema).await; + assert!(add_member_result.is_ok(), "Failed to add team member"); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Team Submission Test Hackathon".to_string(), + description: "Test hackathon for team submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await; + assert!(create_result.is_ok(), "Failed to create hackathon"); + let hackathon_schema = create_result.unwrap(); + + // Submit project as team + let create_submission = HackathonSubmissionCreateRequestDto { + project_name: "Our Team Rust Project".to_string(), + description: "A collaborative Rust project by our team".to_string(), + repository_url: Some("https://github.com/team/our-rust-project".to_string()), + demo_url: Some("https://our-team-project.com".to_string()), + slides_url: Some("https://docs.google.com/presentation/d/12345".to_string()), + technologies: vec![], + }; + + let submit_result = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), team_schema.id.id.to_raw(), create_submission.clone()).await; + assert!(submit_result.is_ok(), "Failed to submit team project"); + let _submitted = submit_result.unwrap(); + + // Verify submission + let submission_id = _submitted.id.id.to_raw(); + let result = repo.get_hackathon_submission_by_id(submission_id.clone()).await; + assert!(result.is_ok(), "Failed to get team submission by ID"); + let retrieved_submission = result.unwrap(); + + assert_eq!(retrieved_submission.project_name, Some(create_submission.project_name.clone())); + // second comparison should clone to avoid moved value + assert_eq!(retrieved_submission.project_name, Some(create_submission.project_name.clone())); + assert_eq!(retrieved_submission.description, Some(create_submission.description.clone())); + assert_eq!(retrieved_submission.repository_url, create_submission.repository_url); + assert_eq!(retrieved_submission.demo_url, create_submission.demo_url); + assert_eq!(retrieved_submission.slides_url, create_submission.slides_url); + assert_eq!(retrieved_submission.submission_status, Some(HackathonSubmissionStatus::Draft)); + + // Clean up + let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; + let _ = teams_repo.query_delete_team(team_schema.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_get_hackathon_submissions() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer"); + let participant_email = generate_unique_email("hackathon_participant"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Submissions Test Hackathon".to_string(), + description: "Test hackathon for retrieving submissions".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request.clone()).await; + assert!(create_result.is_ok(), "Failed to create hackathon"); + let hackathon_schema = create_result.unwrap(); + + let _hackathon_thing = make_thing_from_enum(ResourceEnum::Hackathons, &hackathon_schema.id.id.to_raw()); + + // Submit multiple projects using create requests + let create_requests = [ + ("".to_string(), HackathonSubmissionCreateRequestDto { + project_name: "Project 1".to_string(), + description: "Description 1".to_string(), + repository_url: Some("https://github.com/user/project1".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }), + ("".to_string(), HackathonSubmissionCreateRequestDto { + project_name: "Project 2".to_string(), + description: "Description 2".to_string(), + repository_url: Some("https://github.com/user/project2".to_string()), + demo_url: Some("https://project2.com".to_string()), + slides_url: None, + technologies: vec![], + }), + ]; + + for (team_id, create_req) in create_requests.iter() { + let _ = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), team_id.clone(), create_req.clone()).await.expect("Failed to submit project"); + } + + // Get hackathon submissions + + let submissions_result = repo.list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_schema.id.id.to_raw()).await; + assert!(submissions_result.is_ok(), "Failed to get hackathon submissions"); + let submissions = submissions_result.unwrap().data; + + assert_eq!(submissions.len(), 2, "Should have 2 submissions"); + assert_eq!(submissions[0].project_name, Some("Project 1".to_string())); + assert_eq!(submissions[1].project_name, Some("Project 2".to_string())); + assert_eq!(submissions[0].submission_status, Some(HackathonSubmissionStatus::Draft)); + assert_eq!(submissions[1].submission_status, Some(HackathonSubmissionStatus::Draft)); + + // Clean up + let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_get_user_hackathon_submissions() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer"); + let participant_email = generate_unique_email("hackathon_participant"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create multiple hackathons + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Hackathon 1".to_string(), + description: "First hackathon".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Hackathon 2".to_string(), + description: "Second hackathon".to_string(), + start_date: Utc::now() + Duration::days(14), + end_date: Utc::now() + Duration::days(21), + registration_deadline: Utc::now() + Duration::days(17), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + } + ]; + + let mut hackathon_ids = Vec::new(); + + for hackathon_request in hackathon_requests.iter() { + let create_result = repo.create_hackathon(hackathon_request.clone()).await; + assert!(create_result.is_ok(), "Failed to create hackathon"); + let hackathon_schema = create_result.unwrap(); + hackathon_ids.push(hackathon_schema.id.id.to_raw()); + } + + // Submit projects to different hackathons + // Submit projects using the current CreateRequest DTO + let create_reqs = vec![ + (hackathon_ids[0].clone(), HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 1".to_string(), + description: "Description for hackathon 1".to_string(), + repository_url: Some("https://github.com/user/hackathon1-project".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }), + (hackathon_ids[1].clone(), HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 2".to_string(), + description: "Description for hackathon 2".to_string(), + repository_url: Some("https://github.com/user/hackathon2-project".to_string()), + demo_url: Some("https://hackathon2-project.com".to_string()), + slides_url: None, + technologies: vec![], + }), + ]; + + for (hackathon_id, create_req) in create_reqs.into_iter() { + let submit_result = repo.create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), create_req).await; + assert!(submit_result.is_ok(), "Failed to submit project"); + } + + // Get user's hackathon submissions + // Use list_submissions_by_team to fetch submissions for the participant across hackathons + let submissions_result = repo.list_submissions_by_team(imphnen_libs::MetaRequestDto::default(), participant.id.id.to_raw()).await; + assert!(submissions_result.is_ok(), "Failed to get user's hackathon submissions"); + let submissions_list = submissions_result.unwrap().data; + + assert_eq!(submissions_list.len(), 2, "Should have 2 submissions"); + assert_eq!(submissions_list[0].project_name, Some("Project for Hackathon 1".to_string())); + assert_eq!(submissions_list[1].project_name, Some("Project for Hackathon 2".to_string())); + assert_eq!(submissions_list[0].submission_status, Some(SubmissionStatus::Draft)); + assert_eq!(submissions_list[1].submission_status, Some(SubmissionStatus::Draft)); + + // Clean up + for hackathon_id in hackathon_ids { + let _ = repo.delete_hackathon(hackathon_id).await; + } + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_search_hackathons() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test organizer + let email = generate_unique_email("hackathon_search_organizer"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create test hackathons + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Rust Backend Hackathon".to_string(), + description: "Build Rust backend projects".to_string(), + start_date: Utc::now().checked_add_days(Days::new(7)).unwrap(), + end_date: Utc::now().checked_add_days(Days::new(14)).unwrap(), + registration_deadline: Utc::now().checked_add_days(Days::new(10)).unwrap(), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "TypeScript Frontend Hackathon".to_string(), + description: "Build TypeScript frontend projects".to_string(), + start_date: Utc::now().checked_add_days(Days::new(14)).unwrap(), + end_date: Utc::now().checked_add_days(Days::new(21)).unwrap(), + registration_deadline: Utc::now().checked_add_days(Days::new(17)).unwrap(), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Rust Fullstack Hackathon".to_string(), + description: "Build fullstack projects with Rust".to_string(), + start_date: Utc::now() + Duration::days(21), + end_date: Utc::now() + Duration::days(28), + registration_deadline: Utc::now() + Duration::days(24), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + } + ]; + + let mut hackathon_ids = Vec::new(); + + for hackathon_request in hackathon_requests.iter() { + let create_result = repo.create_hackathon(hackathon_request.clone()).await; + assert!(create_result.is_ok(), "Failed to create hackathon"); + let hackathon_schema = create_result.unwrap(); + hackathon_ids.push(hackathon_schema.id.id.to_raw()); + } + + // Test search with multiple parameters + // Search functionality is covered by list_hackathons + QueryListBuilder; skip exact search test here + + // Clean up + for hackathon_id in hackathon_ids { + let _ = repo.delete_hackathon(hackathon_id).await; + } + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_update_submission_status() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer"); + let participant_email = generate_unique_email("hackathon_participant"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Submission Status Test Hackathon".to_string(), + description: "Test hackathon for submission status updates".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = repo.create_hackathon(hackathon_request).await; + assert!(create_result.is_ok(), "Failed to create hackathon"); + let hackathon_schema = create_result.unwrap(); + + // Submit project + let create_req = HackathonSubmissionCreateRequestDto { + project_name: "Test Project".to_string(), + description: "Test description".to_string(), + repository_url: Some("https://github.com/user/test-project".to_string()), + demo_url: None, + slides_url: None, + technologies: vec![], + }; + let submit_result = repo.create_hackathon_submission(hackathon_schema.id.id.to_raw(), String::new(), create_req).await; + assert!(submit_result.is_ok(), "Failed to submit project"); + + // Get submission ID from result + let created_submission = submit_result.unwrap(); + // created_submission is a HackathonSubmissionsSchema; get id + let submission_id = created_submission.id.id.to_raw(); + + // Submit the submission (mark as Submitted) using repository API and verify + let _submitted_schema = repo.submit_hackathon_submission(submission_id.clone()).await.expect("Failed to submit hackathon submission"); + let updated_submission = repo.get_hackathon_submission_by_id(submission_id.clone()).await.expect("Failed to get updated submission"); + assert_eq!(updated_submission.submission_status, Some(HackathonSubmissionStatus::Submitted)); + assert!(updated_submission.updated_at > updated_submission.created_at); + + // Clean up + let _ = repo.delete_hackathon(hackathon_schema.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_delete_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test organizer + let email = generate_unique_email("hackathon_deleter"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + // Create hackathon + let hackathon_request = HackathonCreateRequestDto { + name: "Hackathon to Delete".to_string(), + description: "Hackathon that will be deleted".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let created = repo.create_hackathon(hackathon_request.clone()).await.expect("Failed to create hackathon"); + + // Verify hackathon exists before deletion + let exists_before = repo.get_hackathon_by_id(created.id.id.to_raw()).await.is_ok(); + assert!(exists_before, "Hackathon should exist before deletion"); + + // Delete hackathon + let delete_result = repo.delete_hackathon(created.id.id.to_raw()).await; + assert!(delete_result.is_ok(), "Failed to delete hackathon"); + assert_eq!(delete_result.unwrap(), "Hackathon deleted successfully"); + + // Verify hackathon is deleted + let exists_after = repo.get_hackathon_by_id(created.id.id.to_raw()).await.is_ok(); + assert!(!exists_after, "Hackathon should not exist after deletion"); + + // Clean up + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } } \ No newline at end of file diff --git a/tests/src/hackathon/hackathon_service_test.rs b/tests/src/hackathon/hackathon_service_test.rs index 4bc734b..baf86d2 100644 --- a/tests/src/hackathon/hackathon_service_test.rs +++ b/tests/src/hackathon/hackathon_service_test.rs @@ -1,737 +1,737 @@ -#[cfg(test)] -mod tests { - use crate::{generate_unique_email, get_role_id, UsersRepository}; - use chrono::{Duration, Utc}; - use imphnen_hackathon::v1::hackathon::hackathon_dto::{ - HackathonCreateRequestDto, HackathonSubmissionCreateRequestDto - }; - use imphnen_hackathon::v1::hackathon::hackathon_repository::{ - HackathonRepository - }; - use imphnen_hackathon::v1::hackathon::hackathon_service::{HackathonService, HackathonServiceTrait}; - use imphnen_hackathon::v1::hackathon::hackathon_schema::SubmissionStatus; - use imphnen_iam::v1::teams::{TeamsCreateRequestDto, TeamsRepository}; - use imphnen_utils::{make_thing_from_enum, ResourceEnum}; - - - - #[tokio::test] - async fn test_service_create_and_get_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - - // Create test organizer - let email = generate_unique_email("hackathon_organizer_service"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Test Hackathon Service".to_string(), - description: "Hackathon created via service test".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - // Get hackathon by ID via service - let result = HackathonService::get_hackathon(created.data.id.clone(), &app_state).await; - assert!(result.is_ok(), "Failed to get hackathon by ID via service"); - let retrieved_hackathon = result.unwrap().data; - - // Validate hackathon data - assert_eq!(retrieved_hackathon.name, "Test Hackathon Service"); - assert_eq!(retrieved_hackathon.description, "Hackathon created via service test"); - assert_eq!(retrieved_hackathon.start_date, hackathon_request.start_date); - assert_eq!(retrieved_hackathon.end_date, hackathon_request.end_date); - assert_eq!(retrieved_hackathon.registration_deadline, hackathon_request.registration_deadline); - assert_eq!(retrieved_hackathon.organizers, vec![user.id.id.to_raw()]); - - // Clean up - let hackathon_id = created.data.id; - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_update_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - // Use static service methods via the trait - - // Create test organizer - let email = generate_unique_email("hackathon_updater_service"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Original Hackathon Service".to_string(), - description: "Original description service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - // Get hackathon ID - let hackathon_id = created.data.id.clone(); - - // Get hackathon for update - let hackathon_result = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await; - assert!(hackathon_result.is_ok(), "Failed to get hackathon for update"); - let _hackathon = hackathon_result.unwrap().data; - - // Update hackathon data - let update_payload = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonUpdateRequestDto { - name: Some("Updated Hackathon Title Service".to_string()), - description: Some("Updated description with more details service".to_string()), - start_date: Some(Utc::now() + Duration::days(8)), - end_date: Some(Utc::now() + Duration::days(15)), - registration_deadline: Some(Utc::now() + Duration::days(11)), - max_participants: None, - theme: None, - rules: Some("Must use Rust or TypeScript\nOpen source required\nPresentation required".to_string()), - prizes: None, - previous_winners: None, - organizers: None, - }; - - // Update hackathon via service - let update_result = HackathonService::update_hackathon(hackathon_id.clone(), update_payload, &app_state).await; - assert!(update_result.is_ok(), "Failed to update hackathon via service"); - - // Verify update via service - let updated_hackathon_result = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await; - assert!(updated_hackathon_result.is_ok(), "Failed to get updated hackathon via service"); - let updated_hackathon = updated_hackathon_result.unwrap().data; - - assert_eq!(updated_hackathon.name, "Updated Hackathon Title Service"); - assert_eq!(updated_hackathon.description, "Updated description with more details service"); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_submit_project_as_user() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - // Use static service implementation via trait - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer_submit_service"); - let participant_email = generate_unique_email("hackathon_participant_submit_service"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "User Submission Test Hackathon Service".to_string(), - description: "Test hackathon for user submissions via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - let hackathon_id = created.data.id.clone(); - - // Submit project as user via service - let submission_create_dto = HackathonSubmissionCreateRequestDto { - project_name: "My Rust Project Service".to_string(), - description: "A cool Rust project for the hackathon via service".to_string(), - repository_url: Some("https://github.com/user/my-rust-project-service".to_string()), - demo_url: Some("https://my-rust-project-service.com".to_string()), - slides_url: None, - technologies: vec!["Rust".to_string()], - }; - - let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create_dto.clone(), &app_state).await; - assert!(submit_result.is_ok(), "Failed to submit project via service"); - let submitted = submit_result.unwrap(); - - // Verify submission via service - let submission_id = submitted.data.id.clone(); - let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; - assert!(result.is_ok(), "Failed to get submission by ID via service"); - let retrieved_submission = result.unwrap().data; - - assert_eq!(retrieved_submission.hackathon_id, hackathon_id); - assert_eq!(retrieved_submission.project_name, submission_create_dto.project_name); - assert_eq!(retrieved_submission.description, submission_create_dto.description); - assert_eq!(retrieved_submission.submission_status, SubmissionStatus::Draft); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_submit_project_as_team() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let teams_repo = TeamsRepository::new(&app_state); - let hackathon_repo = HackathonRepository::new(&app_state); - let teams_repository = TeamsRepository::new(&app_state); - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer_team_submit_service"); - let member1_email = generate_unique_email("team_member1_submit_service"); - let member2_email = generate_unique_email("team_member2_submit_service"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); - let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let member1_result = users_repo.query_create_user(member1_data.clone()).await; - let member2_result = users_repo.query_create_user(member2_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(member1_result.is_ok(), "Failed to create member1"); - assert!(member2_result.is_ok(), "Failed to create member2"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); - let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); - - // Create team via teams service - let team_request = TeamsCreateRequestDto { - name: "Hackathon Team Service".to_string(), - description: Some("Team for hackathon submissions via service".to_string()), - is_open: Some(false), - max_members: Some(5), - skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), - location: Some("Remote".to_string()), - website_url: None, - github_url: None, - avatar: None, - member_emails: vec![], - }; - - // Create team via repository using helper - let team_schema = imphnen_iam::v1::teams::teams_schema::TeamsSchema::create(team_request.clone(), member1.id.id.to_raw()); - let team_create_result = teams_repository.query_create_team(team_schema.clone()).await; - assert!(team_create_result.is_ok(), "Failed to create team via repository"); - let team_id = team_schema.id.id.to_raw(); - - // Add team member via repository using helper - let member_schema = imphnen_iam::v1::teams::teams_schema::TeamMembersSchema::create(team_id.clone(), member2.id.id.to_raw(), Some("member".to_string())); - let add_member_result = teams_repository.query_add_team_member(member_schema).await; - assert!(add_member_result.is_ok(), "Failed to add team member via repository"); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Team Submission Test Hackathon Service".to_string(), - description: "Test hackathon for team submissions via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - let hackathon_id = created.data.id.clone(); - - // Submit project as team via service - let team_submission_create = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonSubmissionCreateRequestDto { - project_name: "Our Team Rust Project Service".to_string(), - description: "A collaborative Rust project by our team via service".to_string(), - repository_url: Some("https://github.com/team/our-rust-project-service".to_string()), - demo_url: Some("https://our-team-project-service.com".to_string()), - slides_url: Some("https://docs.google.com/presentation/d/12345-service".to_string()), - technologies: vec!["Rust".to_string()], - }; - - let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), team_id.clone(), team_submission_create.clone(), &app_state).await; - assert!(submit_result.is_ok(), "Failed to submit team project via service"); - let submitted = submit_result.unwrap(); - - // Verify submission via service - let submission_id = submitted.data.id.clone(); - let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; - assert!(result.is_ok(), "Failed to get team submission by ID via service"); - let retrieved_submission = result.unwrap().data; - - assert_eq!(retrieved_submission.hackathon_id, hackathon_id); - assert_eq!(retrieved_submission.team_id, team_id); - assert_eq!(retrieved_submission.project_name, team_submission_create.project_name); - assert_eq!(retrieved_submission.description, team_submission_create.description); - assert_eq!(retrieved_submission.submission_status, SubmissionStatus::Draft); - - // Clean up - let _ = hackathon_repo.delete_hackathon(hackathon_id).await; - let _ = teams_repo.query_delete_team(team_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_get_hackathon_submissions() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - let _service = HackathonService; - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer_submissions_service"); - let participant_email = generate_unique_email("hackathon_participant_submissions_service"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Submissions Test Hackathon Service".to_string(), - description: "Test hackathon for retrieving submissions via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - let hackathon_id = created.data.id.clone(); - - // Submit multiple projects via service - let submission_creates = [ - HackathonSubmissionCreateRequestDto { - project_name: "Project 1 Service".to_string(), - description: "Description 1 service".to_string(), - repository_url: Some("https://github.com/user/project1-service".to_string()), - demo_url: None, - slides_url: None, - technologies: vec!["Rust".to_string()], - }, - HackathonSubmissionCreateRequestDto { - project_name: "Project 2 Service".to_string(), - description: "Description 2 service".to_string(), - repository_url: Some("https://github.com/user/project2-service".to_string()), - demo_url: Some("https://project2-service.com".to_string()), - slides_url: None, - technologies: vec!["Rust".to_string()], - } - ]; - - for submission_create in submission_creates.iter() { - let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create.clone(), &app_state).await; - assert!(submit_result.is_ok(), "Failed to submit project via service"); - } - - // Get hackathon submissions via service - let submissions_result = HackathonService::list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_id.clone(), &app_state).await; - assert!(submissions_result.is_ok(), "Failed to get hackathon submissions via service"); - let submissions = submissions_result.unwrap().data; - - assert_eq!(submissions.len(), 2, "Should have 2 submissions via service"); - // service returns DTOs with concrete fields; compare their values directly - assert_eq!(submissions[0].project_name, "Project 1 Service"); - assert_eq!(submissions[1].project_name, "Project 2 Service"); - assert_eq!(submissions[0].submission_status, SubmissionStatus::Draft); - assert_eq!(submissions[1].submission_status, SubmissionStatus::Draft); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_get_user_hackathon_submissions() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - let _service = HackathonService; - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer_user_submissions_service"); - let participant_email = generate_unique_email("hackathon_participant_user_submissions_service"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - let _participant_thing = make_thing_from_enum(ResourceEnum::Users, &participant.id.id.to_raw()); - - // Create multiple hackathons via service - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Hackathon 1 Service".to_string(), - description: "First hackathon service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Hackathon 2 Service".to_string(), - description: "Second hackathon service".to_string(), - start_date: Utc::now() + Duration::days(14), - end_date: Utc::now() + Duration::days(21), - registration_deadline: Utc::now() + Duration::days(17), - max_participants: None, - theme: None, - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - } - ]; - - let mut hackathon_ids = Vec::new(); - - for hackathon_request in hackathon_requests.iter() { - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let hackathon_id = create_result.unwrap().data.id; - hackathon_ids.push(hackathon_id); - } - - // Submit projects to different hackathons via service - let submission_dtos = [ - HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 1 Service".to_string(), - description: "Description for hackathon 1 service".to_string(), - repository_url: Some("https://github.com/user/hackathon1-project-service".to_string()), - demo_url: None, - slides_url: None, - technologies: vec!["Rust".to_string()], - }, - HackathonSubmissionCreateRequestDto { - project_name: "Project for Hackathon 2 Service".to_string(), - description: "Description for hackathon 2 service".to_string(), - repository_url: Some("https://github.com/user/hackathon2-project-service".to_string()), - demo_url: Some("https://hackathon2-project-service.com".to_string()), - slides_url: None, - technologies: vec!["Rust".to_string()], - } - ]; - - for (i, submission_dto) in submission_dtos.iter().enumerate() { - let submit_result = HackathonService::create_hackathon_submission(hackathon_ids[i].clone(), participant.id.id.to_raw(), submission_dto.clone(), &app_state).await; - assert!(submit_result.is_ok(), "Failed to submit project via service"); - } - - // Aggregate submissions from repository for verification - let mut submissions: Vec = Vec::new(); - for hackathon_id in hackathon_ids.iter() { - let res = repo.list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_id.clone()).await.unwrap(); - submissions.extend(res.data); - } - - assert_eq!(submissions.len(), 2, "Should have 2 submissions via service"); - // these are repository-level schemas; fields are Option - assert_eq!(submissions[0].project_name, Some("Project for Hackathon 1 Service".to_string())); - assert_eq!(submissions[1].project_name, Some("Project for Hackathon 2 Service".to_string())); - assert_eq!(submissions[0].submission_status, Some(SubmissionStatus::Draft)); - assert_eq!(submissions[1].submission_status, Some(SubmissionStatus::Draft)); - - // Clean up - for hackathon_id in hackathon_ids { - let _ = repo.delete_hackathon(hackathon_id).await; - } - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_search_hackathons() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let _repo = HackathonRepository::new(&app_state); - let _service = HackathonService; - - // Create test organizer - let email = generate_unique_email("hackathon_search_organizer_service"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create test hackathons via service (use fields present in current DTOs) - let hackathon_requests = [ - HackathonCreateRequestDto { - name: "Rust Backend Hackathon Service".to_string(), - description: "Build Rust backend projects via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: Some("Backend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "TypeScript Frontend Hackathon Service".to_string(), - description: "Build TypeScript frontend projects via service".to_string(), - start_date: Utc::now() + Duration::days(14), - end_date: Utc::now() + Duration::days(21), - registration_deadline: Utc::now() + Duration::days(17), - max_participants: None, - theme: Some("Frontend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }, - HackathonCreateRequestDto { - name: "Rust Fullstack Hackathon Service".to_string(), - description: "Build fullstack projects with Rust via service".to_string(), - start_date: Utc::now() + Duration::days(21), - end_date: Utc::now() + Duration::days(28), - registration_deadline: Utc::now() + Duration::days(24), - max_participants: None, - theme: Some("Fullstack".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - } - ]; - - for hackathon_request in hackathon_requests.iter() { - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - } - - // List hackathons and verify created ones exist - let list_result = HackathonService::list_hackathons(imphnen_libs::MetaRequestDto::default(), &app_state).await; - assert!(list_result.is_ok(), "Failed to list hackathons via service"); - let list = list_result.unwrap().data; - - // Ensure at least the Rust Backend item exists - assert!(list.iter().any(|h| h.name == "Rust Backend Hackathon Service"), "Rust Backend Hackathon not found"); - - // Clean up - this would normally be done by tracking created team IDs, but for simplicity we'll leave it - // In a real test, you would store the team IDs and delete them individually - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_update_submission_status() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = HackathonRepository::new(&app_state); - // Use static service methods via trait - - // Create test users - let organizer_email = generate_unique_email("hackathon_organizer_status_service"); - let participant_email = generate_unique_email("hackathon_participant_status_service"); - - let role_id = get_role_id("mentee", &app_state).await; - let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); - let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); - - let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; - let participant_result = users_repo.query_create_user(participant_data.clone()).await; - - assert!(organizer_result.is_ok(), "Failed to create organizer"); - assert!(participant_result.is_ok(), "Failed to create participant"); - - let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); - let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Submission Status Test Hackathon Service".to_string(), - description: "Test hackathon for submission status updates via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: Some("Backend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![organizer.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - let hackathon_id = created.data.id.clone(); - - // Submit project via service - let submission_create = HackathonSubmissionCreateRequestDto { - project_name: "Test Project Service".to_string(), - description: "Test description service".to_string(), - repository_url: Some("https://github.com/user/test-project-service".to_string()), - demo_url: None, - slides_url: None, - technologies: vec!["Rust".to_string()], - }; - - let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create.clone(), &app_state).await; - assert!(submit_result.is_ok(), "Failed to submit project via service"); - let submitted = submit_result.unwrap(); - - // Get submission ID from result - let submission_id = submitted.data.id.clone(); - - // Update submission status to "Accepted" via service - let update_result = HackathonService::submit_hackathon_submission(submission_id.clone(), &app_state).await; - assert!(update_result.is_ok(), "Failed to submit hackathon submission via service"); - - // For status update flow, repository/service may expose update functions; here we assert submission retrieval - let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; - assert!(result.is_ok(), "Failed to get updated submission via service"); - let updated_submission = result.unwrap().data; - - // Expect the submission to be in Draft or Submitted state depending on service behavior - assert!(matches!(updated_submission.submission_status, SubmissionStatus::Draft | SubmissionStatus::Submitted)); - - // Clean up - let _ = repo.delete_hackathon(hackathon_id).await; - let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; - let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; - } - - #[tokio::test] - async fn test_service_delete_hackathon() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let _repo = HackathonRepository::new(&app_state); - // Use static service methods via trait - - // Create test organizer - let email = generate_unique_email("hackathon_deleter_service"); - let role_id = get_role_id("mentor", &app_state).await; - let user_data = crate::create_test_user(&email, "password123", true, &role_id); - let user_result = users_repo.query_create_user(user_data.clone()).await; - assert!(user_result.is_ok(), "Failed to create test user"); - let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); - - // Create hackathon via service - let hackathon_request = HackathonCreateRequestDto { - name: "Hackathon to Delete Service".to_string(), - description: "Hackathon that will be deleted via service".to_string(), - start_date: Utc::now() + Duration::days(7), - end_date: Utc::now() + Duration::days(14), - registration_deadline: Utc::now() + Duration::days(10), - max_participants: None, - theme: Some("Backend".to_string()), - rules: None, - prizes: None, - previous_winners: None, - organizers: vec![user.id.id.to_raw()], - }; - - let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; - assert!(create_result.is_ok(), "Failed to create hackathon via service"); - let created = create_result.unwrap(); - - // Get hackathon ID - let hackathon_id = created.data.id.clone(); - - // Verify hackathon exists before deletion - let exists_before = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await.is_ok(); - assert!(exists_before, "Hackathon should exist before deletion via service"); - - // Delete hackathon via service - let delete_result = HackathonService::delete_hackathon(hackathon_id.clone(), &app_state).await; - assert!(delete_result.is_ok(), "Failed to delete hackathon via service"); - - // Verify hackathon is deleted via service - let exists_after = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await.is_ok(); - assert!(!exists_after, "Hackathon should not exist after deletion via service"); - - // Clean up - let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; - } +#[cfg(test)] +mod tests { + use crate::{generate_unique_email, get_role_id, UsersRepository}; + use chrono::{Duration, Utc}; + use imphnen_hackathon::v1::hackathon::hackathon_dto::{ + HackathonCreateRequestDto, HackathonSubmissionCreateRequestDto + }; + use imphnen_hackathon::v1::hackathon::hackathon_repository::{ + HackathonRepository + }; + use imphnen_hackathon::v1::hackathon::hackathon_service::{HackathonService, HackathonServiceTrait}; + use imphnen_hackathon::v1::hackathon::hackathon_schema::SubmissionStatus; + use imphnen_iam::v1::teams::{TeamsCreateRequestDto, TeamsRepository}; + use imphnen_utils::{make_thing_from_enum, ResourceEnum}; + + + + #[tokio::test] + async fn test_service_create_and_get_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + + // Create test organizer + let email = generate_unique_email("hackathon_organizer_service"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Test Hackathon Service".to_string(), + description: "Hackathon created via service test".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + // Get hackathon by ID via service + let result = HackathonService::get_hackathon(created.data.id.clone(), &app_state).await; + assert!(result.is_ok(), "Failed to get hackathon by ID via service"); + let retrieved_hackathon = result.unwrap().data; + + // Validate hackathon data + assert_eq!(retrieved_hackathon.name, "Test Hackathon Service"); + assert_eq!(retrieved_hackathon.description, "Hackathon created via service test"); + assert_eq!(retrieved_hackathon.start_date, hackathon_request.start_date); + assert_eq!(retrieved_hackathon.end_date, hackathon_request.end_date); + assert_eq!(retrieved_hackathon.registration_deadline, hackathon_request.registration_deadline); + assert_eq!(retrieved_hackathon.organizers, vec![user.id.id.to_raw()]); + + // Clean up + let hackathon_id = created.data.id; + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_update_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + // Use static service methods via the trait + + // Create test organizer + let email = generate_unique_email("hackathon_updater_service"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Original Hackathon Service".to_string(), + description: "Original description service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + // Get hackathon ID + let hackathon_id = created.data.id.clone(); + + // Get hackathon for update + let hackathon_result = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await; + assert!(hackathon_result.is_ok(), "Failed to get hackathon for update"); + let _hackathon = hackathon_result.unwrap().data; + + // Update hackathon data + let update_payload = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonUpdateRequestDto { + name: Some("Updated Hackathon Title Service".to_string()), + description: Some("Updated description with more details service".to_string()), + start_date: Some(Utc::now() + Duration::days(8)), + end_date: Some(Utc::now() + Duration::days(15)), + registration_deadline: Some(Utc::now() + Duration::days(11)), + max_participants: None, + theme: None, + rules: Some("Must use Rust or TypeScript\nOpen source required\nPresentation required".to_string()), + prizes: None, + previous_winners: None, + organizers: None, + }; + + // Update hackathon via service + let update_result = HackathonService::update_hackathon(hackathon_id.clone(), update_payload, &app_state).await; + assert!(update_result.is_ok(), "Failed to update hackathon via service"); + + // Verify update via service + let updated_hackathon_result = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await; + assert!(updated_hackathon_result.is_ok(), "Failed to get updated hackathon via service"); + let updated_hackathon = updated_hackathon_result.unwrap().data; + + assert_eq!(updated_hackathon.name, "Updated Hackathon Title Service"); + assert_eq!(updated_hackathon.description, "Updated description with more details service"); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_submit_project_as_user() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + // Use static service implementation via trait + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer_submit_service"); + let participant_email = generate_unique_email("hackathon_participant_submit_service"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "User Submission Test Hackathon Service".to_string(), + description: "Test hackathon for user submissions via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + let hackathon_id = created.data.id.clone(); + + // Submit project as user via service + let submission_create_dto = HackathonSubmissionCreateRequestDto { + project_name: "My Rust Project Service".to_string(), + description: "A cool Rust project for the hackathon via service".to_string(), + repository_url: Some("https://github.com/user/my-rust-project-service".to_string()), + demo_url: Some("https://my-rust-project-service.com".to_string()), + slides_url: None, + technologies: vec!["Rust".to_string()], + }; + + let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create_dto.clone(), &app_state).await; + assert!(submit_result.is_ok(), "Failed to submit project via service"); + let submitted = submit_result.unwrap(); + + // Verify submission via service + let submission_id = submitted.data.id.clone(); + let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; + assert!(result.is_ok(), "Failed to get submission by ID via service"); + let retrieved_submission = result.unwrap().data; + + assert_eq!(retrieved_submission.hackathon_id, hackathon_id); + assert_eq!(retrieved_submission.project_name, submission_create_dto.project_name); + assert_eq!(retrieved_submission.description, submission_create_dto.description); + assert_eq!(retrieved_submission.submission_status, SubmissionStatus::Draft); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_submit_project_as_team() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let teams_repo = TeamsRepository::new(&app_state); + let hackathon_repo = HackathonRepository::new(&app_state); + let teams_repository = TeamsRepository::new(&app_state); + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer_team_submit_service"); + let member1_email = generate_unique_email("team_member1_submit_service"); + let member2_email = generate_unique_email("team_member2_submit_service"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let member1_data = crate::create_test_user(&member1_email, "password123", true, &role_id); + let member2_data = crate::create_test_user(&member2_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let member1_result = users_repo.query_create_user(member1_data.clone()).await; + let member2_result = users_repo.query_create_user(member2_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(member1_result.is_ok(), "Failed to create member1"); + assert!(member2_result.is_ok(), "Failed to create member2"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let member1 = users_repo.query_user_by_email(member1_email.clone()).await.unwrap(); + let member2 = users_repo.query_user_by_email(member2_email.clone()).await.unwrap(); + + // Create team via teams service + let team_request = TeamsCreateRequestDto { + name: "Hackathon Team Service".to_string(), + description: Some("Team for hackathon submissions via service".to_string()), + is_open: Some(false), + max_members: Some(5), + skills_required: Some(vec!["Rust".to_string(), "Backend".to_string()]), + location: Some("Remote".to_string()), + website_url: None, + github_url: None, + avatar: None, + member_emails: vec![], + }; + + // Create team via repository using helper + let team_schema = imphnen_iam::v1::teams::teams_schema::TeamsSchema::create(team_request.clone(), member1.id.id.to_raw()); + let team_create_result = teams_repository.query_create_team(team_schema.clone()).await; + assert!(team_create_result.is_ok(), "Failed to create team via repository"); + let team_id = team_schema.id.id.to_raw(); + + // Add team member via repository using helper + let member_schema = imphnen_iam::v1::teams::teams_schema::TeamMembersSchema::create(team_id.clone(), member2.id.id.to_raw(), Some("member".to_string())); + let add_member_result = teams_repository.query_add_team_member(member_schema).await; + assert!(add_member_result.is_ok(), "Failed to add team member via repository"); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Team Submission Test Hackathon Service".to_string(), + description: "Test hackathon for team submissions via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + let hackathon_id = created.data.id.clone(); + + // Submit project as team via service + let team_submission_create = imphnen_hackathon::v1::hackathon::hackathon_dto::HackathonSubmissionCreateRequestDto { + project_name: "Our Team Rust Project Service".to_string(), + description: "A collaborative Rust project by our team via service".to_string(), + repository_url: Some("https://github.com/team/our-rust-project-service".to_string()), + demo_url: Some("https://our-team-project-service.com".to_string()), + slides_url: Some("https://docs.google.com/presentation/d/12345-service".to_string()), + technologies: vec!["Rust".to_string()], + }; + + let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), team_id.clone(), team_submission_create.clone(), &app_state).await; + assert!(submit_result.is_ok(), "Failed to submit team project via service"); + let submitted = submit_result.unwrap(); + + // Verify submission via service + let submission_id = submitted.data.id.clone(); + let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; + assert!(result.is_ok(), "Failed to get team submission by ID via service"); + let retrieved_submission = result.unwrap().data; + + assert_eq!(retrieved_submission.hackathon_id, hackathon_id); + assert_eq!(retrieved_submission.team_id, team_id); + assert_eq!(retrieved_submission.project_name, team_submission_create.project_name); + assert_eq!(retrieved_submission.description, team_submission_create.description); + assert_eq!(retrieved_submission.submission_status, SubmissionStatus::Draft); + + // Clean up + let _ = hackathon_repo.delete_hackathon(hackathon_id).await; + let _ = teams_repo.query_delete_team(team_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member1.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(member2.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_get_hackathon_submissions() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + let _service = HackathonService; + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer_submissions_service"); + let participant_email = generate_unique_email("hackathon_participant_submissions_service"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Submissions Test Hackathon Service".to_string(), + description: "Test hackathon for retrieving submissions via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + let hackathon_id = created.data.id.clone(); + + // Submit multiple projects via service + let submission_creates = [ + HackathonSubmissionCreateRequestDto { + project_name: "Project 1 Service".to_string(), + description: "Description 1 service".to_string(), + repository_url: Some("https://github.com/user/project1-service".to_string()), + demo_url: None, + slides_url: None, + technologies: vec!["Rust".to_string()], + }, + HackathonSubmissionCreateRequestDto { + project_name: "Project 2 Service".to_string(), + description: "Description 2 service".to_string(), + repository_url: Some("https://github.com/user/project2-service".to_string()), + demo_url: Some("https://project2-service.com".to_string()), + slides_url: None, + technologies: vec!["Rust".to_string()], + } + ]; + + for submission_create in submission_creates.iter() { + let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create.clone(), &app_state).await; + assert!(submit_result.is_ok(), "Failed to submit project via service"); + } + + // Get hackathon submissions via service + let submissions_result = HackathonService::list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_id.clone(), &app_state).await; + assert!(submissions_result.is_ok(), "Failed to get hackathon submissions via service"); + let submissions = submissions_result.unwrap().data; + + assert_eq!(submissions.len(), 2, "Should have 2 submissions via service"); + // service returns DTOs with concrete fields; compare their values directly + assert_eq!(submissions[0].project_name, "Project 1 Service"); + assert_eq!(submissions[1].project_name, "Project 2 Service"); + assert_eq!(submissions[0].submission_status, SubmissionStatus::Draft); + assert_eq!(submissions[1].submission_status, SubmissionStatus::Draft); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_get_user_hackathon_submissions() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + let _service = HackathonService; + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer_user_submissions_service"); + let participant_email = generate_unique_email("hackathon_participant_user_submissions_service"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + let _participant_thing = make_thing_from_enum(ResourceEnum::Users, &participant.id.id.to_raw()); + + // Create multiple hackathons via service + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Hackathon 1 Service".to_string(), + description: "First hackathon service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Hackathon 2 Service".to_string(), + description: "Second hackathon service".to_string(), + start_date: Utc::now() + Duration::days(14), + end_date: Utc::now() + Duration::days(21), + registration_deadline: Utc::now() + Duration::days(17), + max_participants: None, + theme: None, + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + } + ]; + + let mut hackathon_ids = Vec::new(); + + for hackathon_request in hackathon_requests.iter() { + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let hackathon_id = create_result.unwrap().data.id; + hackathon_ids.push(hackathon_id); + } + + // Submit projects to different hackathons via service + let submission_dtos = [ + HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 1 Service".to_string(), + description: "Description for hackathon 1 service".to_string(), + repository_url: Some("https://github.com/user/hackathon1-project-service".to_string()), + demo_url: None, + slides_url: None, + technologies: vec!["Rust".to_string()], + }, + HackathonSubmissionCreateRequestDto { + project_name: "Project for Hackathon 2 Service".to_string(), + description: "Description for hackathon 2 service".to_string(), + repository_url: Some("https://github.com/user/hackathon2-project-service".to_string()), + demo_url: Some("https://hackathon2-project-service.com".to_string()), + slides_url: None, + technologies: vec!["Rust".to_string()], + } + ]; + + for (i, submission_dto) in submission_dtos.iter().enumerate() { + let submit_result = HackathonService::create_hackathon_submission(hackathon_ids[i].clone(), participant.id.id.to_raw(), submission_dto.clone(), &app_state).await; + assert!(submit_result.is_ok(), "Failed to submit project via service"); + } + + // Aggregate submissions from repository for verification + let mut submissions: Vec = Vec::new(); + for hackathon_id in hackathon_ids.iter() { + let res = repo.list_hackathon_submissions(imphnen_libs::MetaRequestDto::default(), hackathon_id.clone()).await.unwrap(); + submissions.extend(res.data); + } + + assert_eq!(submissions.len(), 2, "Should have 2 submissions via service"); + // these are repository-level schemas; fields are Option + assert_eq!(submissions[0].project_name, Some("Project for Hackathon 1 Service".to_string())); + assert_eq!(submissions[1].project_name, Some("Project for Hackathon 2 Service".to_string())); + assert_eq!(submissions[0].submission_status, Some(SubmissionStatus::Draft)); + assert_eq!(submissions[1].submission_status, Some(SubmissionStatus::Draft)); + + // Clean up + for hackathon_id in hackathon_ids { + let _ = repo.delete_hackathon(hackathon_id).await; + } + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_search_hackathons() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let _repo = HackathonRepository::new(&app_state); + let _service = HackathonService; + + // Create test organizer + let email = generate_unique_email("hackathon_search_organizer_service"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create test hackathons via service (use fields present in current DTOs) + let hackathon_requests = [ + HackathonCreateRequestDto { + name: "Rust Backend Hackathon Service".to_string(), + description: "Build Rust backend projects via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: Some("Backend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "TypeScript Frontend Hackathon Service".to_string(), + description: "Build TypeScript frontend projects via service".to_string(), + start_date: Utc::now() + Duration::days(14), + end_date: Utc::now() + Duration::days(21), + registration_deadline: Utc::now() + Duration::days(17), + max_participants: None, + theme: Some("Frontend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }, + HackathonCreateRequestDto { + name: "Rust Fullstack Hackathon Service".to_string(), + description: "Build fullstack projects with Rust via service".to_string(), + start_date: Utc::now() + Duration::days(21), + end_date: Utc::now() + Duration::days(28), + registration_deadline: Utc::now() + Duration::days(24), + max_participants: None, + theme: Some("Fullstack".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + } + ]; + + for hackathon_request in hackathon_requests.iter() { + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + } + + // List hackathons and verify created ones exist + let list_result = HackathonService::list_hackathons(imphnen_libs::MetaRequestDto::default(), &app_state).await; + assert!(list_result.is_ok(), "Failed to list hackathons via service"); + let list = list_result.unwrap().data; + + // Ensure at least the Rust Backend item exists + assert!(list.iter().any(|h| h.name == "Rust Backend Hackathon Service"), "Rust Backend Hackathon not found"); + + // Clean up - this would normally be done by tracking created team IDs, but for simplicity we'll leave it + // In a real test, you would store the team IDs and delete them individually + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_update_submission_status() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = HackathonRepository::new(&app_state); + // Use static service methods via trait + + // Create test users + let organizer_email = generate_unique_email("hackathon_organizer_status_service"); + let participant_email = generate_unique_email("hackathon_participant_status_service"); + + let role_id = get_role_id("mentee", &app_state).await; + let organizer_data = crate::create_test_user(&organizer_email, "password123", true, &role_id); + let participant_data = crate::create_test_user(&participant_email, "password123", true, &role_id); + + let organizer_result = users_repo.query_create_user(organizer_data.clone()).await; + let participant_result = users_repo.query_create_user(participant_data.clone()).await; + + assert!(organizer_result.is_ok(), "Failed to create organizer"); + assert!(participant_result.is_ok(), "Failed to create participant"); + + let organizer = users_repo.query_user_by_email(organizer_email.clone()).await.unwrap(); + let participant = users_repo.query_user_by_email(participant_email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Submission Status Test Hackathon Service".to_string(), + description: "Test hackathon for submission status updates via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: Some("Backend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![organizer.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + let hackathon_id = created.data.id.clone(); + + // Submit project via service + let submission_create = HackathonSubmissionCreateRequestDto { + project_name: "Test Project Service".to_string(), + description: "Test description service".to_string(), + repository_url: Some("https://github.com/user/test-project-service".to_string()), + demo_url: None, + slides_url: None, + technologies: vec!["Rust".to_string()], + }; + + let submit_result = HackathonService::create_hackathon_submission(hackathon_id.clone(), participant.id.id.to_raw(), submission_create.clone(), &app_state).await; + assert!(submit_result.is_ok(), "Failed to submit project via service"); + let submitted = submit_result.unwrap(); + + // Get submission ID from result + let submission_id = submitted.data.id.clone(); + + // Update submission status to "Accepted" via service + let update_result = HackathonService::submit_hackathon_submission(submission_id.clone(), &app_state).await; + assert!(update_result.is_ok(), "Failed to submit hackathon submission via service"); + + // For status update flow, repository/service may expose update functions; here we assert submission retrieval + let result = HackathonService::get_hackathon_submission(submission_id.clone(), &app_state).await; + assert!(result.is_ok(), "Failed to get updated submission via service"); + let updated_submission = result.unwrap().data; + + // Expect the submission to be in Draft or Submitted state depending on service behavior + assert!(matches!(updated_submission.submission_status, SubmissionStatus::Draft | SubmissionStatus::Submitted)); + + // Clean up + let _ = repo.delete_hackathon(hackathon_id).await; + let _ = users_repo.query_delete_user(organizer.id.id.to_raw()).await; + let _ = users_repo.query_delete_user(participant.id.id.to_raw()).await; + } + + #[tokio::test] + async fn test_service_delete_hackathon() { + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let _repo = HackathonRepository::new(&app_state); + // Use static service methods via trait + + // Create test organizer + let email = generate_unique_email("hackathon_deleter_service"); + let role_id = get_role_id("mentor", &app_state).await; + let user_data = crate::create_test_user(&email, "password123", true, &role_id); + let user_result = users_repo.query_create_user(user_data.clone()).await; + assert!(user_result.is_ok(), "Failed to create test user"); + let user = users_repo.query_user_by_email(email.clone()).await.unwrap(); + + // Create hackathon via service + let hackathon_request = HackathonCreateRequestDto { + name: "Hackathon to Delete Service".to_string(), + description: "Hackathon that will be deleted via service".to_string(), + start_date: Utc::now() + Duration::days(7), + end_date: Utc::now() + Duration::days(14), + registration_deadline: Utc::now() + Duration::days(10), + max_participants: None, + theme: Some("Backend".to_string()), + rules: None, + prizes: None, + previous_winners: None, + organizers: vec![user.id.id.to_raw()], + }; + + let create_result = HackathonService::create_hackathon(hackathon_request.clone(), &app_state).await; + assert!(create_result.is_ok(), "Failed to create hackathon via service"); + let created = create_result.unwrap(); + + // Get hackathon ID + let hackathon_id = created.data.id.clone(); + + // Verify hackathon exists before deletion + let exists_before = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await.is_ok(); + assert!(exists_before, "Hackathon should exist before deletion via service"); + + // Delete hackathon via service + let delete_result = HackathonService::delete_hackathon(hackathon_id.clone(), &app_state).await; + assert!(delete_result.is_ok(), "Failed to delete hackathon via service"); + + // Verify hackathon is deleted via service + let exists_after = HackathonService::get_hackathon(hackathon_id.clone(), &app_state).await.is_ok(); + assert!(!exists_after, "Hackathon should not exist after deletion via service"); + + // Clean up + let _ = users_repo.query_delete_user(user.id.id.to_raw()).await; + } } \ No newline at end of file