Readur/tests/integration_document_routes...

952 lines
38 KiB
Rust

#[cfg(test)]
mod document_routes_deletion_tests {
use readur::models::{UserRole, User, Document, AuthProvider};
use readur::routes::documents::{BulkDeleteRequest};
use axum::http::StatusCode;
use chrono::Utc;
use serde_json::json;
use uuid::Uuid;
// Mock implementations for testing
struct MockAppState {
// Add fields that AppState would have for testing
pub delete_results: std::collections::HashMap<Uuid, bool>,
pub bulk_delete_results: std::collections::HashMap<Vec<Uuid>, Vec<Document>>,
}
impl MockAppState {
fn new() -> Self {
Self {
delete_results: std::collections::HashMap::new(),
bulk_delete_results: std::collections::HashMap::new(),
}
}
}
fn create_test_user(role: UserRole) -> User {
User {
id: Uuid::new_v4(),
username: "testuser".to_string(),
email: "test@example.com".to_string(),
password_hash: Some("hashed_password".to_string()),
role,
created_at: Utc::now(),
updated_at: Utc::now(),
oidc_subject: None,
oidc_issuer: None,
oidc_email: None,
auth_provider: AuthProvider::Local,
}
}
fn create_test_document(user_id: Uuid) -> Document {
Document {
id: Uuid::new_v4(),
filename: "test_document.pdf".to_string(),
original_filename: "test_document.pdf".to_string(),
file_path: "/uploads/test_document.pdf".to_string(),
file_size: 1024,
mime_type: "application/pdf".to_string(),
content: Some("Test document content".to_string()),
ocr_text: Some("This is extracted OCR text".to_string()),
ocr_confidence: Some(95.5),
ocr_word_count: Some(150),
ocr_processing_time_ms: Some(1200),
ocr_status: Some("completed".to_string()),
ocr_error: None,
ocr_completed_at: Some(Utc::now()),
tags: vec!["test".to_string()],
created_at: Utc::now(),
updated_at: Utc::now(),
user_id,
file_hash: Some("hash123".to_string()),
original_created_at: None,
original_modified_at: None,
source_path: None,
source_type: None,
source_id: None,
file_permissions: None,
file_owner: None,
file_group: None,
source_metadata: None,
ocr_retry_count: None,
ocr_failure_reason: None,
}
}
#[test]
fn test_bulk_delete_request_serialization() {
let request = BulkDeleteRequest {
document_ids: vec![Uuid::new_v4(), Uuid::new_v4()],
};
// Test serialization
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("document_ids"));
// Test deserialization
let deserialized: BulkDeleteRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.document_ids.len(), 2);
assert_eq!(deserialized.document_ids, request.document_ids);
}
#[test]
fn test_bulk_delete_request_empty_list() {
let request = BulkDeleteRequest {
document_ids: vec![],
};
let json = serde_json::to_string(&request).unwrap();
let deserialized: BulkDeleteRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.document_ids.len(), 0);
}
#[test]
fn test_bulk_delete_request_validation() {
// Test with valid UUIDs
let valid_request = json!({
"document_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001"
]
});
let result: Result<BulkDeleteRequest, _> = serde_json::from_value(valid_request);
assert!(result.is_ok());
assert_eq!(result.unwrap().document_ids.len(), 2);
// Test with invalid UUIDs should fail
let invalid_request = json!({
"document_ids": ["not-a-uuid", "also-not-a-uuid"]
});
let result: Result<BulkDeleteRequest, _> = serde_json::from_value(invalid_request);
assert!(result.is_err());
}
#[test]
fn test_auth_user_role_permissions() {
let user = create_test_user(UserRole::User);
let admin = create_test_user(UserRole::Admin);
// Test user role
assert_eq!(user.role, UserRole::User);
assert_ne!(user.role, UserRole::Admin);
// Test admin role
assert_eq!(admin.role, UserRole::Admin);
assert_ne!(admin.role, UserRole::User);
}
#[test]
fn test_document_deletion_authorization_logic() {
let user1 = create_test_user(UserRole::User);
let user2 = create_test_user(UserRole::User);
let admin = create_test_user(UserRole::Admin);
let document = create_test_document(user1.id);
// User1 should be able to delete their own document
let can_delete_own = document.user_id == user1.id || user1.role == UserRole::Admin;
assert!(can_delete_own);
// User2 should not be able to delete user1's document
let can_delete_other = document.user_id == user2.id || user2.role == UserRole::Admin;
assert!(!can_delete_other);
// Admin should be able to delete any document
let admin_can_delete = document.user_id == admin.id || admin.role == UserRole::Admin;
assert!(admin_can_delete);
}
#[test]
fn test_bulk_delete_authorization_logic() {
let user1 = create_test_user(UserRole::User);
let user2 = create_test_user(UserRole::User);
let admin = create_test_user(UserRole::Admin);
let doc1_user1 = create_test_document(user1.id);
let doc2_user1 = create_test_document(user1.id);
let doc1_user2 = create_test_document(user2.id);
let all_documents = vec![&doc1_user1, &doc2_user1, &doc1_user2];
// Test what user1 can delete
let user1_can_delete: Vec<&Document> = all_documents
.iter()
.filter(|doc| doc.user_id == user1.id || user1.role == UserRole::Admin)
.cloned()
.collect();
assert_eq!(user1_can_delete.len(), 2); // Only their own documents
// Test what admin can delete
let admin_can_delete: Vec<&Document> = all_documents
.iter()
.filter(|doc| doc.user_id == admin.id || admin.role == UserRole::Admin)
.cloned()
.collect();
assert_eq!(admin_can_delete.len(), 3); // All documents
}
#[test]
fn test_document_response_format() {
let user = create_test_user(UserRole::User);
let document = create_test_document(user.id);
// Test successful deletion response format
let success_response = json!({
"success": true,
"message": "Document deleted successfully",
"id": document.id.to_string()
});
assert_eq!(success_response["success"], true);
assert!(success_response["message"].is_string());
assert_eq!(success_response["id"], document.id.to_string());
// Test error response format
let error_response = json!({
"success": false,
"error": "Document not found or not authorized to delete"
});
assert_eq!(error_response["success"], false);
assert!(error_response["error"].is_string());
}
#[test]
fn test_bulk_delete_response_format() {
let user = create_test_user(UserRole::User);
let doc1 = create_test_document(user.id);
let doc2 = create_test_document(user.id);
// Test successful bulk deletion response format
let success_response = json!({
"success": true,
"message": "2 documents deleted successfully",
"deleted_count": 2,
"deleted_documents": [
{
"id": doc1.id.to_string(),
"filename": doc1.filename
},
{
"id": doc2.id.to_string(),
"filename": doc2.filename
}
]
});
assert_eq!(success_response["success"], true);
assert_eq!(success_response["deleted_count"], 2);
assert!(success_response["deleted_documents"].is_array());
assert_eq!(success_response["deleted_documents"].as_array().unwrap().len(), 2);
// Test partial success response format
let partial_response = json!({
"success": true,
"message": "1 of 2 documents deleted successfully",
"deleted_count": 1,
"requested_count": 2,
"deleted_documents": [
{
"id": doc1.id.to_string(),
"filename": doc1.filename
}
]
});
assert_eq!(partial_response["success"], true);
assert_eq!(partial_response["deleted_count"], 1);
assert_eq!(partial_response["requested_count"], 2);
}
#[test]
fn test_http_status_codes() {
// Test successful deletion status codes
assert_eq!(StatusCode::OK.as_u16(), 200);
// Test error status codes
assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
assert_eq!(StatusCode::UNAUTHORIZED.as_u16(), 401);
assert_eq!(StatusCode::FORBIDDEN.as_u16(), 403);
assert_eq!(StatusCode::BAD_REQUEST.as_u16(), 400);
assert_eq!(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), 500);
}
#[test]
fn test_path_parameter_parsing() {
let document_id = Uuid::new_v4();
let _path_str = format!("/documents/{}", document_id);
// Test that UUID can be parsed from path
let parsed_id = document_id.to_string();
let reparsed_id = Uuid::parse_str(&parsed_id).unwrap();
assert_eq!(reparsed_id, document_id);
}
#[test]
fn test_json_request_validation() {
// Test valid JSON request
let valid_json = json!({
"document_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001"
]
});
let result: Result<BulkDeleteRequest, _> = serde_json::from_value(valid_json);
assert!(result.is_ok());
// Test invalid JSON structure
let invalid_json = json!({
"wrong_field": ["not-document-ids"]
});
let result: Result<BulkDeleteRequest, _> = serde_json::from_value(invalid_json);
assert!(result.is_err());
// Test empty request
let empty_json = json!({
"document_ids": []
});
let result: Result<BulkDeleteRequest, _> = serde_json::from_value(empty_json);
assert!(result.is_ok());
assert_eq!(result.unwrap().document_ids.len(), 0);
}
#[test]
fn test_concurrent_deletion_safety() {
let user = create_test_user(UserRole::User);
let document = create_test_document(user.id);
// Test that multiple deletion attempts for the same document
// should be handled gracefully (first succeeds, subsequent ones are no-op)
let document_id = document.id;
// Simulate concurrent deletions by checking if the same document ID
// would be processed multiple times
let mut processed_ids = std::collections::HashSet::new();
// First deletion attempt
let first_attempt = processed_ids.insert(document_id);
assert!(first_attempt); // Should be true (new entry)
// Second deletion attempt
let second_attempt = processed_ids.insert(document_id);
assert!(!second_attempt); // Should be false (already exists)
}
#[test]
fn test_bulk_delete_request_size_limits() {
// Test reasonable request size
let reasonable_request = BulkDeleteRequest {
document_ids: (0..10).map(|_| Uuid::new_v4()).collect(),
};
assert_eq!(reasonable_request.document_ids.len(), 10);
// Test large request size (should still be valid but might be rate-limited in real app)
let large_request = BulkDeleteRequest {
document_ids: (0..100).map(|_| Uuid::new_v4()).collect(),
};
assert_eq!(large_request.document_ids.len(), 100);
// Test very large request size (might need limits in production)
let very_large_request = BulkDeleteRequest {
document_ids: (0..1000).map(|_| Uuid::new_v4()).collect(),
};
assert_eq!(very_large_request.document_ids.len(), 1000);
}
#[test]
fn test_error_message_formats() {
// Test error messages for different scenarios
let not_found_error = "Document not found";
let unauthorized_error = "Not authorized to delete this document";
let validation_error = "Invalid request format";
let server_error = "Internal server error occurred during deletion";
assert!(!not_found_error.is_empty());
assert!(!unauthorized_error.is_empty());
assert!(!validation_error.is_empty());
assert!(!server_error.is_empty());
// Test that error messages are user-friendly
assert!(!not_found_error.contains("SQL"));
assert!(!not_found_error.contains("database"));
assert!(!unauthorized_error.contains("403"));
assert!(!validation_error.contains("serde"));
}
// Low confidence deletion tests
mod low_confidence_deletion_tests {
use super::*;
use readur::routes::documents::DeleteLowConfidenceRequest;
fn create_low_confidence_document(user_id: Uuid, confidence: f32) -> Document {
Document {
id: Uuid::new_v4(),
filename: format!("low_conf_{}.pdf", confidence),
original_filename: format!("low_conf_{}.pdf", confidence),
file_path: format!("/uploads/low_conf_{}.pdf", confidence),
file_size: 1024,
mime_type: "application/pdf".to_string(),
content: Some("Test document content".to_string()),
ocr_text: Some("Low quality OCR text".to_string()),
ocr_confidence: Some(confidence),
ocr_word_count: Some(10),
ocr_processing_time_ms: Some(500),
ocr_status: Some("completed".to_string()),
ocr_error: None,
ocr_completed_at: Some(Utc::now()),
tags: vec!["low-confidence".to_string()],
created_at: Utc::now(),
updated_at: Utc::now(),
user_id,
file_hash: Some("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890".to_string()),
original_created_at: None,
original_modified_at: None,
source_metadata: None,
ocr_retry_count: None,
ocr_failure_reason: None,
}
}
#[test]
fn test_delete_low_confidence_request_serialization() {
// Test valid request
let valid_request = json!({
"max_confidence": 50.0,
"preview_only": true
});
let result: Result<DeleteLowConfidenceRequest, _> = serde_json::from_value(valid_request);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.max_confidence, 50.0);
assert_eq!(request.preview_only, Some(true));
// Test request with only max_confidence
let minimal_request = json!({
"max_confidence": 30.0
});
let result: Result<DeleteLowConfidenceRequest, _> = serde_json::from_value(minimal_request);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.max_confidence, 30.0);
assert_eq!(request.preview_only, None);
}
#[test]
fn test_delete_low_confidence_request_validation() {
// Test invalid confidence values
let invalid_negative = json!({
"max_confidence": -10.0,
"preview_only": false
});
let result: Result<DeleteLowConfidenceRequest, _> = serde_json::from_value(invalid_negative);
assert!(result.is_ok()); // Serialization succeeds, validation happens in handler
let invalid_too_high = json!({
"max_confidence": 150.0,
"preview_only": false
});
let result: Result<DeleteLowConfidenceRequest, _> = serde_json::from_value(invalid_too_high);
assert!(result.is_ok()); // Serialization succeeds, validation happens in handler
}
#[test]
fn test_confidence_threshold_logic() {
let user = create_test_user(UserRole::User);
// Create documents with various confidence levels
let high_confidence_doc = create_low_confidence_document(user.id, 95.0);
let medium_confidence_doc = create_low_confidence_document(user.id, 60.0);
let low_confidence_doc = create_low_confidence_document(user.id, 25.0);
let very_low_confidence_doc = create_low_confidence_document(user.id, 5.0);
let documents = vec![
&high_confidence_doc,
&medium_confidence_doc,
&low_confidence_doc,
&very_low_confidence_doc
];
// Test threshold logic for different confidence values
let threshold_50 = 50.0;
let threshold_30 = 30.0;
let threshold_10 = 10.0;
// Documents below 50% threshold
let below_50: Vec<_> = documents.iter()
.filter(|doc| doc.ocr_confidence.unwrap_or(0.0) < threshold_50)
.collect();
assert_eq!(below_50.len(), 2); // 25.0 and 5.0
// Documents below 30% threshold
let below_30: Vec<_> = documents.iter()
.filter(|doc| doc.ocr_confidence.unwrap_or(0.0) < threshold_30)
.collect();
assert_eq!(below_30.len(), 2); // 25.0 and 5.0
// Documents below 10% threshold
let below_10: Vec<_> = documents.iter()
.filter(|doc| doc.ocr_confidence.unwrap_or(0.0) < threshold_10)
.collect();
assert_eq!(below_10.len(), 1); // 5.0
}
#[test]
fn test_user_role_authorization_for_low_confidence_deletion() {
let user1 = create_test_user(UserRole::User);
let user2 = create_test_user(UserRole::User);
let admin = create_test_user(UserRole::Admin);
let user1_doc = create_low_confidence_document(user1.id, 25.0);
let user2_doc = create_low_confidence_document(user2.id, 15.0);
// User1 should only be able to delete their own low confidence documents
assert_eq!(user1_doc.user_id, user1.id);
assert_ne!(user1_doc.user_id, user2.id);
// User2 should only be able to delete their own low confidence documents
assert_eq!(user2_doc.user_id, user2.id);
assert_ne!(user2_doc.user_id, user1.id);
// Admin should be able to delete any low confidence documents
let admin_can_delete_user1 = user1_doc.user_id == admin.id || admin.role == UserRole::Admin;
let admin_can_delete_user2 = user2_doc.user_id == admin.id || admin.role == UserRole::Admin;
assert!(admin_can_delete_user1);
assert!(admin_can_delete_user2);
}
#[test]
fn test_edge_cases_for_confidence_values() {
let user = create_test_user(UserRole::User);
// Test document with None confidence (should not be included)
let mut no_confidence_doc = create_low_confidence_document(user.id, 0.0);
no_confidence_doc.ocr_confidence = None;
// Test document with exactly threshold confidence (should not be included)
let exact_threshold_doc = create_low_confidence_document(user.id, 30.0);
// Test document just below threshold (should be included)
let just_below_doc = create_low_confidence_document(user.id, 29.9);
let threshold = 30.0;
// None confidence should be excluded (no OCR confidence available)
assert!(no_confidence_doc.ocr_confidence.is_none());
// Exact threshold should be excluded (not less than threshold)
assert_eq!(exact_threshold_doc.ocr_confidence.unwrap(), threshold);
assert!(!(exact_threshold_doc.ocr_confidence.unwrap() < threshold));
// Just below threshold should be included
assert!(just_below_doc.ocr_confidence.unwrap() < threshold);
}
#[test]
fn test_preview_mode_behavior() {
let user = create_test_user(UserRole::User);
let doc1 = create_low_confidence_document(user.id, 20.0);
let doc2 = create_low_confidence_document(user.id, 10.0);
let preview_request = DeleteLowConfidenceRequest {
max_confidence: 30.0,
preview_only: Some(true),
};
let delete_request = DeleteLowConfidenceRequest {
max_confidence: 30.0,
preview_only: Some(false),
};
let no_preview_request = DeleteLowConfidenceRequest {
max_confidence: 30.0,
preview_only: None,
};
// Preview mode should be true when explicitly set
assert_eq!(preview_request.preview_only.unwrap_or(false), true);
// Delete mode should be false when explicitly set
assert_eq!(delete_request.preview_only.unwrap_or(false), false);
// Default should be false when not specified
assert_eq!(no_preview_request.preview_only.unwrap_or(false), false);
}
#[test]
fn test_response_format_expectations() {
// Test expected response structure for preview mode
let expected_preview_response = json!({
"success": true,
"message": "Found 5 documents with OCR confidence below 30%",
"matched_count": 5,
"preview": true,
"document_ids": ["uuid1", "uuid2", "uuid3", "uuid4", "uuid5"]
});
// Test expected response structure for delete mode
let expected_delete_response = json!({
"success": true,
"message": "Successfully deleted 5 documents with OCR confidence below 30%",
"deleted_count": 5,
"matched_count": 5,
"successful_file_deletions": 5,
"failed_file_deletions": 0,
"ignored_file_creation_failures": 0,
"deleted_document_ids": ["uuid1", "uuid2", "uuid3", "uuid4", "uuid5"]
});
// Verify JSON structure is valid
assert!(expected_preview_response.is_object());
assert!(expected_delete_response.is_object());
// Verify required fields exist
assert!(expected_preview_response["success"].is_boolean());
assert!(expected_preview_response["matched_count"].is_number());
assert!(expected_preview_response["document_ids"].is_array());
assert!(expected_delete_response["success"].is_boolean());
assert!(expected_delete_response["deleted_count"].is_number());
assert!(expected_delete_response["deleted_document_ids"].is_array());
}
#[test]
fn test_error_scenarios() {
// Test validation error for invalid confidence range
let invalid_confidence_cases = vec![
(-1.0, "negative confidence"),
(101.0, "confidence over 100"),
(150.5, "way over 100"),
];
for (confidence, description) in invalid_confidence_cases {
let request = DeleteLowConfidenceRequest {
max_confidence: confidence,
preview_only: Some(false),
};
// Validation logic should catch these in the handler
assert!(confidence < 0.0 || confidence > 100.0,
"Should be invalid: {}", description);
}
// Test empty result scenario
let request = DeleteLowConfidenceRequest {
max_confidence: 0.0, // Very low threshold, should match nothing
preview_only: Some(true),
};
assert_eq!(request.max_confidence, 0.0);
// This should result in zero matched documents
}
}
#[cfg(test)]
mod delete_failed_ocr_tests {
use super::*;
use serde_json::json;
#[test]
fn test_delete_failed_ocr_request_serialization() {
// Test preview mode
let preview_request = json!({
"preview_only": true
});
let parsed: serde_json::Value = serde_json::from_value(preview_request).unwrap();
assert_eq!(parsed["preview_only"], true);
// Test delete mode
let delete_request = json!({
"preview_only": false
});
let parsed: serde_json::Value = serde_json::from_value(delete_request).unwrap();
assert_eq!(parsed["preview_only"], false);
// Test empty request (should default to preview_only: false)
let empty_request = json!({});
let parsed: serde_json::Value = serde_json::from_value(empty_request).unwrap();
assert!(parsed.get("preview_only").is_none() || parsed["preview_only"] == false);
}
#[test]
fn test_delete_failed_ocr_user_authorization() {
let admin_user = create_test_user(UserRole::Admin);
let regular_user = create_test_user(UserRole::User);
// Both admins and regular users should be able to delete their own failed documents
assert_eq!(admin_user.role, UserRole::Admin);
assert_eq!(regular_user.role, UserRole::User);
// Admin should be able to see all failed documents
// Regular user should only see their own failed documents
// This logic would be tested in the actual endpoint implementation
}
#[test]
fn test_failed_document_criteria() {
let user_id = Uuid::new_v4();
// Test document with failed OCR status
let mut failed_doc = create_test_document(user_id);
failed_doc.ocr_status = Some("failed".to_string());
failed_doc.ocr_confidence = None;
failed_doc.ocr_error = Some("OCR processing failed".to_string());
// Should be included in failed document deletion
assert_eq!(failed_doc.ocr_status, Some("failed".to_string()));
assert!(failed_doc.ocr_confidence.is_none());
// Test document with NULL confidence but completed status
let mut null_confidence_doc = create_test_document(user_id);
null_confidence_doc.ocr_status = Some("completed".to_string());
null_confidence_doc.ocr_confidence = None;
null_confidence_doc.ocr_text = Some("Text but no confidence".to_string());
// Should be included in failed document deletion (NULL confidence indicates failure)
assert_eq!(null_confidence_doc.ocr_status, Some("completed".to_string()));
assert!(null_confidence_doc.ocr_confidence.is_none());
// Test document with successful OCR
let mut success_doc = create_test_document(user_id);
success_doc.ocr_status = Some("completed".to_string());
success_doc.ocr_confidence = Some(85.0);
success_doc.ocr_text = Some("Successfully extracted text".to_string());
// Should NOT be included in failed document deletion
assert_eq!(success_doc.ocr_status, Some("completed".to_string()));
assert!(success_doc.ocr_confidence.is_some());
// Test document with pending status
let mut pending_doc = create_test_document(user_id);
pending_doc.ocr_status = Some("pending".to_string());
pending_doc.ocr_confidence = None;
// Should NOT be included in failed document deletion (still processing)
assert_eq!(pending_doc.ocr_status, Some("pending".to_string()));
// Test document with processing status
let mut processing_doc = create_test_document(user_id);
processing_doc.ocr_status = Some("processing".to_string());
processing_doc.ocr_confidence = None;
// Should NOT be included in failed document deletion (still processing)
assert_eq!(processing_doc.ocr_status, Some("processing".to_string()));
}
#[test]
fn test_delete_failed_ocr_response_format() {
// Test preview response format
let preview_response = json!({
"success": true,
"message": "Found 5 documents with failed OCR processing",
"matched_count": 5,
"preview": true,
"document_ids": ["id1", "id2", "id3", "id4", "id5"]
});
assert_eq!(preview_response["success"], true);
assert_eq!(preview_response["matched_count"], 5);
assert_eq!(preview_response["preview"], true);
assert!(preview_response["document_ids"].is_array());
// Test delete response format
let delete_response = json!({
"success": true,
"message": "Successfully deleted 3 documents with failed OCR processing",
"deleted_count": 3,
"matched_count": 3,
"successful_file_deletions": 3,
"failed_file_deletions": 0,
"ignored_file_creation_failures": 0,
"deleted_document_ids": ["id1", "id2", "id3"]
});
assert_eq!(delete_response["success"], true);
assert_eq!(delete_response["deleted_count"], 3);
assert_eq!(delete_response["matched_count"], 3);
assert!(delete_response["deleted_document_ids"].is_array());
assert!(delete_response.get("preview").is_none()); // Should not have preview flag in delete response
// Test no documents found response
let no_docs_response = json!({
"success": true,
"message": "No documents found with failed OCR processing",
"deleted_count": 0
});
assert_eq!(no_docs_response["success"], true);
assert_eq!(no_docs_response["deleted_count"], 0);
}
#[test]
fn test_delete_failed_ocr_error_scenarios() {
// Test with no failed documents
let no_failed_docs_request = json!({
"preview_only": true
});
// Should return success with 0 matched count
// This would be tested in integration tests with actual database
// Test with file deletion failures
let file_deletion_error = json!({
"success": true,
"message": "Successfully deleted 2 documents with failed OCR processing",
"deleted_count": 2,
"matched_count": 2,
"successful_file_deletions": 1,
"failed_file_deletions": 1,
"ignored_file_creation_failures": 0,
"deleted_document_ids": ["id1", "id2"]
});
// Should still report success but indicate file deletion issues
assert_eq!(file_deletion_error["success"], true);
assert_eq!(file_deletion_error["failed_file_deletions"], 1);
// Test with ignored file creation failures
let ignored_file_error = json!({
"success": true,
"message": "Successfully deleted 2 documents with failed OCR processing",
"deleted_count": 2,
"matched_count": 2,
"successful_file_deletions": 2,
"failed_file_deletions": 0,
"ignored_file_creation_failures": 1,
"deleted_document_ids": ["id1", "id2"]
});
assert_eq!(ignored_file_error["success"], true);
assert_eq!(ignored_file_error["ignored_file_creation_failures"], 1);
}
#[test]
fn test_delete_failed_ocr_failure_reason_handling() {
let user_id = Uuid::new_v4();
// Test document with specific failure reason
let mut ocr_timeout_doc = create_test_document(user_id);
ocr_timeout_doc.ocr_status = Some("failed".to_string());
ocr_timeout_doc.ocr_error = Some("OCR processing timed out after 2 minutes".to_string());
// Test document with corruption error
let mut corruption_doc = create_test_document(user_id);
corruption_doc.ocr_status = Some("failed".to_string());
corruption_doc.ocr_error = Some("Invalid image format - file appears corrupted".to_string());
// Test document with font encoding error
let mut font_error_doc = create_test_document(user_id);
font_error_doc.ocr_status = Some("failed".to_string());
font_error_doc.ocr_error = Some("PDF text extraction failed due to font encoding issues".to_string());
// All should be valid candidates for deletion
assert!(ocr_timeout_doc.ocr_error.is_some());
assert!(corruption_doc.ocr_error.is_some());
assert!(font_error_doc.ocr_error.is_some());
// The deletion should create appropriate ignored file records with the error reasons
}
#[test]
fn test_delete_failed_ocr_ignored_file_creation() {
// Test that deleted failed documents create proper ignored file records
let user_id = Uuid::new_v4();
let mut failed_doc = create_test_document(user_id);
failed_doc.ocr_status = Some("failed".to_string());
failed_doc.ocr_error = Some("OCR processing failed due to corrupted image".to_string());
// Expected ignored file reason should include the error
let expected_reason = "deleted due to failed OCR processing: OCR processing failed due to corrupted image";
// In the actual implementation, this would be tested by verifying the ignored file record
assert!(failed_doc.ocr_error.is_some());
// Test document with no specific error
let mut failed_no_error_doc = create_test_document(user_id);
failed_no_error_doc.ocr_status = Some("failed".to_string());
failed_no_error_doc.ocr_error = None;
// Should use generic reason
let expected_generic_reason = "deleted due to failed OCR processing";
// Both should result in appropriate ignored file records
assert_eq!(failed_doc.ocr_status, Some("failed".to_string()));
assert_eq!(failed_no_error_doc.ocr_status, Some("failed".to_string()));
}
#[test]
fn test_delete_failed_ocr_vs_low_confidence_distinction() {
let user_id = Uuid::new_v4();
// Failed OCR document (should be in failed deletion, not low confidence)
let mut failed_doc = create_test_document(user_id);
failed_doc.ocr_status = Some("failed".to_string());
failed_doc.ocr_confidence = None;
// Low confidence document (should be in low confidence deletion, not failed)
let mut low_confidence_doc = create_test_document(user_id);
low_confidence_doc.ocr_status = Some("completed".to_string());
low_confidence_doc.ocr_confidence = Some(25.0);
// NULL confidence but completed (edge case - should be in failed deletion)
let mut null_confidence_doc = create_test_document(user_id);
null_confidence_doc.ocr_status = Some("completed".to_string());
null_confidence_doc.ocr_confidence = None;
// High confidence document (should be in neither)
let mut high_confidence_doc = create_test_document(user_id);
high_confidence_doc.ocr_status = Some("completed".to_string());
high_confidence_doc.ocr_confidence = Some(95.0);
// Verify the logic for each type
assert_eq!(failed_doc.ocr_status, Some("failed".to_string()));
assert!(failed_doc.ocr_confidence.is_none());
assert_eq!(low_confidence_doc.ocr_status, Some("completed".to_string()));
assert!(low_confidence_doc.ocr_confidence.unwrap() < 50.0);
assert_eq!(null_confidence_doc.ocr_status, Some("completed".to_string()));
assert!(null_confidence_doc.ocr_confidence.is_none());
assert_eq!(high_confidence_doc.ocr_status, Some("completed".to_string()));
assert!(high_confidence_doc.ocr_confidence.unwrap() > 50.0);
}
#[test]
fn test_delete_failed_ocr_endpoint_path() {
// Test that the endpoint path is correct
let endpoint_path = "/api/documents/delete-failed-ocr";
// This would be used in integration tests
assert!(endpoint_path.contains("delete-failed-ocr"));
assert!(endpoint_path.starts_with("/api/documents/"));
}
#[test]
fn test_delete_failed_ocr_http_methods() {
// The endpoint should only accept POST requests
// GET, PUT, DELETE should not be allowed
// This would be tested in integration tests with actual HTTP requests
let allowed_method = "POST";
let disallowed_methods = vec!["GET", "PUT", "DELETE", "PATCH"];
assert_eq!(allowed_method, "POST");
assert!(disallowed_methods.contains(&"GET"));
assert!(disallowed_methods.contains(&"DELETE"));
}
}
}