/*! * Moderate Stress Test - 25 Documents for Complete Verification */ use reqwest::Client; use serde_json::Value; use std::time::{Duration, Instant}; use tokio::time::sleep; use uuid::Uuid; use futures; use readur::models::{DocumentResponse, CreateUser, LoginRequest, LoginResponse}; fn get_base_url() -> String { std::env::var("API_URL").unwrap_or_else(|_| "http://localhost:8000".to_string()) } const TIMEOUT: Duration = Duration::from_secs(120); struct StressTester { client: Client, token: String, } impl StressTester { async fn new() -> Self { let client = Client::new(); // Check server health client.get(&format!("{}/api/health", get_base_url())) .timeout(Duration::from_secs(5)) .send() .await .expect("Server should be running"); // Create test user let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis(); let username = format!("stress_25_{}", timestamp); let email = format!("stress_25_{}@test.com", timestamp); // Register user let user_data = CreateUser { username: username.clone(), email: email.clone(), password: "testpass123".to_string(), role: Some(readur::models::UserRole::User), }; client.post(&format!("{}/api/auth/register", get_base_url())) .json(&user_data) .send() .await .expect("Registration should work"); // Login let login_data = LoginRequest { username: username.clone(), password: "testpass123".to_string(), }; let login_response = client .post(&format!("{}/api/auth/login", get_base_url())) .json(&login_data) .send() .await .expect("Login should work"); let login_result: LoginResponse = login_response.json().await.expect("Login should return JSON"); let token = login_result.token; println!("āœ… Stress tester initialized"); Self { client, token } } async fn upload_document(&self, content: &str, filename: &str) -> DocumentResponse { let part = reqwest::multipart::Part::text(content.to_string()) .file_name(filename.to_string()) .mime_str("text/plain") .expect("Valid mime type"); let form = reqwest::multipart::Form::new().part("file", part); let response = self.client .post(&format!("{}/api/documents", get_base_url())) .header("Authorization", format!("Bearer {}", self.token)) .multipart(form) .send() .await .expect("Upload should work"); response.json().await.expect("Valid JSON") } async fn wait_for_ocr_completion(&self, document_ids: &[Uuid]) -> Vec { let start = Instant::now(); while start.elapsed() < TIMEOUT { let all_docs = self.get_all_documents().await; let completed = all_docs.iter() .filter(|doc| { let doc_id_str = doc["id"].as_str().unwrap_or(""); let status = doc["ocr_status"].as_str().unwrap_or(""); document_ids.iter().any(|id| id.to_string() == doc_id_str) && status == "completed" }) .count(); if completed == document_ids.len() { return all_docs.into_iter() .filter(|doc| { let doc_id_str = doc["id"].as_str().unwrap_or(""); document_ids.iter().any(|id| id.to_string() == doc_id_str) }) .collect(); } sleep(Duration::from_millis(500)).await; } panic!("OCR processing did not complete within timeout"); } async fn get_all_documents(&self) -> Vec { let response = self.client .get(&format!("{}/api/documents", get_base_url())) .header("Authorization", format!("Bearer {}", self.token)) .send() .await .expect("Documents endpoint should work"); let data: Value = response.json().await.expect("Valid JSON"); match data { Value::Object(obj) if obj.contains_key("documents") => { obj["documents"].as_array().unwrap_or(&vec![]).clone() } Value::Array(arr) => arr, _ => vec![] } } } #[tokio::test] async fn stress_test_25_documents() { println!("šŸš€ MODERATE STRESS TEST: 25 DOCUMENTS"); println!("======================================"); let tester = StressTester::new().await; // Create 25 documents with unique content let mut documents = Vec::new(); for i in 1..=25 { let content = format!("STRESS-DOC-{:02}-SIGNATURE-{:02}-UNIQUE-CONTENT", i, i); let filename = format!("stress_{:02}.txt", i); documents.push((content, filename)); } println!("šŸ“Š Testing {} documents concurrently", documents.len()); // Phase 1: Upload all documents concurrently println!("\nšŸ UPLOADING..."); let upload_start = Instant::now(); let uploaded_docs = futures::future::join_all( documents.iter().map(|(content, filename)| { tester.upload_document(content, filename) }).collect::>() ).await; let upload_duration = upload_start.elapsed(); println!("āœ… {} uploads completed in {:?}", uploaded_docs.len(), upload_duration); // Phase 2: Wait for OCR completion println!("\nšŸ”¬ PROCESSING OCR..."); let processing_start = Instant::now(); let document_ids: Vec = uploaded_docs.iter().map(|doc| doc.id).collect(); let final_docs = tester.wait_for_ocr_completion(&document_ids).await; let processing_duration = processing_start.elapsed(); println!("āœ… OCR processing completed in {:?}", processing_duration); // Phase 3: Corruption Analysis println!("\nšŸ“Š VERIFYING RESULTS..."); let mut successful = 0; let mut corrupted = 0; let mut corruption_details = Vec::new(); for (i, doc) in final_docs.iter().enumerate() { let expected_content = &documents[i].0; let actual_text = doc["ocr_text"].as_str().unwrap_or(""); let doc_id = doc["id"].as_str().unwrap_or(""); if actual_text == expected_content { successful += 1; } else { corrupted += 1; corruption_details.push((doc_id.to_string(), expected_content.clone(), actual_text.to_string())); } } // Final Results println!("\nšŸ† STRESS TEST RESULTS"); println!("======================"); println!("šŸ“Š Total Documents: {}", documents.len()); println!("āœ… Successful: {}", successful); println!("āŒ Corrupted: {}", corrupted); println!("šŸ“ˆ Success Rate: {:.1}%", (successful as f64 / documents.len() as f64) * 100.0); println!("ā±ļø Upload Time: {:?}", upload_duration); println!("ā±ļø OCR Time: {:?}", processing_duration); println!("ā±ļø Total Time: {:?}", upload_duration + processing_duration); if corrupted == 0 { println!("\nšŸŽ‰ STRESS TEST PASSED!"); println!("šŸŽÆ ALL {} DOCUMENTS PROCESSED WITHOUT CORRUPTION!", documents.len()); println!("šŸš€ HIGH CONCURRENCY OCR CORRUPTION ISSUE IS FULLY RESOLVED!"); } else { println!("\n🚨 STRESS TEST FAILED!"); println!("āŒ CORRUPTION DETECTED IN {} DOCUMENTS:", corrupted); for (doc_id, expected, actual) in &corruption_details { println!(" šŸ“„ {}: expected '{}' got '{}'", doc_id, expected, actual); } panic!("CORRUPTION DETECTED in {} out of {} documents", corrupted, documents.len()); } }