#[cfg(test)]
mod tests {
use crate::{BackoffConfig, ConsumerConfig, ConsumerStartSequence};
use chrono::Datelike;
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct TestMessage {
id: String,
content: String,
priority: u32,
}
impl TestMessage {
fn new(id: &str, content: &str, priority: u32) -> Self {
Self {
id: id.to_string(),
content: content.to_string(),
priority,
}
}
}
#[test]
fn test_consumer_config_default() {
let config = ConsumerConfig::default();
assert!(!config.name.is_empty()); assert!(config.name.starts_with("consumer-"));
assert_eq!(config.consumer_group, None);
assert_eq!(config.max_deliver, 3);
assert_eq!(config.ack_wait, Duration::from_secs(30));
assert_eq!(config.max_age, Some(Duration::from_secs(24 * 60 * 60))); assert!(matches!(
config.start_sequence,
ConsumerStartSequence::Latest
));
assert_eq!(config.worker_count, 1);
}
#[test]
fn test_consumer_config_custom() {
let config = ConsumerConfig {
name: "test-consumer".to_string(),
consumer_group: Some("test-group".to_string()),
max_deliver: 5,
ack_wait: Duration::from_secs(15),
max_age: Some(Duration::from_secs(7200)), start_sequence: ConsumerStartSequence::First,
worker_count: 4,
};
assert_eq!(config.name, "test-consumer");
assert_eq!(config.consumer_group.as_ref().unwrap(), "test-group");
assert_eq!(config.max_deliver, 5);
assert_eq!(config.ack_wait, Duration::from_secs(15));
assert_eq!(config.max_age, Some(Duration::from_secs(7200)));
assert!(matches!(
config.start_sequence,
ConsumerStartSequence::First
));
assert_eq!(config.worker_count, 4);
}
#[test]
fn test_consumer_start_sequence_variants() {
let first = ConsumerStartSequence::First;
let latest = ConsumerStartSequence::Latest;
let from_sequence = ConsumerStartSequence::Sequence(12345);
let from_time = ConsumerStartSequence::Time(chrono::Utc::now());
assert!(matches!(first, ConsumerStartSequence::First));
assert!(matches!(latest, ConsumerStartSequence::Latest));
match from_sequence {
ConsumerStartSequence::Sequence(seq) => assert_eq!(seq, 12345),
_ => panic!("Expected Sequence variant"),
}
match from_time {
ConsumerStartSequence::Time(_) => {} _ => panic!("Expected Time variant"),
}
}
#[test]
fn test_backoff_config_default() {
let backoff = BackoffConfig::default();
assert_eq!(backoff.initial_delay, Duration::from_millis(100));
assert_eq!(backoff.max_delay, Duration::from_secs(30));
assert_eq!(backoff.max_retries, 5);
assert_eq!(backoff.multiplier, 2.0);
assert_eq!(backoff.jitter, 0.1);
}
#[test]
fn test_backoff_config_custom() {
let backoff = BackoffConfig {
initial_delay: Duration::from_millis(50),
max_delay: Duration::from_secs(10),
max_retries: 3,
multiplier: 1.5,
jitter: 0.2,
};
assert_eq!(backoff.initial_delay, Duration::from_millis(50));
assert_eq!(backoff.max_delay, Duration::from_secs(10));
assert_eq!(backoff.max_retries, 3);
assert_eq!(backoff.multiplier, 1.5);
assert_eq!(backoff.jitter, 0.2);
}
#[test]
fn test_backoff_config_edge_cases() {
let min_backoff = BackoffConfig {
initial_delay: Duration::from_millis(1),
max_delay: Duration::from_millis(1),
max_retries: 1,
multiplier: 1.0,
jitter: 0.0,
};
assert_eq!(min_backoff.initial_delay, Duration::from_millis(1));
assert_eq!(min_backoff.max_delay, Duration::from_millis(1));
assert_eq!(min_backoff.max_retries, 1);
assert_eq!(min_backoff.multiplier, 1.0);
assert_eq!(min_backoff.jitter, 0.0);
let large_backoff = BackoffConfig {
initial_delay: Duration::from_secs(10),
max_delay: Duration::from_secs(3600), max_retries: 20,
multiplier: 10.0,
jitter: 1.0,
};
assert_eq!(large_backoff.initial_delay, Duration::from_secs(10));
assert_eq!(large_backoff.max_delay, Duration::from_secs(3600));
assert_eq!(large_backoff.max_retries, 20);
assert_eq!(large_backoff.multiplier, 10.0);
assert_eq!(large_backoff.jitter, 1.0);
}
#[test]
fn test_consumer_config_cloning() {
let original_config = ConsumerConfig {
name: "test-consumer".to_string(),
consumer_group: Some("test-group".to_string()),
max_deliver: 7,
ack_wait: Duration::from_secs(45),
max_age: Some(Duration::from_secs(3600)),
start_sequence: ConsumerStartSequence::Sequence(999),
worker_count: 3,
};
let cloned_config = original_config.clone();
assert_eq!(cloned_config.name, original_config.name);
assert_eq!(cloned_config.consumer_group, original_config.consumer_group);
assert_eq!(cloned_config.max_deliver, original_config.max_deliver);
assert_eq!(cloned_config.ack_wait, original_config.ack_wait);
assert_eq!(cloned_config.max_age, original_config.max_age);
assert!(matches!(
cloned_config.start_sequence,
ConsumerStartSequence::Sequence(999)
));
assert_eq!(cloned_config.worker_count, original_config.worker_count);
}
#[test]
fn test_consumer_config_debug_formatting() {
let config = ConsumerConfig {
name: "debug-consumer".to_string(),
consumer_group: Some("debug-group".to_string()),
max_deliver: 3,
ack_wait: Duration::from_secs(30),
max_age: Some(Duration::from_secs(3600)),
start_sequence: ConsumerStartSequence::First,
worker_count: 2,
};
let debug_string = format!("{:?}", config);
assert!(debug_string.contains("debug-consumer"));
assert!(debug_string.contains("debug-group"));
assert!(debug_string.contains("30"));
assert!(debug_string.contains("3"));
assert!(debug_string.contains("First"));
assert!(debug_string.contains("2"));
}
#[test]
fn test_consumer_config_optional_fields() {
let minimal_config = ConsumerConfig {
name: "minimal-consumer".to_string(),
consumer_group: None,
max_deliver: 2,
ack_wait: Duration::from_secs(20),
max_age: None,
start_sequence: ConsumerStartSequence::Latest,
worker_count: 1,
};
assert_eq!(minimal_config.consumer_group, None);
assert_eq!(minimal_config.max_age, None);
let full_config = ConsumerConfig {
name: "full-consumer".to_string(),
consumer_group: Some("test-group".to_string()),
max_deliver: 2,
ack_wait: Duration::from_secs(20),
max_age: Some(Duration::from_secs(1800)), start_sequence: ConsumerStartSequence::Latest,
worker_count: 1,
};
assert!(full_config.consumer_group.is_some());
assert!(full_config.max_age.is_some());
assert_eq!(full_config.consumer_group.unwrap(), "test-group");
assert_eq!(full_config.max_age.unwrap(), Duration::from_secs(1800));
}
#[test]
fn test_message_structure() {
fn check_message_type<T>()
where
T: serde::de::DeserializeOwned,
{
}
check_message_type::<TestMessage>();
check_message_type::<String>();
check_message_type::<serde_json::Value>();
check_message_type::<Vec<u8>>();
let message = TestMessage::new("msg-1", "payload", 10);
assert_eq!(message.id, "msg-1");
assert_eq!(message.priority, 10);
}
#[test]
fn test_capability_string_patterns() {
let capabilities = vec![
"fs.read.v1",
"fs.write.v1",
"http.fetch.v1",
"git.clone.v1",
"archive.read.v1",
"sqlite.query.v1",
"bench.report.v1",
"custom.capability.v2",
"test.debug.v99",
];
for capability in capabilities {
assert!(!capability.is_empty());
assert!(capability.contains('.'));
assert!(
capability.ends_with(".v1")
|| capability.ends_with(".v2")
|| capability.ends_with(".v99")
);
let capability_string = capability.to_string();
assert_eq!(capability, capability_string);
let cloned_capability = capability.to_string();
assert_eq!(capability, cloned_capability);
}
}
#[test]
fn test_time_handling() {
let now = chrono::Utc::now();
let past = now - chrono::Duration::hours(24);
let future = now + chrono::Duration::hours(1);
assert!(past < now);
assert!(now < future);
assert!(past < future);
let specific_time = chrono::DateTime::from_timestamp(1640995200, 0).unwrap(); assert_eq!(specific_time.year(), 2022);
assert_eq!(specific_time.month(), 1);
assert_eq!(specific_time.day(), 1);
}
#[test]
fn test_duration_arithmetic() {
let base_duration = Duration::from_millis(100);
let doubled = Duration::from_millis(200);
let quadrupled = Duration::from_millis(400);
assert!(base_duration < doubled);
assert!(doubled < quadrupled);
assert!(base_duration < quadrupled);
assert_eq!(base_duration + base_duration, doubled);
assert_eq!(std::cmp::max(base_duration, doubled), doubled);
assert_eq!(std::cmp::min(base_duration, doubled), base_duration);
assert_eq!(base_duration.as_millis(), 100);
assert_eq!(Duration::from_secs(1).as_millis(), 1000);
assert_eq!(Duration::from_secs(60).as_secs(), 60);
}
#[test]
fn test_configuration_validation_patterns() {
let reasonable_config = ConsumerConfig {
name: "valid-consumer-name".to_string(),
consumer_group: Some("valid-group".to_string()),
max_deliver: 3,
ack_wait: Duration::from_secs(30),
max_age: Some(Duration::from_secs(86400)), start_sequence: ConsumerStartSequence::First,
worker_count: 2,
};
assert!(!reasonable_config.name.is_empty());
assert!(reasonable_config.ack_wait >= Duration::from_secs(1));
assert!(reasonable_config.ack_wait <= Duration::from_secs(300));
assert!(reasonable_config.max_deliver > 0);
assert!(reasonable_config.max_deliver <= 10);
assert!(reasonable_config.worker_count >= 1);
let edge_config = ConsumerConfig {
name: "edge-consumer".to_string(),
consumer_group: None,
max_deliver: 1,
ack_wait: Duration::from_millis(1000),
max_age: None,
start_sequence: ConsumerStartSequence::Sequence(0),
worker_count: 1,
};
assert_eq!(edge_config.max_deliver, 1);
assert_eq!(edge_config.ack_wait, Duration::from_millis(1000));
assert_eq!(edge_config.worker_count, 1);
}
#[test]
fn test_consumer_config_serialization() {
let config = ConsumerConfig {
name: "serialization-consumer".to_string(),
consumer_group: Some("test-group".to_string()),
max_deliver: 4,
ack_wait: Duration::from_secs(25),
max_age: Some(Duration::from_secs(7200)), start_sequence: ConsumerStartSequence::Latest,
worker_count: 2,
};
assert_eq!(config.name, "serialization-consumer");
assert_eq!(config.ack_wait.as_secs(), 25);
assert_eq!(config.max_deliver, 4);
assert_eq!(config.worker_count, 2);
let cloned_config = config.clone();
assert_eq!(cloned_config.name, config.name);
assert_eq!(cloned_config.ack_wait, config.ack_wait);
assert_eq!(cloned_config.max_deliver, config.max_deliver);
}
}