fix(server): resolve NUMERIC db type and f64 rust type

This commit is contained in:
perf3ct 2025-07-02 02:26:11 +00:00
parent 05c04f242e
commit 68aa492a96
3 changed files with 98 additions and 20 deletions

View File

@ -335,10 +335,19 @@ pub async fn get_ocr_retry_stats(
let failure_reasons: Vec<serde_json::Value> = failure_stats.into_iter() let failure_reasons: Vec<serde_json::Value> = failure_stats.into_iter()
.map(|row| { .map(|row| {
// Handle NUMERIC type from database by trying different types
let avg_file_size_mb = if let Ok(val) = row.try_get::<f64, _>("avg_file_size") {
val / 1_048_576.0
} else if let Ok(val) = row.try_get::<i64, _>("avg_file_size") {
val as f64 / 1_048_576.0
} else {
0.0
};
serde_json::json!({ serde_json::json!({
"reason": row.get::<Option<String>, _>("ocr_failure_reason").unwrap_or_else(|| "unknown".to_string()), "reason": row.get::<Option<String>, _>("ocr_failure_reason").unwrap_or_else(|| "unknown".to_string()),
"count": row.get::<i64, _>("count"), "count": row.get::<i64, _>("count"),
"avg_file_size_mb": row.get::<Option<f64>, _>("avg_file_size").unwrap_or(0.0) / 1_048_576.0, "avg_file_size_mb": avg_file_size_mb,
"first_occurrence": row.get::<chrono::DateTime<chrono::Utc>, _>("first_occurrence"), "first_occurrence": row.get::<chrono::DateTime<chrono::Utc>, _>("first_occurrence"),
"last_occurrence": row.get::<chrono::DateTime<chrono::Utc>, _>("last_occurrence"), "last_occurrence": row.get::<chrono::DateTime<chrono::Utc>, _>("last_occurrence"),
}) })
@ -347,10 +356,19 @@ pub async fn get_ocr_retry_stats(
let file_types: Vec<serde_json::Value> = type_stats.into_iter() let file_types: Vec<serde_json::Value> = type_stats.into_iter()
.map(|row| { .map(|row| {
// Handle NUMERIC type from database by trying different types
let avg_file_size_mb = if let Ok(val) = row.try_get::<f64, _>("avg_file_size") {
val / 1_048_576.0
} else if let Ok(val) = row.try_get::<i64, _>("avg_file_size") {
val as f64 / 1_048_576.0
} else {
0.0
};
serde_json::json!({ serde_json::json!({
"mime_type": row.get::<String, _>("mime_type"), "mime_type": row.get::<String, _>("mime_type"),
"count": row.get::<i64, _>("count"), "count": row.get::<i64, _>("count"),
"avg_file_size_mb": row.get::<Option<f64>, _>("avg_file_size").unwrap_or(0.0) / 1_048_576.0, "avg_file_size_mb": avg_file_size_mb,
}) })
}) })
.collect(); .collect();

View File

@ -306,7 +306,16 @@ impl OcrRetryService {
.map(|row| FailureStatistic { .map(|row| FailureStatistic {
reason: row.get::<String, _>("reason"), reason: row.get::<String, _>("reason"),
count: row.get::<i64, _>("count"), count: row.get::<i64, _>("count"),
avg_file_size_mb: row.get::<Option<f64>, _>("avg_file_size").unwrap_or(0.0) / 1_048_576.0, avg_file_size_mb: {
// Handle NUMERIC type from database by trying different types
if let Ok(val) = row.try_get::<f64, _>("avg_file_size") {
val / 1_048_576.0
} else if let Ok(val) = row.try_get::<i64, _>("avg_file_size") {
val as f64 / 1_048_576.0
} else {
0.0
}
},
recent_failures: row.get::<i64, _>("recent_failures"), recent_failures: row.get::<i64, _>("recent_failures"),
}) })
.collect(); .collect();

View File

@ -20,16 +20,30 @@ impl OcrRetryTestHelper {
async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> { async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let client = Client::new(); let client = Client::new();
// First check if server is running // First check if server is running with better error handling
let health_check = client let health_check = client
.get(&format!("{}/api/health", get_base_url())) .get(&format!("{}/api/health", get_base_url()))
.timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(10))
.send() .send()
.await; .await;
if let Err(e) = health_check { match health_check {
eprintln!("Health check failed: {}. Is the server running at {}?", e, get_base_url()); Ok(response) => {
return Err(format!("Server not running: {}", e).into()); if !response.status().is_success() {
let status = response.status();
let text = response.text().await.unwrap_or_else(|_| "Unable to read response".to_string());
return Err(format!("Health check failed with status {}: {}. Is the server running at {}?", status, text, get_base_url()).into());
}
println!("✅ Server health check passed at {}", get_base_url());
}
Err(e) => {
eprintln!("❌ Cannot connect to server at {}: {}", get_base_url(), e);
eprintln!("💡 To run integration tests, start the server first:");
eprintln!(" cargo run");
eprintln!(" Then run tests in another terminal:");
eprintln!(" cargo test --test integration_ocr_retry_tests");
return Err(format!("Server not reachable: {}", e).into());
}
} }
// Create a test admin user // Create a test admin user
@ -96,12 +110,23 @@ impl OcrRetryTestHelper {
.send() .send()
.await?; .await?;
if !response.status().is_success() { let status = response.status();
return Err(format!("Failed to get retry stats: {}", response.text().await?).into()); let response_text = response.text().await?;
if !status.is_success() {
return Err(format!("Failed to get retry stats (status {}): {}", status, response_text).into());
} }
let result: Value = response.json().await?; // Try to parse the JSON and provide better error messages
Ok(result) match serde_json::from_str::<Value>(&response_text) {
Ok(result) => Ok(result),
Err(e) => {
eprintln!("JSON parsing failed for retry stats response:");
eprintln!("Status: {}", status);
eprintln!("Response text: {}", response_text);
Err(format!("Failed to parse JSON response: {}. Raw response: {}", e, response_text).into())
}
}
} }
async fn get_retry_recommendations(&self) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> { async fn get_retry_recommendations(&self) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
@ -112,12 +137,23 @@ impl OcrRetryTestHelper {
.send() .send()
.await?; .await?;
if !response.status().is_success() { let status = response.status();
return Err(format!("Failed to get retry recommendations: {}", response.text().await?).into()); let response_text = response.text().await?;
if !status.is_success() {
return Err(format!("Failed to get retry recommendations (status {}): {}", status, response_text).into());
} }
let result: Value = response.json().await?; // Try to parse the JSON and provide better error messages
Ok(result) match serde_json::from_str::<Value>(&response_text) {
Ok(result) => Ok(result),
Err(e) => {
eprintln!("JSON parsing failed for retry recommendations response:");
eprintln!("Status: {}", status);
eprintln!("Response text: {}", response_text);
Err(format!("Failed to parse JSON response: {}. Raw response: {}", e, response_text).into())
}
}
} }
async fn bulk_retry_ocr(&self, mode: &str, document_ids: Option<Vec<String>>, preview_only: bool) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> { async fn bulk_retry_ocr(&self, mode: &str, document_ids: Option<Vec<String>>, preview_only: bool) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
@ -138,12 +174,23 @@ impl OcrRetryTestHelper {
.send() .send()
.await?; .await?;
if !response.status().is_success() { let status = response.status();
return Err(format!("Failed to bulk retry OCR: {}", response.text().await?).into()); let response_text = response.text().await?;
if !status.is_success() {
return Err(format!("Failed to bulk retry OCR (status {}): {}", status, response_text).into());
} }
let result: Value = response.json().await?; // Try to parse the JSON and provide better error messages
Ok(result) match serde_json::from_str::<Value>(&response_text) {
Ok(result) => Ok(result),
Err(e) => {
eprintln!("JSON parsing failed for bulk retry response:");
eprintln!("Status: {}", status);
eprintln!("Response text: {}", response_text);
Err(format!("Failed to parse JSON response: {}. Raw response: {}", e, response_text).into())
}
}
} }
async fn get_document_retry_history(&self, document_id: &str) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> { async fn get_document_retry_history(&self, document_id: &str) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
@ -203,6 +250,7 @@ async fn test_ocr_retry_stats_endpoint() {
} }
Err(e) => { Err(e) => {
println!("❌ OCR retry stats test failed: {}", e); println!("❌ OCR retry stats test failed: {}", e);
println!("💡 This might indicate a server issue or missing endpoint implementation");
panic!("OCR retry stats endpoint failed: {}", e); panic!("OCR retry stats endpoint failed: {}", e);
} }
} }
@ -240,6 +288,7 @@ async fn test_ocr_retry_recommendations_endpoint() {
} }
Err(e) => { Err(e) => {
println!("❌ OCR retry recommendations test failed: {}", e); println!("❌ OCR retry recommendations test failed: {}", e);
println!("💡 This might indicate a server issue or missing endpoint implementation");
panic!("OCR retry recommendations endpoint failed: {}", e); panic!("OCR retry recommendations endpoint failed: {}", e);
} }
} }
@ -274,6 +323,7 @@ async fn test_bulk_retry_preview_mode() {
} }
Err(e) => { Err(e) => {
println!("❌ Bulk retry preview test failed: {}", e); println!("❌ Bulk retry preview test failed: {}", e);
println!("💡 This might indicate a server issue or missing endpoint implementation");
panic!("Bulk retry preview failed: {}", e); panic!("Bulk retry preview failed: {}", e);
} }
} }
@ -319,6 +369,7 @@ async fn test_document_retry_history() {
} }
Err(e) => { Err(e) => {
println!("❌ Document retry history test failed: {}", e); println!("❌ Document retry history test failed: {}", e);
println!("💡 This might indicate a server issue or missing endpoint implementation");
panic!("Document retry history failed: {}", e); panic!("Document retry history failed: {}", e);
} }
} }