use ccxt_core::{Result, time::TimestampUtils};
#[tokio::test]
async fn test_timestamp_overflow_scenarios() -> Result<()> {
let max_valid = TimestampUtils::YEAR_2100_MS; assert!(TimestampUtils::validate_timestamp(max_valid).is_ok());
let beyond_max = TimestampUtils::YEAR_2100_MS + 1;
assert!(TimestampUtils::validate_timestamp(beyond_max).is_err());
assert!(TimestampUtils::validate_timestamp(i64::MAX).is_err());
let very_large = 9_999_999_999_999i64;
assert!(TimestampUtils::validate_timestamp(very_large).is_err());
Ok(())
}
#[tokio::test]
async fn test_timestamp_underflow_scenarios() -> Result<()> {
let min_valid = 0i64;
assert!(TimestampUtils::validate_timestamp(min_valid).is_ok());
assert!(TimestampUtils::validate_timestamp(-1).is_err());
assert!(TimestampUtils::validate_timestamp(-1000).is_err());
assert!(TimestampUtils::validate_timestamp(i64::MIN).is_err());
Ok(())
}
#[tokio::test]
async fn test_invalid_timestamp_parsing() {
assert!(TimestampUtils::parse_timestamp("").is_err());
assert!(TimestampUtils::parse_timestamp("invalid").is_err());
assert!(TimestampUtils::parse_timestamp("abc123").is_err());
assert!(TimestampUtils::parse_timestamp("not_a_number").is_err());
assert!(TimestampUtils::parse_timestamp("123.456.789").is_err());
assert!(TimestampUtils::parse_timestamp("123,456").is_err());
assert!(TimestampUtils::parse_timestamp("1e10").is_err());
assert!(TimestampUtils::parse_timestamp(" 1672531200000 ").is_err());
assert!(TimestampUtils::parse_timestamp("1672531200000\n").is_err());
assert!(TimestampUtils::parse_timestamp("\t1672531200000").is_err());
let long_string = "1".repeat(100);
assert!(TimestampUtils::parse_timestamp(&long_string).is_err());
}
#[tokio::test]
async fn test_valid_edge_case_parsing() -> Result<()> {
let epoch = TimestampUtils::parse_timestamp("0")?;
assert_eq!(epoch, 0);
let decimal_seconds = TimestampUtils::parse_timestamp("1672531200.123")?;
assert_eq!(decimal_seconds, 1672531200123);
let high_precision = TimestampUtils::parse_timestamp("1672531200.123456")?;
assert_eq!(high_precision, 1672531200123);
let millis = TimestampUtils::parse_timestamp("1672531200000")?;
assert_eq!(millis, 1672531200000);
Ok(())
}
#[tokio::test]
#[allow(deprecated)] async fn test_conversion_edge_cases() -> Result<()> {
let max_safe_u64 = (i64::MAX as u64).min(TimestampUtils::YEAR_2100_MS as u64);
let converted = TimestampUtils::u64_to_i64(max_safe_u64)?;
assert_eq!(converted, max_safe_u64 as i64);
let overflow_u64 = (i64::MAX as u64) + 1;
assert!(TimestampUtils::u64_to_i64(overflow_u64).is_err());
let max_valid_i64 = TimestampUtils::YEAR_2100_MS;
let converted_back = TimestampUtils::i64_to_u64(max_valid_i64)?;
assert_eq!(converted_back, max_valid_i64 as u64);
assert!(TimestampUtils::i64_to_u64(-1).is_err());
assert!(TimestampUtils::i64_to_u64(i64::MIN).is_err());
Ok(())
}
#[tokio::test]
async fn test_time_unit_conversion_edge_cases() {
let max_seconds = i64::MAX / 1000;
let converted = TimestampUtils::seconds_to_ms(max_seconds);
assert_eq!(converted, max_seconds * 1000);
let millis_with_remainder = 1672531200123i64;
let seconds = TimestampUtils::ms_to_seconds(millis_with_remainder);
assert_eq!(seconds, 1672531200);
assert_eq!(TimestampUtils::seconds_to_ms(0), 0);
assert_eq!(TimestampUtils::ms_to_seconds(0), 0);
assert_eq!(TimestampUtils::seconds_to_ms(-1), -1000);
assert_eq!(TimestampUtils::ms_to_seconds(-1000), -1);
}
#[tokio::test]
async fn test_iso8601_formatting_edge_cases() -> Result<()> {
let epoch_formatted = TimestampUtils::format_iso8601(0)?;
assert_eq!(epoch_formatted, "1970-01-01T00:00:00.000Z");
let with_millis = TimestampUtils::format_iso8601(1672531200123)?;
assert!(with_millis.contains(".123Z"));
let y2k = 946684800000i64; let y2k_formatted = TimestampUtils::format_iso8601(y2k)?;
assert!(y2k_formatted.contains("2000-01-01"));
let leap_day = 1709164800000i64; let leap_formatted = TimestampUtils::format_iso8601(leap_day)?;
assert!(leap_formatted.contains("2024-02-29"));
assert!(TimestampUtils::format_iso8601(-1).is_err());
assert!(TimestampUtils::format_iso8601(i64::MAX).is_err());
Ok(())
}
#[tokio::test]
async fn test_timestamp_validation_boundaries() {
assert!(TimestampUtils::validate_timestamp(0).is_ok()); assert!(TimestampUtils::validate_timestamp(TimestampUtils::YEAR_2100_MS).is_ok());
assert!(TimestampUtils::validate_timestamp(-1).is_err()); assert!(TimestampUtils::validate_timestamp(TimestampUtils::YEAR_2100_MS + 1).is_err());
let year_2020 = 1577836800000i64; let year_2030 = 1893456000000i64; assert!(TimestampUtils::validate_timestamp(year_2020).is_ok());
assert!(TimestampUtils::validate_timestamp(year_2030).is_ok());
let now = TimestampUtils::now_ms();
assert!(TimestampUtils::validate_timestamp(now).is_ok());
}
#[tokio::test]
async fn test_error_message_quality() {
match TimestampUtils::validate_timestamp(-1) {
Err(e) => {
let error_msg = format!("{}", e);
assert!(error_msg.contains("negative") || error_msg.contains("invalid"));
}
Ok(_) => panic!("Expected error for negative timestamp"),
}
match TimestampUtils::validate_timestamp(i64::MAX) {
Err(e) => {
let error_msg = format!("{}", e);
assert!(
error_msg.contains("future")
|| error_msg.contains("invalid")
|| error_msg.contains("range")
);
}
Ok(_) => panic!("Expected error for timestamp too far in future"),
}
match TimestampUtils::parse_timestamp("invalid") {
Err(e) => {
let error_msg = format!("{}", e);
assert!(
error_msg.contains("parse")
|| error_msg.contains("invalid")
|| error_msg.contains("format")
);
}
Ok(_) => panic!("Expected error for invalid timestamp string"),
}
}
#[tokio::test]
async fn test_concurrent_timestamp_operations() -> Result<()> {
use std::sync::Arc;
use tokio::task;
let test_timestamps = Arc::new(vec![
0i64,
1672531200000i64,
1577836800000i64,
1893456000000i64,
]);
let mut handles = vec![];
for _i in 0..10 {
let timestamps = Arc::clone(&test_timestamps);
let handle = task::spawn(async move {
for &ts in timestamps.iter() {
assert!(TimestampUtils::validate_timestamp(ts).is_ok());
let formatted = TimestampUtils::format_iso8601(ts)?;
assert!(!formatted.is_empty());
let seconds = TimestampUtils::ms_to_seconds(ts);
let back_to_ms = TimestampUtils::seconds_to_ms(seconds);
assert_eq!(back_to_ms / 1000 * 1000, ts / 1000 * 1000); }
Ok::<(), ccxt_core::Error>(())
});
handles.push(handle);
}
for handle in handles {
if let Err(e) = handle.await {
return Err(ccxt_core::Error::network(format!("Task join error: {}", e)));
}
}
Ok(())
}
#[tokio::test]
async fn test_memory_efficiency() -> Result<()> {
let large_dataset: Vec<i64> = (0..10000)
.map(|i| 1672531200000i64 + (i * 60000)) .collect();
for ×tamp in &large_dataset {
let _ = TimestampUtils::validate_timestamp(timestamp)?;
let formatted = TimestampUtils::format_iso8601(timestamp)?;
let _ = TimestampUtils::parse_timestamp(×tamp.to_string())?;
assert!(formatted.len() > 20 && formatted.len() < 30);
}
Ok(())
}
#[tokio::test]
async fn test_timestamp_precision() -> Result<()> {
let base_timestamp = 1672531200000i64;
for millis in 0..1000 {
let timestamp_with_millis = base_timestamp + millis;
assert!(TimestampUtils::validate_timestamp(timestamp_with_millis).is_ok());
let formatted = TimestampUtils::format_iso8601(timestamp_with_millis)?;
let expected_millis = format!(".{:03}Z", millis);
assert!(formatted.ends_with(&expected_millis));
let parsed = TimestampUtils::parse_timestamp(×tamp_with_millis.to_string())?;
assert_eq!(parsed, timestamp_with_millis);
}
Ok(())
}