use ccxt_core::{
Result,
time::TimestampUtils,
types::{
Amount, Ohlcv, Order, OrderSide, OrderStatus, OrderType, Price, Timestamp, Trade,
Transaction, TransactionStatus, TransactionType,
},
};
#[tokio::test]
async fn test_ohlcv_timestamp_consistency() -> Result<()> {
let test_timestamp: Timestamp = 1672531200000;
let ohlcv = Ohlcv {
timestamp: test_timestamp,
open: Price::new(rust_decimal::Decimal::from(50000)),
high: Price::new(rust_decimal::Decimal::from(51000)),
low: Price::new(rust_decimal::Decimal::from(49000)),
close: Price::new(rust_decimal::Decimal::from(50500)),
volume: Amount::new(rust_decimal::Decimal::from(100)),
};
assert_eq!(ohlcv.timestamp, test_timestamp);
assert!(TimestampUtils::validate_timestamp(ohlcv.timestamp).is_ok());
let formatted = TimestampUtils::format_iso8601(ohlcv.timestamp)?;
assert!(formatted.contains("2023-01-01"));
Ok(())
}
#[tokio::test]
async fn test_trade_timestamp_consistency() -> Result<()> {
let test_timestamp: i64 = 1672531200000;
let trade = Trade {
id: Some("test_trade_123".to_string()),
order: Some("order_456".to_string()),
symbol: "BTC/USDT".to_string(),
trade_type: Some(OrderType::Market),
side: OrderSide::Buy,
taker_or_maker: None,
price: Price::new(rust_decimal::Decimal::from(50000)),
amount: Amount::new(rust_decimal::Decimal::from(1)),
cost: None,
fee: None,
timestamp: test_timestamp,
datetime: Some("2023-01-01T00:00:00.000Z".to_string()),
info: std::collections::HashMap::new(),
};
assert_eq!(trade.timestamp, test_timestamp);
assert!(TimestampUtils::validate_timestamp(trade.timestamp).is_ok());
Ok(())
}
#[tokio::test]
async fn test_order_timestamp_consistency() -> Result<()> {
let test_timestamp: i64 = 1672531200000;
let updated_timestamp: i64 = test_timestamp + 60000;
let order = Order {
id: "order_123".to_string(),
client_order_id: Some("client_456".to_string()),
timestamp: Some(test_timestamp),
datetime: Some("2023-01-01T00:00:00.000Z".to_string()),
last_trade_timestamp: Some(updated_timestamp),
symbol: "BTC/USDT".to_string(),
order_type: OrderType::Limit,
time_in_force: None,
post_only: None,
reduce_only: None,
side: OrderSide::Buy,
price: Some(rust_decimal::Decimal::from(50000)),
stop_price: None,
trigger_price: None,
take_profit_price: None,
stop_loss_price: None,
trailing_delta: None,
trailing_percent: None,
activation_price: None,
callback_rate: None,
working_type: None,
amount: rust_decimal::Decimal::from(1),
filled: Some(rust_decimal::Decimal::ZERO),
remaining: Some(rust_decimal::Decimal::from(1)),
cost: None,
average: None,
status: OrderStatus::Open,
fee: None,
fees: None,
trades: None,
info: std::collections::HashMap::new(),
};
assert_eq!(order.timestamp, Some(test_timestamp));
assert_eq!(order.last_trade_timestamp, Some(updated_timestamp));
if let Some(ts) = order.timestamp {
assert!(TimestampUtils::validate_timestamp(ts).is_ok());
}
if let Some(ts) = order.last_trade_timestamp {
assert!(TimestampUtils::validate_timestamp(ts).is_ok());
}
Ok(())
}
#[tokio::test]
async fn test_transaction_timestamp_consistency() -> Result<()> {
let test_timestamp: i64 = 1672531200000;
let updated_timestamp: i64 = test_timestamp + 60000;
let transaction = Transaction {
info: None,
id: "tx_123".to_string(),
txid: Some("blockchain_tx_456".to_string()),
timestamp: Some(test_timestamp),
datetime: Some("2023-01-01T00:00:00.000Z".to_string()),
network: Some("BTC".to_string()),
address: Some("bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string()),
address_to: None,
address_from: None,
tag: None,
tag_to: None,
tag_from: None,
transaction_type: TransactionType::Deposit,
amount: rust_decimal::Decimal::from(1),
currency: "BTC".to_string(),
status: TransactionStatus::Ok,
updated: Some(updated_timestamp),
internal: None,
comment: None,
fee: None,
};
assert_eq!(transaction.timestamp, Some(test_timestamp));
assert_eq!(transaction.updated, Some(updated_timestamp));
if let Some(ts) = transaction.timestamp {
assert!(TimestampUtils::validate_timestamp(ts).is_ok());
}
if let Some(ts) = transaction.updated {
assert!(TimestampUtils::validate_timestamp(ts).is_ok());
}
Ok(())
}
#[tokio::test]
async fn test_timestamp_conversion_utilities() -> Result<()> {
let current_time = TimestampUtils::now_ms();
assert!(current_time > 0);
assert!(current_time < 4_102_444_800_000);
let seconds = 1672531200; let millis = TimestampUtils::seconds_to_ms(seconds);
assert_eq!(millis, 1672531200000);
let back_to_seconds = TimestampUtils::ms_to_seconds(millis);
assert_eq!(back_to_seconds, seconds);
assert!(TimestampUtils::validate_timestamp(1672531200000).is_ok()); assert!(TimestampUtils::validate_timestamp(-1).is_err()); assert!(TimestampUtils::validate_timestamp(5_000_000_000_000).is_err());
let formatted = TimestampUtils::format_iso8601(1672531200000)?;
assert!(formatted.contains("2023-01-01T00:00:00"));
Ok(())
}
#[tokio::test]
async fn test_timestamp_parsing() -> Result<()> {
let parsed = TimestampUtils::parse_timestamp("1672531200000")?;
assert_eq!(parsed, 1672531200000);
let parsed_decimal = TimestampUtils::parse_timestamp("1672531200.123")?;
assert_eq!(parsed_decimal, 1672531200123);
assert!(TimestampUtils::parse_timestamp("invalid").is_err());
assert!(TimestampUtils::parse_timestamp("").is_err());
Ok(())
}
#[tokio::test]
async fn test_end_to_end_timestamp_flow() -> Result<()> {
let api_timestamp_str = "1672531200000";
let parsed_timestamp = TimestampUtils::parse_timestamp(api_timestamp_str)?;
let ohlcv = Ohlcv {
timestamp: parsed_timestamp,
open: Price::new(rust_decimal::Decimal::from(50000)),
high: Price::new(rust_decimal::Decimal::from(51000)),
low: Price::new(rust_decimal::Decimal::from(49000)),
close: Price::new(rust_decimal::Decimal::from(50500)),
volume: Amount::new(rust_decimal::Decimal::from(100)),
};
assert!(TimestampUtils::validate_timestamp(ohlcv.timestamp).is_ok());
let formatted = TimestampUtils::format_iso8601(ohlcv.timestamp)?;
assert!(formatted.contains("2023-01-01"));
assert_eq!(ohlcv.timestamp, parsed_timestamp);
assert_eq!(parsed_timestamp, 1672531200000);
Ok(())
}
#[tokio::test]
#[allow(deprecated)] async fn test_backward_compatibility_conversions() -> Result<()> {
let u64_timestamp: u64 = 1672531200000;
let converted = TimestampUtils::u64_to_i64(u64_timestamp)?;
assert_eq!(converted, 1672531200000i64);
let i64_timestamp: i64 = 1672531200000;
let converted_back = TimestampUtils::i64_to_u64(i64_timestamp)?;
assert_eq!(converted_back, 1672531200000u64);
let large_u64: u64 = u64::MAX;
assert!(TimestampUtils::u64_to_i64(large_u64).is_err());
let negative_i64: i64 = -1;
assert!(TimestampUtils::i64_to_u64(negative_i64).is_err());
Ok(())
}
#[tokio::test]
async fn test_cross_type_timestamp_consistency() -> Result<()> {
let base_timestamp: i64 = 1672531200000;
let ohlcv = Ohlcv {
timestamp: base_timestamp,
open: Price::new(rust_decimal::Decimal::from(50000)),
high: Price::new(rust_decimal::Decimal::from(51000)),
low: Price::new(rust_decimal::Decimal::from(49000)),
close: Price::new(rust_decimal::Decimal::from(50500)),
volume: Amount::new(rust_decimal::Decimal::from(100)),
};
let trade = Trade {
id: Some("test_trade".to_string()),
order: None,
symbol: "BTC/USDT".to_string(),
trade_type: Some(OrderType::Market),
side: OrderSide::Buy,
taker_or_maker: None,
price: Price::new(rust_decimal::Decimal::from(50000)),
amount: Amount::new(rust_decimal::Decimal::from(1)),
cost: None,
fee: None,
timestamp: base_timestamp,
datetime: Some("2023-01-01T00:00:00.000Z".to_string()),
info: std::collections::HashMap::new(),
};
let order = Order {
id: "test_order".to_string(),
client_order_id: None,
timestamp: Some(base_timestamp),
datetime: Some("2023-01-01T00:00:00.000Z".to_string()),
last_trade_timestamp: None,
symbol: "BTC/USDT".to_string(),
order_type: OrderType::Limit,
time_in_force: None,
post_only: None,
reduce_only: None,
side: OrderSide::Buy,
price: Some(rust_decimal::Decimal::from(50000)),
stop_price: None,
trigger_price: None,
take_profit_price: None,
stop_loss_price: None,
trailing_delta: None,
trailing_percent: None,
activation_price: None,
callback_rate: None,
working_type: None,
amount: rust_decimal::Decimal::from(1),
filled: Some(rust_decimal::Decimal::ZERO),
remaining: Some(rust_decimal::Decimal::from(1)),
cost: None,
average: None,
status: OrderStatus::Open,
fee: None,
fees: None,
trades: None,
info: std::collections::HashMap::new(),
};
assert_eq!(ohlcv.timestamp, base_timestamp);
assert_eq!(trade.timestamp, base_timestamp);
assert_eq!(order.timestamp, Some(base_timestamp));
assert_eq!(ohlcv.timestamp, trade.timestamp);
if let Some(order_timestamp) = order.timestamp {
assert_eq!(trade.timestamp, order_timestamp);
}
Ok(())
}