use ccxt_core::{
time::TimestampUtils,
types::{Ohlcv, Trade, Order, Transaction},
Result,
};
use ccxt_exchanges::{Binance, Bitget, Bybit, OKX, Hyperliquid};
use std::sync::Arc;
#[tokio::test]
async fn test_ohlcv_timestamp_consistency() -> Result<()> {
let test_timestamp: i64 = 1672531200000;
let ohlcv = Ohlcv {
timestamp: test_timestamp,
open: rust_decimal::Decimal::from(50000),
high: rust_decimal::Decimal::from(51000),
low: rust_decimal::Decimal::from(49000),
close: rust_decimal::Decimal::from(50500),
volume: 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: "test_trade_123".to_string(),
order_id: Some("order_456".to_string()),
timestamp: test_timestamp,
datetime: test_timestamp, symbol: "BTC/USDT".to_string(),
side: ccxt_core::types::OrderSide::Buy,
amount: rust_decimal::Decimal::from(1),
price: rust_decimal::Decimal::from(50000),
cost: rust_decimal::Decimal::from(50000),
fee: None,
taker_or_maker: None,
};
assert_eq!(trade.timestamp, test_timestamp);
assert_eq!(trade.datetime, test_timestamp);
assert_eq!(trade.timestamp, trade.datetime);
assert!(TimestampUtils::validate_timestamp(trade.timestamp).is_ok());
assert!(TimestampUtils::validate_timestamp(trade.datetime).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: test_timestamp,
created_at: test_timestamp,
updated_at: Some(updated_timestamp),
last_trade_timestamp: Some(updated_timestamp),
symbol: "BTC/USDT".to_string(),
side: ccxt_core::types::OrderSide::Buy,
amount: rust_decimal::Decimal::from(1),
price: Some(rust_decimal::Decimal::from(50000)),
filled: rust_decimal::Decimal::ZERO,
remaining: rust_decimal::Decimal::from(1),
status: ccxt_core::types::OrderStatus::Open,
order_type: ccxt_core::types::OrderType::Limit,
time_in_force: None,
fee: None,
trades: vec![],
};
assert_eq!(order.timestamp, test_timestamp);
assert_eq!(order.created_at, test_timestamp);
assert_eq!(order.updated_at, Some(updated_timestamp));
assert_eq!(order.last_trade_timestamp, Some(updated_timestamp));
assert!(TimestampUtils::validate_timestamp(order.timestamp).is_ok());
assert!(TimestampUtils::validate_timestamp(order.created_at).is_ok());
if let Some(ts) = order.updated_at {
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 {
id: "tx_123".to_string(),
txid: Some("blockchain_tx_456".to_string()),
timestamp: test_timestamp,
datetime: test_timestamp,
updated_at: Some(updated_timestamp),
currency: "BTC".to_string(),
amount: rust_decimal::Decimal::from(1),
status: ccxt_core::types::TransactionStatus::Ok,
transaction_type: ccxt_core::types::TransactionType::Deposit,
address: Some("bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string()),
tag: None,
fee: None,
network: Some("BTC".to_string()),
};
assert_eq!(transaction.timestamp, test_timestamp);
assert_eq!(transaction.datetime, test_timestamp);
assert_eq!(transaction.updated_at, Some(updated_timestamp));
assert_eq!(transaction.timestamp, transaction.datetime);
assert!(TimestampUtils::validate_timestamp(transaction.timestamp).is_ok());
assert!(TimestampUtils::validate_timestamp(transaction.datetime).is_ok());
if let Some(ts) = transaction.updated_at {
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: rust_decimal::Decimal::from(50000),
high: rust_decimal::Decimal::from(51000),
low: rust_decimal::Decimal::from(49000),
close: rust_decimal::Decimal::from(50500),
volume: 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]
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: rust_decimal::Decimal::from(50000),
high: rust_decimal::Decimal::from(51000),
low: rust_decimal::Decimal::from(49000),
close: rust_decimal::Decimal::from(50500),
volume: rust_decimal::Decimal::from(100),
};
let trade = Trade {
id: "test_trade".to_string(),
order_id: None,
timestamp: base_timestamp,
datetime: base_timestamp,
symbol: "BTC/USDT".to_string(),
side: ccxt_core::types::OrderSide::Buy,
amount: rust_decimal::Decimal::from(1),
price: rust_decimal::Decimal::from(50000),
cost: rust_decimal::Decimal::from(50000),
fee: None,
taker_or_maker: None,
};
let order = Order {
id: "test_order".to_string(),
client_order_id: None,
timestamp: base_timestamp,
created_at: base_timestamp,
updated_at: None,
last_trade_timestamp: None,
symbol: "BTC/USDT".to_string(),
side: ccxt_core::types::OrderSide::Buy,
amount: rust_decimal::Decimal::from(1),
price: Some(rust_decimal::Decimal::from(50000)),
filled: rust_decimal::Decimal::ZERO,
remaining: rust_decimal::Decimal::from(1),
status: ccxt_core::types::OrderStatus::Open,
order_type: ccxt_core::types::OrderType::Limit,
time_in_force: None,
fee: None,
trades: vec![],
};
assert_eq!(ohlcv.timestamp, base_timestamp);
assert_eq!(trade.timestamp, base_timestamp);
assert_eq!(trade.datetime, base_timestamp);
assert_eq!(order.timestamp, base_timestamp);
assert_eq!(order.created_at, base_timestamp);
assert_eq!(ohlcv.timestamp, trade.timestamp);
assert_eq!(trade.timestamp, order.timestamp);
assert_eq!(order.timestamp, order.created_at);
Ok(())
}