#[cfg(test)]
mod integration_tests {
use super::super::*;
use crate::app::models::FileInfo;
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::sleep;
fn create_test_file_info(hash: &str, station: &str) -> FileInfo {
let temp_dir = TempDir::new().unwrap();
let valid_hash = format!("{:0<32}", hash);
let hash_obj = crate::app::hash::Md5Hash::from_hex(&valid_hash).unwrap();
let path = format!(
"./data/uk-daily-temperature-obs/dataset-version-202407/devon/{}_twist/qc-version-1/midas-open_uk-daily-temperature-obs_dv-202407_devon_{}_twist_qcv-1_1980.csv",
station, station
);
FileInfo::new(hash_obj, path, temp_dir.path(), None).unwrap()
}
#[tokio::test]
async fn test_end_to_end_workflow() {
let queue = WorkQueue::new();
let files = vec![
create_test_file_info("1", "01381"),
create_test_file_info("2", "01382"),
create_test_file_info("3", "01383"),
];
let mut file_hashes = Vec::new();
for file in files {
file_hashes.push(file.hash);
queue.add_work(file).await.unwrap();
}
let mut completed = 0;
while let Some(work) = queue.get_next_work().await {
sleep(Duration::from_millis(1)).await;
queue.mark_completed(work.work_id()).await.unwrap();
completed += 1;
if completed >= 3 {
break;
}
}
let stats = queue.stats().await;
assert_eq!(stats.total_added, 3);
assert_eq!(stats.completed_count, 3);
assert_eq!(stats.pending_count, 0);
assert_eq!(stats.in_progress_count, 0);
assert!(queue.is_finished().await);
}
#[tokio::test]
async fn test_failure_and_retry_workflow() {
let config = ConfigPresets::testing();
let queue = WorkQueue::with_config(config);
let file = create_test_file_info("1", "01381");
let file_hash = file.hash;
queue.add_work(file).await.unwrap();
for attempt in 1..=2 {
let work = queue
.get_next_work()
.await
.expect("Work should be available");
assert_eq!(work.work_id(), &file_hash);
queue
.mark_failed(&file_hash, &format!("Error {}", attempt))
.await
.unwrap();
sleep(Duration::from_millis(15)).await;
}
let work_info = queue.get_work_info(&file_hash).await.unwrap();
assert!(work_info.is_abandoned());
let stats = queue.stats().await;
assert_eq!(stats.abandoned_count, 1);
assert!(queue.is_finished().await);
}
#[tokio::test]
async fn test_concurrent_worker_simulation() {
let queue = Arc::new(WorkQueue::new());
let worker_count = 5;
let work_per_worker = 10;
for i in 0..(worker_count * work_per_worker) {
let file = create_test_file_info(&format!("{:032}", i), &format!("{:05}", i));
queue.add_work(file).await.unwrap();
}
let mut handles = Vec::new();
for _worker_id in 0..worker_count {
let queue_clone = Arc::clone(&queue);
let handle = tokio::spawn(async move {
let mut processed = 0;
while let Some(work) = queue_clone.get_next_work().await {
sleep(Duration::from_millis(1)).await;
if processed % 10 == 0 {
queue_clone
.mark_failed(work.work_id(), "Simulated error")
.await
.unwrap();
} else {
queue_clone.mark_completed(work.work_id()).await.unwrap();
}
processed += 1;
if processed >= work_per_worker {
break;
}
}
processed
});
handles.push(handle);
}
let mut total_processed = 0;
for handle in handles {
let worker_processed = handle.await.unwrap();
total_processed += worker_processed;
}
let stats = queue.stats().await;
assert_eq!(stats.total_added, (worker_count * work_per_worker) as u64);
assert!(stats.completed_count > 0);
assert_eq!(total_processed, worker_count * work_per_worker);
}
#[tokio::test]
async fn test_timeout_handling() {
let config = WorkQueueConfigBuilder::new()
.work_timeout(Duration::from_millis(50))
.build();
let queue = WorkQueue::with_config(config);
let file = create_test_file_info("1", "01381");
let file_hash = file.hash;
queue.add_work(file).await.unwrap();
let work = queue.get_next_work().await.unwrap();
assert!(work.is_in_progress());
sleep(Duration::from_millis(100)).await;
let timed_out = queue.handle_timeouts().await;
assert_eq!(timed_out, 1);
let work_info = queue.get_work_info(&file_hash).await.unwrap();
assert!(work_info.is_failed());
}
#[tokio::test]
async fn test_capacity_management() {
let config = WorkQueueConfigBuilder::new().max_pending_items(5).build();
let queue = WorkQueue::with_config(config);
for i in 0..4 {
let file = create_test_file_info(&format!("{:032}", i), &format!("{:05}", i));
queue.add_work(file).await.unwrap();
}
assert!(queue.has_capacity().await);
let file = create_test_file_info(&format!("{:032}", 4), &format!("{:05}", 4));
queue.add_work(file).await.unwrap();
assert!(!queue.has_capacity().await);
let stats = queue.stats().await;
assert_eq!(stats.pending_count, 5);
}
#[tokio::test]
async fn test_statistics_accuracy() {
let queue = WorkQueue::new();
let files = vec![
create_test_file_info("1", "01381"),
create_test_file_info("2", "01382"),
create_test_file_info("3", "01383"),
];
for file in files {
queue.add_work(file).await.unwrap();
}
let stats = queue.stats().await;
assert_eq!(stats.total_added, 3);
assert_eq!(stats.pending_count, 3);
assert_eq!(stats.in_progress_count, 0);
assert_eq!(stats.completed_count, 0);
let work = queue.get_next_work().await.unwrap();
let stats = queue.stats().await;
assert_eq!(stats.pending_count, 2);
assert_eq!(stats.in_progress_count, 1);
queue.mark_completed(work.work_id()).await.unwrap();
let stats = queue.stats().await;
assert_eq!(stats.pending_count, 2);
assert_eq!(stats.in_progress_count, 0);
assert_eq!(stats.completed_count, 1);
}
#[tokio::test]
async fn test_bulk_operations_efficiency() {
let queue = WorkQueue::new();
let files: Vec<FileInfo> = (0..1000)
.map(|i| create_test_file_info(&format!("{:032}", i), &format!("{:05}", i)))
.collect();
let start = std::time::Instant::now();
let added = queue.add_work_bulk(files).await.unwrap();
let bulk_duration = start.elapsed();
assert_eq!(added, 1000);
assert!(bulk_duration < Duration::from_secs(1));
let stats = queue.stats().await;
assert_eq!(stats.total_added, 1000);
assert_eq!(stats.pending_count, 1000);
}
#[tokio::test]
async fn test_queue_cleanup_efficiency() {
let queue = WorkQueue::new();
for i in 0..100 {
let file = create_test_file_info(&format!("{:032}", i), &format!("{:05}", i));
queue.add_work(file).await.unwrap();
let work = queue.get_next_work().await.unwrap();
queue.mark_completed(work.work_id()).await.unwrap();
}
let stats_before = queue.stats().await;
assert_eq!(stats_before.completed_count, 100);
let removed = queue.cleanup().await;
assert_eq!(removed, 100);
let stats_after = queue.stats().await;
assert_eq!(stats_after.completed_count, 100);
let all_work = queue.get_all_work().await;
assert!(all_work.is_empty());
}
#[tokio::test]
async fn test_priority_ordering() {
let queue = WorkQueue::new();
let low_file = create_test_file_info("1", "01381");
let high_file = create_test_file_info("2", "01382");
let normal_file = create_test_file_info("3", "01383");
queue
.add_work_with_priority(low_file, priority::LOW)
.await
.unwrap();
queue
.add_work_with_priority(high_file, priority::HIGH)
.await
.unwrap();
queue
.add_work_with_priority(normal_file, priority::NORMAL)
.await
.unwrap();
let stats = queue.stats().await;
assert_eq!(stats.pending_count, 3);
}
}