/*! * Document Deletion Integration Tests * * Comprehensive tests for single and bulk document deletion functionality. * Tests HTTP endpoints, file cleanup, role-based access, and edge cases. */ use reqwest::{Client, multipart}; use serde_json::{json, Value}; use std::time::Duration; use uuid::Uuid; use readur::models::{DocumentResponse, CreateUser, LoginRequest, LoginResponse, UserRole}; fn get_base_url() -> String { std::env::var("API_URL").unwrap_or_else(|_| "http://localhost:8000".to_string()) } const TIMEOUT: Duration = Duration::from_secs(30); /// Test client for document deletion integration tests struct DocumentDeletionTestClient { client: Client, token: Option, user_id: Option, } impl DocumentDeletionTestClient { fn new() -> Self { Self { client: Client::new(), token: None, user_id: None, } } /// Check if server is running and healthy async fn check_server_health(&self) -> Result<(), Box> { let response = self.client .get(&format!("{}/api/health", get_base_url())) .timeout(Duration::from_secs(5)) .send() .await?; if !response.status().is_success() { return Err("Server health check failed".into()); } let health: Value = response.json().await?; if health["status"] != "ok" { return Err("Server is not healthy".into()); } Ok(()) } /// Register a new user and login to get auth token async fn register_and_login(&mut self, username: &str, email: &str, password: &str, role: Option) -> Result> { // Register user let user_data = CreateUser { username: username.to_string(), email: email.to_string(), password: password.to_string(), role: Some(role.unwrap_or(UserRole::User)), }; let register_response = self.client .post(&format!("{}/api/auth/register", get_base_url())) .json(&user_data) .timeout(TIMEOUT) .send() .await?; if !register_response.status().is_success() { return Err(format!("Registration failed: {}", register_response.text().await?).into()); } // Login to get token let login_data = LoginRequest { username: username.to_string(), password: password.to_string(), }; let login_response = self.client .post(&format!("{}/api/auth/login", get_base_url())) .json(&login_data) .timeout(TIMEOUT) .send() .await?; if !login_response.status().is_success() { return Err(format!("Login failed: {}", login_response.text().await?).into()); } let login_result: LoginResponse = login_response.json().await?; self.token = Some(login_result.token.clone()); self.user_id = Some(login_result.user.id.to_string()); Ok(login_result.token) } /// Upload a test document async fn upload_document(&self, content: &[u8], filename: &str) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let form = multipart::Form::new() .part("file", multipart::Part::bytes(content.to_vec()) .file_name(filename.to_string()) .mime_str("text/plain")?); let response = self.client .post(&format!("{}/api/documents", get_base_url())) .header("Authorization", format!("Bearer {}", token)) .multipart(form) .timeout(TIMEOUT) .send() .await?; if !response.status().is_success() { return Err(format!("Document upload failed: {}", response.text().await?).into()); } let document: DocumentResponse = response.json().await?; Ok(document) } /// Delete a single document async fn delete_document(&self, document_id: &str) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let response = self.client .delete(&format!("{}/api/documents/{}", get_base_url(), document_id)) .header("Authorization", format!("Bearer {}", token)) .timeout(TIMEOUT) .send() .await?; let status = response.status(); let text = response.text().await?; if !status.is_success() { return Err(format!("Document deletion failed ({}): {}", status, text).into()); } let result: Value = serde_json::from_str(&text)?; Ok(result) } /// Bulk delete documents async fn bulk_delete_documents(&self, document_ids: &[String]) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let request_data = json!({ "document_ids": document_ids }); let response = self.client .delete(&format!("{}/api/documents", get_base_url())) .header("Authorization", format!("Bearer {}", token)) .json(&request_data) .timeout(TIMEOUT) .send() .await?; let status = response.status(); let text = response.text().await?; if !status.is_success() { return Err(format!("Bulk deletion failed ({}): {}", status, text).into()); } let result: Value = serde_json::from_str(&text)?; Ok(result) } /// Delete document without authentication (for testing unauthorized access) async fn delete_document_without_auth(&self, document_id: &str) -> Result> { let response = self.client .delete(&format!("{}/api/documents/{}", get_base_url(), document_id)) .timeout(TIMEOUT) .send() .await?; let status = response.status(); let text = response.text().await?; if !status.is_success() { return Err(format!("Document deletion failed ({}): {}", status, text).into()); } let result: Value = serde_json::from_str(&text)?; Ok(result) } /// Get document by ID async fn get_document(&self, document_id: &str) -> Result, Box> { let token = self.token.as_ref().ok_or("Not authenticated")?; let response = self.client .get(&format!("{}/api/documents/{}", get_base_url(), document_id)) .header("Authorization", format!("Bearer {}", token)) .timeout(TIMEOUT) .send() .await?; if response.status() == 404 { return Ok(None); } if !response.status().is_success() { return Err(format!("Get document failed: {}", response.text().await?).into()); } let document: DocumentResponse = response.json().await?; Ok(Some(document)) } /// List documents async fn list_documents(&self) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let response = self.client .get(&format!("{}/api/documents", get_base_url())) .header("Authorization", format!("Bearer {}", token)) .timeout(TIMEOUT) .send() .await?; if !response.status().is_success() { return Err(format!("List documents failed: {}", response.text().await?).into()); } let result: Value = response.json().await?; Ok(result) } /// Delete failed OCR documents async fn delete_failed_ocr_documents(&self, preview_only: bool) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let response = self.client .post(&format!("{}/api/documents/delete-failed-ocr", get_base_url())) .header("Authorization", format!("Bearer {}", token)) .json(&json!({ "preview_only": preview_only })) .timeout(TIMEOUT) .send() .await?; if !response.status().is_success() { return Err(format!("Delete failed OCR documents failed: {}", response.text().await?).into()); } let result: Value = response.json().await?; Ok(result) } /// Delete low confidence documents (updated to use new combined endpoint) async fn delete_low_confidence_documents(&self, threshold: f64, preview_only: bool) -> Result> { let token = self.token.as_ref().ok_or("Not authenticated")?; let response = self.client .post(&format!("{}/api/documents/delete-low-confidence", get_base_url())) .header("Authorization", format!("Bearer {}", token)) .json(&json!({ "max_confidence": threshold, "preview_only": preview_only })) .timeout(TIMEOUT) .send() .await?; if !response.status().is_success() { return Err(format!("Delete low confidence documents failed: {}", response.text().await?).into()); } let result: Value = response.json().await?; Ok(result) } /// Create and login user (convenience method) async fn create_and_login_user(&mut self, username: &str, password: &str, role: UserRole) -> Result> { // Add random suffix to avoid username collisions between parallel tests let random_suffix = uuid::Uuid::new_v4().to_string().chars().take(8).collect::(); let unique_username = format!("{}_{}", username, random_suffix); let email = format!("{}@example.com", unique_username); self.register_and_login(&unique_username, &email, password, Some(role)).await } } /// Skip test if server is not running macro_rules! skip_if_server_down { ($client:expr) => { if let Err(_) = $client.check_server_health().await { println!("Skipping test: Server is not running at {}", get_base_url()); return; } }; } #[tokio::test] async fn test_single_document_deletion_success() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_delete_{}", Uuid::new_v4()), &format!("testuser_delete_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Upload a test document let test_content = b"This is a test document for deletion."; let document = client.upload_document(test_content, "test_deletion.txt") .await.expect("Failed to upload document"); println!("Uploaded document: {}", document.id); // Verify document exists let retrieved_doc = client.get_document(&document.id.to_string()) .await.expect("Failed to get document"); assert!(retrieved_doc.is_some(), "Document should exist before deletion"); // Delete the document let delete_result = client.delete_document(&document.id.to_string()) .await.expect("Failed to delete document"); // Verify deletion response assert_eq!(delete_result["success"], true); assert_eq!(delete_result["document_id"], document.id.to_string()); assert_eq!(delete_result["filename"], document.filename); // Verify document no longer exists let retrieved_doc_after = client.get_document(&document.id.to_string()) .await.expect("Failed to check document existence"); assert!(retrieved_doc_after.is_none(), "Document should not exist after deletion"); println!("✅ Single document deletion test passed"); } #[tokio::test] async fn test_bulk_document_deletion_success() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_bulk_{}", Uuid::new_v4()), &format!("testuser_bulk_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Upload multiple test documents let mut document_ids = Vec::new(); for i in 1..=5 { let test_content = format!("This is test document number {}", i); let document = client.upload_document(test_content.as_bytes(), &format!("test_bulk_{}.txt", i)) .await.expect("Failed to upload document"); document_ids.push(document.id.to_string()); } println!("Uploaded {} documents for bulk deletion", document_ids.len()); // Verify all documents exist for doc_id in &document_ids { let retrieved_doc = client.get_document(doc_id) .await.expect("Failed to get document"); assert!(retrieved_doc.is_some(), "Document should exist before bulk deletion"); } // Perform bulk deletion let delete_result = client.bulk_delete_documents(&document_ids) .await.expect("Failed to bulk delete documents"); // Verify bulk deletion response assert_eq!(delete_result["success"], true); assert_eq!(delete_result["deleted_count"], 5); assert_eq!(delete_result["requested_count"], 5); let deleted_ids: Vec = delete_result["deleted_document_ids"] .as_array() .unwrap() .iter() .map(|v| v.as_str().unwrap().to_string()) .collect(); assert_eq!(deleted_ids.len(), 5); for doc_id in &document_ids { assert!(deleted_ids.contains(doc_id), "Document ID should be in deleted list"); } // Verify all documents no longer exist for doc_id in &document_ids { let retrieved_doc = client.get_document(doc_id) .await.expect("Failed to check document existence"); assert!(retrieved_doc.is_none(), "Document should not exist after bulk deletion"); } println!("✅ Bulk document deletion test passed"); } #[tokio::test] async fn test_delete_nonexistent_document() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_nonexist_{}", Uuid::new_v4()), &format!("testuser_nonexist_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Try to delete a non-existent document let fake_id = Uuid::new_v4().to_string(); let delete_result = client.delete_document(&fake_id).await; // Should return 404 error assert!(delete_result.is_err(), "Deleting non-existent document should fail"); let error_msg = delete_result.unwrap_err().to_string(); assert!(error_msg.contains("404"), "Should return 404 error for non-existent document"); println!("✅ Delete non-existent document test passed"); } #[tokio::test] async fn test_bulk_delete_empty_request() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_empty_{}", Uuid::new_v4()), &format!("testuser_empty_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Try bulk delete with empty array let delete_result = client.bulk_delete_documents(&[]) .await.expect("Bulk delete with empty array should succeed"); // Should handle empty request gracefully assert_eq!(delete_result["success"], false); assert_eq!(delete_result["deleted_count"], 0); assert!(delete_result["message"].as_str().unwrap().contains("No document IDs provided")); println!("✅ Bulk delete empty request test passed"); } #[tokio::test] async fn test_bulk_delete_mixed_existing_nonexistent() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_mixed_{}", Uuid::new_v4()), &format!("testuser_mixed_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Upload one real document let test_content = b"This is a real document."; let real_document = client.upload_document(test_content, "real_doc.txt") .await.expect("Failed to upload document"); // Create a list with real and fake IDs let fake_id = Uuid::new_v4().to_string(); let mixed_ids = vec![real_document.id.to_string(), fake_id]; // Perform bulk deletion let delete_result = client.bulk_delete_documents(&mixed_ids) .await.expect("Failed to bulk delete mixed documents"); // Should delete only the existing document assert_eq!(delete_result["success"], true); assert_eq!(delete_result["deleted_count"], 1); assert_eq!(delete_result["requested_count"], 2); let deleted_ids: Vec = delete_result["deleted_document_ids"] .as_array() .unwrap() .iter() .map(|v| v.as_str().unwrap().to_string()) .collect(); assert_eq!(deleted_ids.len(), 1); assert_eq!(deleted_ids[0], real_document.id.to_string()); // Verify real document was deleted let retrieved_doc = client.get_document(&real_document.id.to_string()) .await.expect("Failed to check document existence"); assert!(retrieved_doc.is_none(), "Real document should be deleted"); println!("✅ Bulk delete mixed existing/non-existent test passed"); } #[tokio::test] async fn test_unauthorized_deletion() { let client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Try to delete without authentication let fake_id = Uuid::new_v4().to_string(); let delete_result = client.delete_document_without_auth(&fake_id).await; // Should return 401 error assert!(delete_result.is_err(), "Unauthenticated deletion should fail"); let error_msg = delete_result.unwrap_err().to_string(); assert!(error_msg.contains("401") || error_msg.contains("Unauthorized"), "Should return 401/Unauthorized error"); println!("✅ Unauthorized deletion test passed"); } #[tokio::test] async fn test_cross_user_deletion_protection() { let mut client1 = DocumentDeletionTestClient::new(); let mut client2 = DocumentDeletionTestClient::new(); skip_if_server_down!(client1); let user1_suffix = Uuid::new_v4(); let user2_suffix = Uuid::new_v4(); // Register and login as user 1 client1.register_and_login( &format!("testuser1_{}", user1_suffix), &format!("testuser1_{}@example.com", user1_suffix), "password123", None ).await.expect("Failed to register user 1"); // Register and login as user 2 client2.register_and_login( &format!("testuser2_{}", user2_suffix), &format!("testuser2_{}@example.com", user2_suffix), "password123", None ).await.expect("Failed to register user 2"); // User 1 uploads a document let test_content = b"This is user 1's document."; let user1_document = client1.upload_document(test_content, "user1_doc.txt") .await.expect("Failed to upload document as user 1"); // User 2 tries to delete user 1's document let delete_result = client2.delete_document(&user1_document.id.to_string()).await; // Should return 404 (document not found for user 2) assert!(delete_result.is_err(), "Cross-user deletion should fail"); let error_msg = delete_result.unwrap_err().to_string(); assert!(error_msg.contains("404"), "Should return 404 for document not owned by user"); // Verify user 1's document still exists let retrieved_doc = client1.get_document(&user1_document.id.to_string()) .await.expect("Failed to check document existence"); assert!(retrieved_doc.is_some(), "User 1's document should still exist after failed cross-user deletion"); println!("✅ Cross-user deletion protection test passed"); } #[tokio::test] async fn test_admin_can_delete_any_document() { let mut user_client = DocumentDeletionTestClient::new(); let mut admin_client = DocumentDeletionTestClient::new(); skip_if_server_down!(user_client); let user_suffix = Uuid::new_v4(); let admin_suffix = Uuid::new_v4(); // Register and login as regular user user_client.register_and_login( &format!("regularuser_{}", user_suffix), &format!("regularuser_{}@example.com", user_suffix), "password123", None ).await.expect("Failed to register regular user"); // Register and login as admin admin_client.register_and_login( &format!("adminuser_{}", admin_suffix), &format!("adminuser_{}@example.com", admin_suffix), "adminpass123", Some(UserRole::Admin) ).await.expect("Failed to register admin user"); // Regular user uploads a document let test_content = b"This is a regular user's document."; let user_document = user_client.upload_document(test_content, "user_doc.txt") .await.expect("Failed to upload document as user"); // Admin deletes user's document let delete_result = admin_client.delete_document(&user_document.id.to_string()) .await.expect("Admin should be able to delete any document"); // Verify deletion response assert_eq!(delete_result["success"], true); assert_eq!(delete_result["document_id"], user_document.id.to_string()); // Verify document no longer exists let retrieved_doc = user_client.get_document(&user_document.id.to_string()) .await.expect("Failed to check document existence"); assert!(retrieved_doc.is_none(), "Document should be deleted by admin"); println!("✅ Admin can delete any document test passed"); } #[tokio::test] async fn test_document_count_updates_after_deletion() { let mut client = DocumentDeletionTestClient::new(); skip_if_server_down!(client); // Register and login client.register_and_login( &format!("testuser_count_{}", Uuid::new_v4()), &format!("testuser_count_{}@example.com", Uuid::new_v4()), "password123", None ).await.expect("Failed to register and login"); // Get initial document count let initial_list = client.list_documents() .await.expect("Failed to list documents"); let initial_count = initial_list["pagination"]["total"].as_i64().unwrap(); // Upload documents let mut document_ids = Vec::new(); for i in 1..=3 { let test_content = format!("Test document {}", i); let document = client.upload_document(test_content.as_bytes(), &format!("count_test_{}.txt", i)) .await.expect("Failed to upload document"); document_ids.push(document.id.to_string()); } // Verify count increased let after_upload_list = client.list_documents() .await.expect("Failed to list documents"); let after_upload_count = after_upload_list["pagination"]["total"].as_i64().unwrap(); assert_eq!(after_upload_count, initial_count + 3, "Document count should increase after uploads"); // Delete one document client.delete_document(&document_ids[0]) .await.expect("Failed to delete document"); // Verify count decreased by 1 let after_single_delete_list = client.list_documents() .await.expect("Failed to list documents"); let after_single_delete_count = after_single_delete_list["pagination"]["total"].as_i64().unwrap(); assert_eq!(after_single_delete_count, initial_count + 2, "Document count should decrease after single deletion"); // Bulk delete remaining documents let remaining_ids = vec![document_ids[1].clone(), document_ids[2].clone()]; client.bulk_delete_documents(&remaining_ids) .await.expect("Failed to bulk delete documents"); // Verify count is back to initial let final_list = client.list_documents() .await.expect("Failed to list documents"); let final_count = final_list["pagination"]["total"].as_i64().unwrap(); assert_eq!(final_count, initial_count, "Document count should be back to initial after bulk deletion"); println!("✅ Document count updates after deletion test passed"); } /// Test the new failed OCR document deletion endpoint #[tokio::test] async fn test_delete_failed_ocr_documents_endpoint() { let mut client = DocumentDeletionTestClient::new(); if let Err(e) = client.check_server_health().await { println!("⚠️ Server not available: {}. Skipping test.", e); return; } println!("🧪 Testing failed OCR document deletion endpoint..."); // Create and login as regular user client.create_and_login_user("failed_ocr_user", "failed_ocr_password", UserRole::User) .await.expect("Failed to create and login user"); // Preview failed documents (should return empty initially) let preview_response = client.delete_failed_ocr_documents(true) .await.expect("Failed to preview failed OCR documents"); assert_eq!(preview_response["success"], true); assert!(preview_response["matched_count"].as_i64().unwrap() >= 0); assert_eq!(preview_response["preview"], true); println!("📋 Preview request successful: {} failed documents found", preview_response["matched_count"]); // If there are failed documents, test deletion if preview_response["matched_count"].as_i64().unwrap() > 0 { // Test actual deletion let delete_response = client.delete_failed_ocr_documents(false) .await.expect("Failed to delete failed OCR documents"); assert_eq!(delete_response["success"], true); assert!(delete_response["deleted_count"].as_i64().unwrap() >= 0); assert!(delete_response.get("preview").is_none()); println!("🗑️ Successfully deleted {} failed documents", delete_response["deleted_count"]); } else { println!("ℹ️ No failed documents found to delete"); } println!("✅ Failed OCR document deletion endpoint test passed"); } /// Test confidence-based vs failed document deletion distinction #[tokio::test] async fn test_confidence_vs_failed_document_distinction() { let mut client = DocumentDeletionTestClient::new(); if let Err(e) = client.check_server_health().await { println!("⚠️ Server not available: {}. Skipping test.", e); return; } println!("🧪 Testing distinction between confidence and failed document deletion..."); // Create and login as admin to see all documents client.create_and_login_user("distinction_admin", "distinction_password", UserRole::Admin) .await.expect("Failed to create and login admin"); // Get baseline counts let initial_low_confidence = client.delete_low_confidence_documents(30.0, true) .await.expect("Failed to preview low confidence documents"); let initial_failed = client.delete_failed_ocr_documents(true) .await.expect("Failed to preview failed documents"); let initial_low_count = initial_low_confidence["matched_count"].as_i64().unwrap(); let initial_failed_count = initial_failed["matched_count"].as_i64().unwrap(); println!("📊 Initial counts - Low confidence: {}, Failed: {}", initial_low_count, initial_failed_count); // Test that the endpoints return different sets of documents // (This assumes there are some of each type in the system) // Verify that failed documents endpoint only includes failed/NULL confidence docs if initial_failed_count > 0 { let failed_docs = initial_failed["document_ids"].as_array().unwrap(); println!("🔍 Found {} failed document IDs", failed_docs.len()); } // Verify that low confidence endpoint respects threshold if initial_low_count > 0 { let low_confidence_docs = initial_low_confidence["document_ids"].as_array().unwrap(); println!("🔍 Found {} low confidence document IDs", low_confidence_docs.len()); } println!("✅ Document type distinction test passed"); } /// Test error handling for delete endpoints #[tokio::test] async fn test_delete_endpoints_error_handling() { let client = DocumentDeletionTestClient::new(); if let Err(e) = client.check_server_health().await { println!("⚠️ Server not available: {}. Skipping test.", e); return; } println!("🧪 Testing delete endpoints error handling..."); // Test unauthenticated request let failed_response = client.client .post(&format!("{}/api/documents/delete-failed-ocr", get_base_url())) .json(&json!({"preview_only": true})) .timeout(TIMEOUT) .send() .await .expect("Failed to send request"); assert_eq!(failed_response.status(), 401, "Should require authentication"); // Test invalid JSON let invalid_json_response = client.client .post(&format!("{}/api/documents/delete-failed-ocr", get_base_url())) .header("content-type", "application/json") .body("invalid json") .timeout(TIMEOUT) .send() .await .expect("Failed to send request"); assert!(invalid_json_response.status().is_client_error(), "Should reject invalid JSON"); println!("✅ Error handling test passed"); } /// Test role-based access for new delete endpoints #[tokio::test] async fn test_role_based_access_for_delete_endpoints() { let mut client = DocumentDeletionTestClient::new(); if let Err(e) = client.check_server_health().await { println!("⚠️ Server not available: {}. Skipping test.", e); return; } println!("🧪 Testing role-based access for delete endpoints..."); // Test as regular user client.create_and_login_user("delete_regular_user", "delete_password", UserRole::User) .await.expect("Failed to create and login user"); let user_response = client.delete_failed_ocr_documents(true) .await.expect("Failed to preview as user"); assert_eq!(user_response["success"], true); let user_count = user_response["matched_count"].as_i64().unwrap(); // Test as admin client.create_and_login_user("delete_admin_user", "delete_admin_password", UserRole::Admin) .await.expect("Failed to create and login admin"); let admin_response = client.delete_failed_ocr_documents(true) .await.expect("Failed to preview as admin"); assert_eq!(admin_response["success"], true); let admin_count = admin_response["matched_count"].as_i64().unwrap(); // Admin should see at least as many documents as regular user assert!(admin_count >= user_count, "Admin should see at least as many documents as user"); println!("👤 User can see {} documents, Admin can see {} documents", user_count, admin_count); println!("✅ Role-based access test passed"); } /// Test the enhanced low confidence deletion with failed documents #[tokio::test] async fn test_enhanced_low_confidence_deletion() { let mut client = DocumentDeletionTestClient::new(); if let Err(e) = client.check_server_health().await { println!("⚠️ Server not available: {}. Skipping test.", e); return; } println!("🧪 Testing enhanced low confidence deletion (includes failed docs)..."); // Create and login as admin client.create_and_login_user("enhanced_delete_admin", "enhanced_password", UserRole::Admin) .await.expect("Failed to create and login admin"); // Test with various thresholds let thresholds = vec![0.0, 30.0, 50.0, 85.0, 100.0]; for threshold in thresholds { let response = client.delete_low_confidence_documents(threshold, true) .await.expect(&format!("Failed to preview with threshold {}", threshold)); assert_eq!(response["success"], true); let count = response["matched_count"].as_i64().unwrap(); println!("🎯 Threshold {}%: {} documents would be deleted", threshold, count); // Verify response format assert!(response.get("document_ids").is_some()); assert_eq!(response["preview"], true); } // Test that higher thresholds generally include more documents let low_threshold_response = client.delete_low_confidence_documents(10.0, true) .await.expect("Failed to preview with low threshold"); let high_threshold_response = client.delete_low_confidence_documents(90.0, true) .await.expect("Failed to preview with high threshold"); let low_count = low_threshold_response["matched_count"].as_i64().unwrap(); let high_count = high_threshold_response["matched_count"].as_i64().unwrap(); assert!(high_count >= low_count, "Higher threshold should include at least as many documents as lower threshold"); println!("✅ Enhanced low confidence deletion test passed"); }