diff --git a/src/routes/labels.rs b/src/routes/labels.rs index 8cd8f92..80a4152 100644 --- a/src/routes/labels.rs +++ b/src/routes/labels.rs @@ -169,6 +169,11 @@ pub async fn create_label( ) -> Result, StatusCode> { let user_id = auth_user.user.id; + // Validate name is not empty + if payload.name.trim().is_empty() { + return Err(StatusCode::BAD_REQUEST); + } + // Validate color format if !payload.color.starts_with('#') || payload.color.len() != 7 { return Err(StatusCode::BAD_REQUEST); diff --git a/tests/comprehensive_source_management_tests.rs b/tests/comprehensive_source_management_tests.rs index d5f2187..453828a 100644 --- a/tests/comprehensive_source_management_tests.rs +++ b/tests/comprehensive_source_management_tests.rs @@ -16,8 +16,7 @@ use reqwest::Client; use serde_json::{json, Value}; -use std::time::{Duration, Instant}; -use tokio::time::sleep; +use std::time::Duration; use uuid::Uuid; use readur::models::{CreateUser, LoginRequest, LoginResponse, UserRole, SourceType}; @@ -48,12 +47,13 @@ impl SourceTestClient { let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() - .as_millis(); - let username = format!("source_test_{}_{}", role.to_string(), timestamp); + .as_nanos(); + let random_suffix = uuid::Uuid::new_v4().to_string().replace("-", "")[..8].to_string(); + let username = format!("source_test_{}_{}_{}", role.to_string(), timestamp, random_suffix); let email = format!("source_test_{}@example.com", timestamp); let password = "testpassword123"; - // Register user + // Register user with retry logic let user_data = CreateUser { username: username.clone(), email: email.clone(), @@ -61,14 +61,30 @@ impl SourceTestClient { role: Some(role), }; - let register_response = self.client - .post(&format!("{}/api/auth/register", get_base_url())) - .json(&user_data) - .send() - .await?; + let mut retry_count = 0; + let register_response = loop { + match self.client + .post(&format!("{}/api/auth/register", get_base_url())) + .json(&user_data) + .timeout(Duration::from_secs(10)) + .send() + .await + { + Ok(resp) => break resp, + Err(e) => { + retry_count += 1; + if retry_count >= 3 { + return Err(format!("Registration failed after 3 retries: {}", e).into()); + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + } + }; if !register_response.status().is_success() { - return Err(format!("Registration failed: {}", register_response.text().await?).into()); + let status = register_response.status(); + let text = register_response.text().await.unwrap_or_else(|_| "No response body".to_string()); + return Err(format!("Registration failed with status {}: {}", status, text).into()); } // Login to get token diff --git a/tests/error_handling_edge_cases_tests.rs b/tests/error_handling_edge_cases_tests.rs index 27d6f9d..463aea6 100644 --- a/tests/error_handling_edge_cases_tests.rs +++ b/tests/error_handling_edge_cases_tests.rs @@ -56,8 +56,9 @@ impl ErrorHandlingTestClient { let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() - .as_millis(); - let username = format!("error_test_{}_{}", role.to_string(), timestamp); + .as_nanos(); + let random_suffix = uuid::Uuid::new_v4().to_string().replace("-", "")[..8].to_string(); + let username = format!("error_test_{}_{}_{}", role.to_string(), timestamp, random_suffix); let email = format!("error_test_{}@example.com", timestamp); let password = "testpassword123"; diff --git a/tests/file_processing_pipeline_tests.rs b/tests/file_processing_pipeline_tests.rs index a561990..0b2dd68 100644 --- a/tests/file_processing_pipeline_tests.rs +++ b/tests/file_processing_pipeline_tests.rs @@ -62,12 +62,13 @@ impl FileProcessingTestClient { let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() - .as_millis(); - let username = format!("file_proc_test_{}", timestamp); + .as_nanos(); + let random_suffix = uuid::Uuid::new_v4().to_string().replace("-", "")[..8].to_string(); + let username = format!("file_proc_test_{}_{}", timestamp, random_suffix); let email = format!("file_proc_test_{}@example.com", timestamp); let password = "fileprocessingpassword123"; - // Register user + // Register user with retry logic let user_data = CreateUser { username: username.clone(), email: email.clone(), @@ -75,14 +76,30 @@ impl FileProcessingTestClient { role: Some(UserRole::User), }; - let register_response = self.client - .post(&format!("{}/api/auth/register", get_base_url())) - .json(&user_data) - .send() - .await?; + let mut retry_count = 0; + let register_response = loop { + match self.client + .post(&format!("{}/api/auth/register", get_base_url())) + .json(&user_data) + .timeout(Duration::from_secs(10)) + .send() + .await + { + Ok(resp) => break resp, + Err(e) => { + retry_count += 1; + if retry_count >= 3 { + return Err(format!("Registration failed after 3 retries: {}", e).into()); + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + } + }; if !register_response.status().is_success() { - return Err(format!("Registration failed: {}", register_response.text().await?).into()); + let status = register_response.status(); + let text = register_response.text().await.unwrap_or_else(|_| "No response body".to_string()); + return Err(format!("Registration failed with status {}: {}", status, text).into()); } // Login to get token diff --git a/tests/local_folder_sync_tests.rs b/tests/local_folder_sync_tests.rs index 3aef790..1c78936 100644 --- a/tests/local_folder_sync_tests.rs +++ b/tests/local_folder_sync_tests.rs @@ -437,6 +437,10 @@ fn test_concurrent_access_safety() { let base_path = Arc::new(temp_dir.path().to_path_buf()); let file_count = Arc::new(Mutex::new(0)); + // Verify the directory has files before testing concurrent access + let initial_count = fs::read_dir(&*base_path).unwrap().count(); + assert!(initial_count > 0, "Test directory should contain files"); + let mut handles = vec![]; // Spawn multiple threads to read the same directory @@ -446,16 +450,25 @@ fn test_concurrent_access_safety() { let handle = thread::spawn(move || { let mut local_count = 0; - if let Ok(entries) = fs::read_dir(&*base_path) { - for entry in entries { - if let Ok(entry) = entry { - if entry.file_type().unwrap().is_file() { - local_count += 1; + + // Recursively count files + fn count_files_in_dir(path: &std::path::Path, count: &mut usize) { + if let Ok(entries) = fs::read_dir(path) { + for entry in entries { + if let Ok(entry) = entry { + let path = entry.path(); + if path.is_file() { + *count += 1; + } else if path.is_dir() { + count_files_in_dir(&path, count); + } } } } } + count_files_in_dir(&*base_path, &mut local_count); + let mut count = file_count.lock().unwrap(); *count += local_count; });