use deribit_fix::config::DeribitFixConfig;
use deribit_fix::connection::Connection;
use deribit_fix::error::DeribitFixError;
use deribit_fix::session::{Session, SessionState};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
#[cfg(test)]
mod tests {
use super::*;
fn create_test_config() -> DeribitFixConfig {
DeribitFixConfig::new()
.with_credentials("test_user".to_string(), "test_password".to_string())
.with_endpoint("127.0.0.1".to_string(), 0)
.with_session_ids("CLIENT".to_string(), "DERIBIT".to_string())
.with_heartbeat_interval(30)
.with_connection_timeout(Duration::from_millis(1000))
}
#[test]
fn test_session_basic_functionality() {
let config = create_test_config();
assert!(!config.username.is_empty());
assert!(!config.password.is_empty());
assert_eq!(config.sender_comp_id, "CLIENT");
assert_eq!(config.target_comp_id, "DERIBIT");
assert_eq!(config.heartbeat_interval, 30);
}
#[test]
fn test_session_config_variations() {
let minimal_config = DeribitFixConfig::new();
assert!(!minimal_config.host.is_empty());
assert!(minimal_config.port > 0);
let full_config = DeribitFixConfig::new()
.with_credentials("user".to_string(), "pass".to_string())
.with_endpoint("fix-test.deribit.com".to_string(), 9881)
.with_session_ids("CLIENT_TEST".to_string(), "DERIBIT_TEST".to_string())
.with_heartbeat_interval(60)
.with_cancel_on_disconnect(true)
.with_app_credentials("test_app".to_string(), "test_secret".to_string())
.with_use_wordsafe_tags(true)
.with_deribit_sequential(false);
assert_eq!(full_config.username, "user");
assert_eq!(full_config.password, "pass");
assert_eq!(full_config.host, "fix-test.deribit.com");
assert_eq!(full_config.port, 9881);
assert_eq!(full_config.heartbeat_interval, 60);
assert!(full_config.cancel_on_disconnect);
assert_eq!(full_config.app_id, Some("test_app".to_string()));
assert_eq!(full_config.app_secret, Some("test_secret".to_string()));
}
async fn create_mock_connection() -> Result<Arc<Mutex<Connection>>, DeribitFixError> {
let config = DeribitFixConfig::new().with_endpoint("127.0.0.1".to_string(), 1234); match Connection::new(&config).await {
Ok(connection) => Ok(Arc::new(Mutex::new(connection))),
Err(_) => {
let config = DeribitFixConfig::new();
match Connection::new(&config).await {
Ok(connection) => Ok(Arc::new(Mutex::new(connection))),
Err(e) => Err(e),
}
}
}
}
#[tokio::test]
async fn test_session_creation_and_state_management() {
let config = create_test_config();
if let Ok(connection) = create_mock_connection().await
&& let Ok(mut session) = Session::new(&config, connection)
{
assert_eq!(session.get_state(), SessionState::Disconnected);
assert_eq!(session.state(), SessionState::Disconnected);
session.set_state(SessionState::LogonSent);
assert_eq!(session.state(), SessionState::LogonSent);
session.set_state(SessionState::LoggedOn);
assert_eq!(session.state(), SessionState::LoggedOn);
session.set_state(SessionState::LogoutSent);
assert_eq!(session.state(), SessionState::LogoutSent);
session.set_state(SessionState::Disconnected);
assert_eq!(session.state(), SessionState::Disconnected);
}
}
#[tokio::test]
async fn test_session_auth_data_generation() {
let config = create_test_config();
if let Ok(connection) = create_mock_connection().await
&& let Ok(session) = Session::new(&config, connection)
{
let result1 = session.generate_auth_data("test_secret");
assert!(result1.is_ok());
if let Ok((raw_data1, password_hash1)) = result1 {
assert!(!raw_data1.is_empty());
assert!(!password_hash1.is_empty());
assert!(raw_data1.contains('.')); assert_eq!(password_hash1.len(), 44); }
let result2 = session.generate_auth_data("different_secret");
assert!(result2.is_ok());
if let Ok((raw_data2, password_hash2)) = result2 {
assert!(!raw_data2.is_empty());
assert!(!password_hash2.is_empty());
assert!(raw_data2.contains('.'));
if let Ok((_, hash1)) = session.generate_auth_data("test_secret") {
assert_ne!(hash1, password_hash2);
}
}
let result3 = session.generate_auth_data("");
assert!(result3.is_ok());
let result4 = session.generate_auth_data("special@#$%");
assert!(result4.is_ok());
}
}
#[tokio::test]
async fn test_session_connection_management() {
let config = create_test_config();
if let Ok(connection1) = create_mock_connection().await
&& let Ok(mut session) = Session::new(&config, connection1)
{
if let Ok(connection2) = create_mock_connection().await {
session.set_connection(connection2);
assert_eq!(session.get_state(), SessionState::Disconnected);
}
}
}
#[test]
fn test_session_state_values() {
assert_eq!(format!("{:?}", SessionState::Disconnected), "Disconnected");
assert_eq!(format!("{:?}", SessionState::LogonSent), "LogonSent");
assert_eq!(format!("{:?}", SessionState::LoggedOn), "LoggedOn");
assert_eq!(format!("{:?}", SessionState::LogoutSent), "LogoutSent");
}
#[test]
fn test_session_state_equality() {
assert_eq!(SessionState::Disconnected, SessionState::Disconnected);
assert_eq!(SessionState::LoggedOn, SessionState::LoggedOn);
assert_ne!(SessionState::Disconnected, SessionState::LoggedOn);
}
#[test]
fn test_session_state_clone() {
let state = SessionState::LoggedOn;
let cloned = state;
assert_eq!(state, cloned);
}
#[test]
fn test_session_state_copy() {
let state = SessionState::LogonSent;
let copied = state;
assert_eq!(state, copied);
}
#[test]
fn test_session_error() {
let error = DeribitFixError::Session("Session not established".to_string());
let display_str = format!("{error}");
assert!(display_str.contains("Session error"));
assert!(display_str.contains("Session not established"));
}
#[test]
fn test_session_authentication_error() {
let error = DeribitFixError::Authentication("Login failed".to_string());
let display_str = format!("{error}");
assert!(display_str.contains("Authentication error"));
assert!(display_str.contains("Login failed"));
}
#[test]
fn test_config_for_session() {
let config = DeribitFixConfig::new()
.with_credentials("test_user".to_string(), "test_pass".to_string());
assert!(!config.username.is_empty());
assert!(!config.password.is_empty());
assert!(!config.host.is_empty());
assert!(config.port > 0);
}
#[test]
fn test_session_timeout_error() {
let error = DeribitFixError::Timeout("Session timeout".to_string());
let display_str = format!("{error}");
assert!(display_str.contains("Timeout error"));
assert!(display_str.contains("Session timeout"));
}
#[test]
fn test_session_protocol_error() {
let error = DeribitFixError::Protocol("Invalid session message".to_string());
let display_str = format!("{error}");
assert!(display_str.contains("Protocol error"));
assert!(display_str.contains("Invalid session message"));
}
#[test]
fn test_session_message_construction_error() {
let error =
DeribitFixError::MessageConstruction("Failed to build logon message".to_string());
let display_str = format!("{error}");
assert!(display_str.contains("Message construction error"));
assert!(display_str.contains("Failed to build logon message"));
}
#[tokio::test]
async fn test_session_async_methods_without_connection() {
let config = create_test_config();
if let Ok(connection) = create_mock_connection().await
&& let Ok(mut session) = Session::new(&config, connection)
{
let cancel_result = session.cancel_order("ORDER_123".to_string()).await;
assert!(cancel_result.is_ok());
let auth_result = session.generate_auth_data("test_secret");
assert!(auth_result.is_ok());
assert_eq!(session.get_state(), session.state());
}
}
#[tokio::test]
async fn test_session_message_processing() {
let config = create_test_config();
if let Ok(connection) = create_mock_connection().await
&& let Ok(session) = Session::new(&config, connection)
{
use deribit_fix::model::message::FixMessage;
let mut logon_msg = FixMessage::new();
logon_msg.set_field(35, "A".to_string()); logon_msg.set_field(49, "DERIBIT".to_string()); logon_msg.set_field(56, "CLIENT".to_string());
assert_eq!(
session.state(),
deribit_fix::session::SessionState::Disconnected
);
}
}
#[test]
fn test_session_error_variations() {
let connection_error = DeribitFixError::Connection("Connection lost".to_string());
assert!(format!("{connection_error}").contains("Connection error"));
assert!(format!("{connection_error}").contains("Connection lost"));
let io_error = DeribitFixError::Io(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Network timeout",
));
assert!(format!("{io_error}").contains("I/O error"));
assert!(format!("{io_error}").contains("Network timeout"));
let parsing_error = DeribitFixError::MessageParsing("Invalid FIX message".to_string());
assert!(format!("{parsing_error}").contains("Message parsing error"));
assert!(format!("{parsing_error}").contains("Invalid FIX message"));
}
#[test]
fn test_session_auth_data_variations() {
let _config = create_test_config();
let mut test_sessions = Vec::new();
for _ in 0..3 {
let config_variation = DeribitFixConfig::new()
.with_credentials("test_user".to_string(), "different_pass".to_string())
.with_app_credentials("app_id".to_string(), "app_secret".to_string());
test_sessions.push(config_variation);
}
assert_eq!(test_sessions.len(), 3);
assert_eq!(test_sessions[0].username, "test_user");
assert_eq!(test_sessions[0].password, "different_pass");
assert_eq!(test_sessions[0].app_id, Some("app_id".to_string()));
}
#[test]
fn test_session_config_edge_cases() {
let empty_config = DeribitFixConfig::new();
assert!(!empty_config.host.is_empty());
assert!(empty_config.port > 0);
assert!(!empty_config.username.is_empty() || empty_config.username.is_empty());
assert!(!empty_config.password.is_empty() || empty_config.password.is_empty());
let full_config = DeribitFixConfig::new()
.with_credentials("user".to_string(), "pass".to_string())
.with_endpoint("fix-test.deribit.com".to_string(), 9881)
.with_session_ids("SENDER".to_string(), "TARGET".to_string())
.with_heartbeat_interval(60)
.with_cancel_on_disconnect(true)
.with_app_credentials("app_id".to_string(), "app_secret".to_string())
.with_use_wordsafe_tags(true)
.with_deribit_sequential(false)
.with_unsubscribe_execution_reports(true)
.with_connection_only_execution_reports(false)
.with_report_fills_as_exec_reports(true)
.with_display_increment_steps(false);
assert_eq!(full_config.username, "user");
assert_eq!(full_config.password, "pass");
assert_eq!(full_config.host, "fix-test.deribit.com");
assert_eq!(full_config.port, 9881);
assert_eq!(full_config.sender_comp_id, "SENDER");
assert_eq!(full_config.target_comp_id, "TARGET");
assert_eq!(full_config.heartbeat_interval, 60);
assert!(full_config.cancel_on_disconnect);
assert_eq!(full_config.app_id, Some("app_id".to_string()));
assert_eq!(full_config.app_secret, Some("app_secret".to_string()));
assert_eq!(full_config.use_wordsafe_tags, Some(true));
assert_eq!(full_config.deribit_sequential, Some(false));
assert_eq!(full_config.unsubscribe_execution_reports, Some(true));
assert_eq!(full_config.connection_only_execution_reports, Some(false));
assert_eq!(full_config.report_fills_as_exec_reports, Some(true));
assert_eq!(full_config.display_increment_steps, Some(false));
}
#[test]
fn test_session_state_transitions() {
use deribit_fix::session::SessionState;
let states = vec![
SessionState::Disconnected,
SessionState::LogonSent,
SessionState::LoggedOn,
SessionState::LogoutSent,
];
for state in &states {
let debug_str = format!("{:?}", state);
assert!(!debug_str.is_empty());
assert_eq!(*state, *state);
let copied_state = *state;
assert_eq!(*state, copied_state);
}
assert_ne!(SessionState::Disconnected, SessionState::LoggedOn);
assert_ne!(SessionState::LogonSent, SessionState::LogoutSent);
}
#[test]
fn test_session_comprehensive_error_coverage() {
let errors = vec![
DeribitFixError::Connection("TCP connection failed".to_string()),
DeribitFixError::Authentication("Invalid credentials".to_string()),
DeribitFixError::Session("Session terminated".to_string()),
DeribitFixError::Timeout("Login timeout".to_string()),
DeribitFixError::Protocol("Invalid FIX protocol version".to_string()),
DeribitFixError::MessageConstruction("Failed to build message".to_string()),
DeribitFixError::MessageParsing("Malformed FIX message".to_string()),
DeribitFixError::Io(std::io::Error::new(
std::io::ErrorKind::NetworkUnreachable,
"Network unreachable",
)),
];
for error in errors {
let error_string = format!("{}", error);
assert!(!error_string.is_empty());
assert!(error_string.contains("error")); }
}
}