use crate::p2p::network::config::ClientConfig;
use crate::p2p::network::core::{IrohBackend, batch_processor::*};
use bytes::Bytes;
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::timeout;
async fn create_test_config() -> (ClientConfig, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let mut client_config = ClientConfig::testing();
client_config.data_store_path = Some(temp_dir.path().to_path_buf());
(client_config, temp_dir)
}
async fn create_test_backend() -> (Arc<IrohBackend>, TempDir) {
let (client_config, temp_dir) = create_test_config().await;
let backend = IrohBackend::new(&client_config)
.await
.expect("Failed to create backend");
(Arc::new(backend), temp_dir)
}
async fn create_test_batch_processor() -> (BatchProcessor, Arc<IrohBackend>, TempDir) {
let (backend, temp_dir) = create_test_backend().await;
let batch_config = BatchConfig {
max_batch_size: 1, max_batch_wait_ms: 10,
enable_smart_batching: false, ..Default::default()
};
let processor = BatchProcessor::new(batch_config, Arc::clone(&backend));
(processor, backend, temp_dir)
}
async fn create_custom_batch_processor(
batch_config: BatchConfig,
) -> (BatchProcessor, Arc<IrohBackend>, TempDir) {
let (backend, temp_dir) = create_test_backend().await;
let processor = BatchProcessor::new(batch_config, Arc::clone(&backend));
(processor, backend, temp_dir)
}
fn generate_test_data(size: usize) -> Bytes {
Bytes::from((0..size).map(|i| (i % 256) as u8).collect::<Vec<u8>>())
}
#[tokio::test]
async fn test_batch_processor_creation() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let stats = processor.get_stats().await;
assert_eq!(stats.total_operations, 0);
assert_eq!(stats.batched_operations, 0);
}
#[tokio::test]
async fn test_batch_processor_with_custom_config() {
let custom_config = BatchConfig {
max_batch_size: 50,
max_batch_wait_ms: 100,
max_processing_threads: 4,
memory_flush_threshold: 32 * 1024 * 1024,
enable_smart_batching: true,
enable_batch_compression: true,
compression_threshold: 2048,
};
let (processor, _backend, _temp_dir) =
create_custom_batch_processor(custom_config.clone()).await;
let stats = processor.get_stats().await;
assert_eq!(stats.total_operations, 0);
}
#[tokio::test]
async fn test_default_batch_config() {
let batch_config = BatchConfig::default();
assert_eq!(batch_config.max_batch_size, 100);
assert_eq!(batch_config.max_batch_wait_ms, 50);
assert_eq!(batch_config.max_processing_threads, 8);
assert!(batch_config.enable_smart_batching);
assert!(batch_config.enable_batch_compression);
}
#[tokio::test]
async fn test_batch_processor_stats_initialization() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let stats = processor.get_stats().await;
assert_eq!(stats.total_operations, 0);
assert_eq!(stats.batched_operations, 0);
assert_eq!(stats.individual_operations, 0);
assert_eq!(stats.avg_batch_size, 0.0);
assert_eq!(stats.total_bytes_processed, 0);
}
#[tokio::test]
async fn test_add_operation_simple() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = generate_test_data(1024);
let operation_data = OperationData::AddData {
data: test_data.clone(),
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao adicionar dados");
let result = result.unwrap();
assert!(
result.is_ok(),
"Erro ao adicionar dados: {:?}",
result.as_ref().err()
);
if let Ok(OperationResult::AddResult(add_result)) = result {
assert!(!add_result.hash.is_empty());
assert!(!add_result.name.is_empty());
} else {
panic!("Expected AddResult");
}
}
#[tokio::test]
async fn test_add_operation_with_priority() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = generate_test_data(512);
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 10),
)
.await;
assert!(result.is_ok(), "Timeout ao adicionar com prioridade");
assert!(result.unwrap().is_ok(), "Erro ao adicionar com prioridade");
}
#[tokio::test]
async fn test_add_operation_small_data() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = Bytes::from("Small test data");
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao adicionar dados pequenos");
assert!(result.unwrap().is_ok(), "Erro ao adicionar dados pequenos");
}
#[tokio::test]
async fn test_add_operation_large_data() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = generate_test_data(50 * 1024);
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(20),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao adicionar dados grandes");
assert!(result.unwrap().is_ok(), "Erro ao adicionar dados grandes");
}
#[tokio::test]
async fn test_add_operation_empty_data() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = Bytes::new();
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao adicionar dados vazios");
assert!(result.unwrap().is_ok(), "Erro ao adicionar dados vazios");
}
#[tokio::test]
async fn test_add_multiple_operations_concurrent() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut handles = vec![];
for i in 0..5 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Concurrent data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
timeout(
Duration::from_secs(10),
processor_clone.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await
});
handles.push(handle);
}
let mut success_count = 0;
for handle in handles {
if let Ok(Ok(Ok(_))) = handle.await {
success_count += 1;
}
}
assert!(
success_count >= 3,
"Esperado >= 3 operações concorrentes bem-sucedidas, obtido {}",
success_count
);
}
#[tokio::test]
async fn test_get_operation_after_add() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = Bytes::from("Test data for get");
let add_operation = OperationData::AddData {
data: test_data.clone(),
options: AddOptions::default(),
};
let add_result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, add_operation, 5),
)
.await
.expect("Timeout on add")
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let get_operation = OperationData::GetHash {
hash: hash.clone(),
options: GetOptions::default(),
};
let get_result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Get, get_operation, 5),
)
.await;
assert!(get_result.is_ok(), "Timeout on get");
let get_result = get_result.unwrap();
assert!(get_result.is_ok(), "Get failed");
if let Ok(OperationResult::GetResult(data)) = get_result {
assert_eq!(data, test_data);
}
}
#[tokio::test]
async fn test_get_operation_nonexistent_hash() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let fake_hash = "0".repeat(64);
let get_operation = OperationData::GetHash {
hash: fake_hash,
options: GetOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Get, get_operation, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao buscar hash inexistente");
assert!(
result.unwrap().is_err(),
"Deveria retornar erro para hash inexistente"
);
}
#[tokio::test]
async fn test_get_operation_with_timeout() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let test_data = Bytes::from("Timeout test data");
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, add_operation, 5),
)
.await
.expect("Timeout on add")
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let get_operation = OperationData::GetHash {
hash,
options: GetOptions {
timeout: Some(Duration::from_secs(5)),
preferred_peers: vec![],
},
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Get, get_operation, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao buscar com timeout configurado");
}
#[tokio::test]
async fn test_get_multiple_hashes_parallel() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut hashes = vec![];
for i in 0..3 {
let test_data = Bytes::from(format!("Parallel data {}", i));
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, add_operation, 5),
)
.await
.expect("Timeout on add")
.expect("Add failed");
if let OperationResult::AddResult(result) = add_result {
hashes.push(result.hash);
}
}
let mut handles = vec![];
for hash in hashes {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let get_operation = OperationData::GetHash {
hash,
options: GetOptions::default(),
};
timeout(
Duration::from_secs(10),
processor_clone.add_batch_operation(OperationType::Get, get_operation, 5),
)
.await
});
handles.push(handle);
}
let mut success_count = 0;
for handle in handles {
if let Ok(Ok(Ok(_))) = handle.await {
success_count += 1;
}
}
assert!(
success_count >= 2,
"Esperado >= 2 gets paralelos bem-sucedidos, obtido {}",
success_count
);
}
#[tokio::test]
async fn test_get_operation_invalid_hash_format() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let invalid_hash = "invalid_hash";
let get_operation = OperationData::GetHash {
hash: invalid_hash.to_string(),
options: GetOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Get, get_operation, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao buscar hash inválido");
assert!(
result.unwrap().is_err(),
"Deveria retornar erro para hash inválido"
);
}
#[tokio::test]
async fn test_pin_operation() {
let (processor, backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Pin test data");
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let pin_operation = OperationData::PinHash {
hash: hash.clone(),
options: PinOptions::default(),
};
let pin_result = processor
.add_batch_operation(OperationType::Pin, pin_operation, 5)
.await;
assert!(pin_result.is_ok());
let pins = backend.pin_ls().await.expect("Failed to list pins");
assert!(pins.iter().any(|p| p.hash == hash));
}
#[tokio::test]
async fn test_unpin_operation() {
let (processor, backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Unpin test data");
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let pin_operation = OperationData::PinHash {
hash: hash.clone(),
options: PinOptions::default(),
};
processor
.add_batch_operation(OperationType::Pin, pin_operation, 5)
.await
.expect("Pin failed");
let unpin_operation = OperationData::UnpinHash { hash: hash.clone() };
let unpin_result = processor
.add_batch_operation(OperationType::Unpin, unpin_operation, 5)
.await;
assert!(unpin_result.is_ok());
let pins = backend.pin_ls().await.expect("Failed to list pins");
assert!(!pins.iter().any(|p| p.hash == hash));
}
#[tokio::test]
async fn test_pin_nonexistent_hash() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let fake_hash = "0".repeat(64);
let pin_operation = OperationData::PinHash {
hash: fake_hash,
options: PinOptions::default(),
};
let result = processor
.add_batch_operation(OperationType::Pin, pin_operation, 5)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_pin_batch_multiple_hashes() {
let (processor, backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut hashes = vec![];
for i in 0..5 {
let test_data = Bytes::from(format!("Batch pin data {}", i));
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
if let OperationResult::AddResult(result) = add_result {
hashes.push(result.hash);
}
}
let mut handles = vec![];
for hash in &hashes {
let processor_clone = Arc::clone(&processor);
let hash_clone = hash.clone();
let handle = tokio::spawn(async move {
let pin_operation = OperationData::PinHash {
hash: hash_clone,
options: PinOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Pin, pin_operation, 5)
.await
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.expect("Task failed");
assert!(result.is_ok());
}
let pins = backend.pin_ls().await.expect("Failed to list pins");
for hash in &hashes {
assert!(pins.iter().any(|p| &p.hash == hash));
}
}
#[tokio::test]
async fn test_pin_with_high_priority() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Priority pin data");
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let pin_operation = OperationData::PinHash {
hash,
options: PinOptions::default(),
};
let result = processor
.add_batch_operation(OperationType::Pin, pin_operation, 10)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_pin_recursive_option() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Recursive pin data");
let add_operation = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let pin_operation = OperationData::PinHash {
hash,
options: PinOptions {
recursive: true,
progress: false,
},
};
let result = processor
.add_batch_operation(OperationType::Pin, pin_operation, 5)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_pubsub_operation_simple() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let topic = "test_topic".to_string();
let message_data = Bytes::from("Test message");
let pubsub_operation = OperationData::PubSubData {
topic,
data: message_data,
};
let result = processor
.add_batch_operation(OperationType::PubSubPublish, pubsub_operation, 5)
.await;
let _ = result;
}
#[tokio::test]
async fn test_pubsub_multiple_topics() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let topics = vec!["topic1", "topic2", "topic3"];
let mut handles = vec![];
for topic in topics {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let message_data = Bytes::from(format!("Message for {}", topic));
let pubsub_operation = OperationData::PubSubData {
topic: topic.to_string(),
data: message_data,
};
processor_clone
.add_batch_operation(OperationType::PubSubPublish, pubsub_operation, 5)
.await
});
handles.push(handle);
}
for handle in handles {
let _ = handle.await.expect("Task failed");
}
}
#[tokio::test]
async fn test_pubsub_same_topic_batch() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let topic = "batch_topic".to_string();
let mut handles = vec![];
for i in 0..5 {
let processor_clone = Arc::clone(&processor);
let topic_clone = topic.clone();
let handle = tokio::spawn(async move {
let message_data = Bytes::from(format!("Batch message {}", i));
let pubsub_operation = OperationData::PubSubData {
topic: topic_clone,
data: message_data,
};
processor_clone
.add_batch_operation(OperationType::PubSubPublish, pubsub_operation, 5)
.await
});
handles.push(handle);
}
for handle in handles {
let _ = handle.await.expect("Task failed");
}
}
#[tokio::test]
async fn test_pubsub_large_message() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let topic = "large_topic".to_string();
let large_data = generate_test_data(64 * 1024);
let pubsub_operation = OperationData::PubSubData {
topic,
data: large_data,
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::PubSubPublish, pubsub_operation, 5),
)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_smart_batching_enabled() {
let custom_config = BatchConfig {
max_batch_size: 10,
max_batch_wait_ms: 100,
enable_smart_batching: true,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let handle = processor.start_auto_processor();
let mut handles = vec![];
for i in 0..10 {
let processor_clone = Arc::clone(&processor);
let task_handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Batch data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
timeout(
Duration::from_secs(10),
processor_clone.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await
});
handles.push(task_handle);
}
let mut success_count = 0;
for h in handles {
if let Ok(Ok(Ok(_))) = h.await {
success_count += 1;
}
}
tokio::time::sleep(Duration::from_millis(300)).await;
handle.abort();
assert!(
success_count >= 6,
"Esperado >= 6 operações de 10, obtido {}",
success_count
);
}
#[tokio::test]
async fn test_batch_size_threshold() {
let custom_config = BatchConfig {
max_batch_size: 5,
enable_smart_batching: false,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let mut handles = vec![];
for i in 0..5 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Threshold data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Add, operation_data, 5)
.await
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.expect("Task failed");
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_typed_queues_separation() {
let custom_config = BatchConfig {
enable_smart_batching: true,
max_batch_size: 20,
max_batch_wait_ms: 50,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let handle = processor.start_auto_processor();
let mut handles = vec![];
for i in 0..5 {
let processor_clone = Arc::clone(&processor);
let task_handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Add data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
timeout(
Duration::from_secs(10),
processor_clone.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await
});
handles.push(task_handle);
}
let mut success_count = 0;
for h in handles {
if let Ok(Ok(Ok(_))) = h.await {
success_count += 1;
}
}
tokio::time::sleep(Duration::from_millis(200)).await;
handle.abort();
assert!(
success_count >= 3,
"Esperado >= 3 operações, obtido {}",
success_count
);
}
#[tokio::test]
async fn test_batch_compression_threshold() {
let custom_config = BatchConfig {
max_batch_size: 1, enable_batch_compression: true,
compression_threshold: 512,
enable_smart_batching: false,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let large_data = generate_test_data(2048);
let operation_data = OperationData::AddData {
data: large_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao testar compressão");
assert!(result.unwrap().is_ok(), "Erro ao testar compressão");
}
#[tokio::test]
async fn test_priority_based_ordering() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut handles = vec![];
for i in 0..3 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Low priority {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Add, operation_data, 1)
.await
});
handles.push(handle);
}
for i in 0..3 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("High priority {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Add, operation_data, 10)
.await
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.expect("Task failed");
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_memory_flush_threshold() {
let custom_config = BatchConfig {
max_batch_size: 1, memory_flush_threshold: 1024 * 1024, enable_smart_batching: false,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let large_data = generate_test_data(50 * 1024); let operation_data = OperationData::AddData {
data: large_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(15),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao testar memory flush");
assert!(result.unwrap().is_ok(), "Erro ao testar memory flush");
}
#[tokio::test]
async fn test_stats_basic_counters() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
for i in 0..5 {
let test_data = Bytes::from(format!("Stats data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Add, operation_data, 5)
.await;
}
let stats = processor.get_stats().await;
assert!(stats.total_operations >= 5);
}
#[tokio::test]
async fn test_stats_batch_efficiency() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut handles = vec![];
for i in 0..20 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Efficiency data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Add, operation_data, 5)
.await
});
handles.push(handle);
}
for handle in handles {
let _ = handle.await.expect("Task failed");
}
let stats = processor.get_stats().await;
assert!(stats.batch_efficiency >= 0.0 && stats.batch_efficiency <= 1.0);
}
#[tokio::test]
async fn test_stats_average_batch_size() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
for i in 0..10 {
let test_data = Bytes::from(format!("Avg batch data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Add, operation_data, 5)
.await;
}
let stats = processor.get_stats().await;
assert!(stats.avg_batch_size >= 0.0);
}
#[tokio::test]
async fn test_stats_processing_time() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Processing time data");
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Add, operation_data, 5)
.await;
let stats = processor.get_stats().await;
assert!(stats.avg_batch_processing_time_ms >= 0.0);
}
#[tokio::test]
async fn test_operation_history_tracking() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("History tracking data");
let add_operation = OperationData::AddData {
data: test_data.clone(),
options: AddOptions::default(),
};
let add_result = processor
.add_batch_operation(OperationType::Add, add_operation, 5)
.await
.expect("Add failed");
let hash = if let OperationResult::AddResult(result) = add_result {
result.hash
} else {
panic!("Expected AddResult");
};
let get_operation = OperationData::GetHash {
hash,
options: GetOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Get, get_operation, 5)
.await;
let stats = processor.get_stats().await;
assert!(stats.total_operations >= 2);
}
#[tokio::test]
async fn test_auto_processor_start() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let handle = processor.start_auto_processor();
for i in 0..5 {
let test_data = Bytes::from(format!("Auto process data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Add, operation_data, 5)
.await;
}
tokio::time::sleep(Duration::from_millis(200)).await;
handle.abort();
}
#[tokio::test]
async fn test_auto_processor_periodic_flush() {
let custom_config = BatchConfig {
max_batch_wait_ms: 100,
enable_smart_batching: true,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let handle = processor.start_auto_processor();
for i in 0..3 {
let test_data = Bytes::from(format!("Periodic data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let _ = processor
.add_batch_operation(OperationType::Add, operation_data, 5)
.await;
tokio::time::sleep(Duration::from_millis(50)).await;
}
tokio::time::sleep(Duration::from_millis(300)).await;
handle.abort();
}
#[tokio::test]
async fn test_auto_processor_with_high_load() {
let custom_config = BatchConfig {
max_batch_size: 5,
max_batch_wait_ms: 50,
enable_smart_batching: true,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let handle = processor.start_auto_processor();
let mut handles = vec![];
for i in 0..20 {
let processor_clone = Arc::clone(&processor);
let handle_task = tokio::spawn(async move {
let test_data = Bytes::from(format!("High load data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(15),
processor_clone.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
matches!(result, Ok(Ok(_)))
});
handles.push(handle_task);
}
let mut success_count = 0;
for h in handles {
if let Ok(true) = h.await {
success_count += 1;
}
}
tokio::time::sleep(Duration::from_millis(1000)).await;
assert!(
success_count >= 9,
"Esperado >= 9 operações bem-sucedidas (45%), obtido {}",
success_count
);
handle.abort();
}
#[tokio::test]
async fn test_auto_processor_concurrency_control() {
let custom_config = BatchConfig {
max_processing_threads: 2,
max_batch_size: 5,
..Default::default()
};
let (processor, _backend, _temp_dir) = create_custom_batch_processor(custom_config).await;
let processor = Arc::new(processor);
let handle = processor.start_auto_processor();
let mut handles = vec![];
for i in 0..20 {
let processor_clone = Arc::clone(&processor);
let handle_task = tokio::spawn(async move {
let test_data = Bytes::from(format!("Concurrency data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
processor_clone
.add_batch_operation(OperationType::Add, operation_data, 5)
.await
});
handles.push(handle_task);
}
for h in handles {
let _ = h.await.expect("Task failed");
}
tokio::time::sleep(Duration::from_millis(300)).await;
handle.abort();
}
#[tokio::test]
async fn test_empty_data_operations() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let empty_data = Bytes::new();
let operation_data = OperationData::AddData {
data: empty_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
assert!(result.is_ok(), "Timeout ao testar dados vazios");
assert!(result.unwrap().is_ok(), "Erro ao testar dados vazios");
}
#[tokio::test]
async fn test_very_high_priority_operations() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Very high priority");
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = processor
.add_batch_operation(OperationType::Add, operation_data, 10)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_zero_priority_operations() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let test_data = Bytes::from("Zero priority");
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = processor
.add_batch_operation(OperationType::Add, operation_data, 0)
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_mixed_operation_types_stress() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut handles = vec![];
for i in 0..15 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
let test_data = Bytes::from(format!("Mixed data {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let priority = if i % 3 == 0 { 5 } else { 3 };
timeout(
Duration::from_secs(10),
processor_clone.add_batch_operation(OperationType::Add, operation_data, priority),
)
.await
});
handles.push(handle);
}
let mut success_count = 0;
for handle in handles {
if let Ok(Ok(Ok(_))) = handle.await {
success_count += 1;
}
}
assert!(
success_count >= 10,
"Esperado >= 10 operações mistas bem-sucedidas, obtido {}",
success_count
);
}
#[tokio::test]
async fn test_rapid_sequential_operations() {
let (processor, _backend, _temp_dir) = create_test_batch_processor().await;
let processor = Arc::new(processor);
let mut success_count = 0;
for i in 0..20 {
let test_data = Bytes::from(format!("Rapid seq {}", i));
let operation_data = OperationData::AddData {
data: test_data,
options: AddOptions::default(),
};
let result = timeout(
Duration::from_secs(10),
processor.add_batch_operation(OperationType::Add, operation_data, 5),
)
.await;
if let Ok(Ok(_)) = result {
success_count += 1;
}
}
assert!(
success_count >= 15,
"Esperado >= 15 operações sequenciais bem-sucedidas, obtido {}",
success_count
);
}