use std::path::Path;
use std::time::Duration;
use tokio::time::{sleep, timeout};
use tracing::{debug, info};
use deribit_fix::prelude::*;
fn check_env_file() -> Result<()> {
if !Path::new(".env").exists() {
return Err(DeribitFixError::Config(
"Missing .env file. Please create one with DERIBIT_USERNAME and DERIBIT_PASSWORD"
.to_string(),
));
}
dotenv::dotenv().ok();
let required_vars = [
"DERIBIT_USERNAME",
"DERIBIT_PASSWORD",
"DERIBIT_HOST",
"DERIBIT_PORT",
];
for var in &required_vars {
if std::env::var(var).is_err() {
return Err(DeribitFixError::Config(format!(
"Missing required environment variable: {}",
var
)));
}
}
Ok(())
}
#[tokio::test]
async fn test_authentication_with_wrong_password() -> Result<()> {
unsafe {
std::env::set_var("DERIBIT_LOG_LEVEL", "debug");
}
setup_logger();
info!("=== Integration Test: Authentication with Wrong Password ===");
check_env_file()?;
info!("โ
Environment file validation passed");
let mut config = DeribitFixConfig::new();
config.password = "wrong_password_12345".to_string();
config.validate()?;
info!("โ
Configuration created with invalid password");
let mut client = DeribitFixClient::new(&config).await?;
info!("โ
Client created successfully");
info!("๐ Attempting to connect with wrong password...");
let connect_result = client.connect().await;
match connect_result {
Err(DeribitFixError::Authentication(_)) => {
info!("โ
Authentication failed as expected with wrong password");
return Ok(());
}
Err(other_error) => {
info!("โ
Connection failed as expected: {}", other_error);
return Ok(());
}
Ok(_) => {
info!("โ ๏ธ Connection succeeded despite wrong password (checking for rejection)...");
let rejection_timeout = Duration::from_secs(5);
let mut logout_received = false;
let _rejection_result = timeout(rejection_timeout, async {
loop {
match client.receive_message().await {
Ok(Some(message)) => {
if let Some(msg_type) = message.get_field(35) {
debug!("๐จ Received message type: {}", msg_type);
if msg_type == "5" {
info!("๐ค Received Logout message (authentication rejected)");
if let Some(text) = message.get_field(58) {
info!("Logout reason: {}", text);
}
logout_received = true;
return Ok::<(), DeribitFixError>(());
}
}
}
Ok(None) => {
sleep(Duration::from_millis(100)).await;
}
Err(e) => {
debug!("Error receiving message: {}", e);
break;
}
}
}
Ok(())
})
.await;
client.disconnect().await.ok();
if logout_received {
info!("โ
Authentication properly rejected with Logout message");
} else {
info!(
"โ ๏ธ Test server may be permissive with authentication (acceptable in test environment)"
);
}
return Ok(());
}
}
}
#[tokio::test]
async fn test_authentication_with_invalid_sender_comp_id() -> Result<()> {
setup_logger();
info!("=== Integration Test: Authentication with Invalid SenderCompID ===");
check_env_file()?;
let mut config = DeribitFixConfig::new();
config.sender_comp_id = "INVALID_CLIENT_ID".to_string();
config.validate()?;
info!(
"โ
Configuration created with invalid SenderCompID: {}",
config.sender_comp_id
);
let mut client = DeribitFixClient::new(&config).await?;
info!("๐ Attempting to connect with invalid SenderCompID...");
let connect_result = client.connect().await;
match connect_result {
Err(DeribitFixError::Authentication(_)) => {
info!("โ
Authentication failed as expected with invalid SenderCompID");
Ok(())
}
Err(other_error) => {
info!("โ
Connection failed as expected: {}", other_error);
Ok(())
}
Ok(_) => {
info!(
"โ ๏ธ Connection succeeded despite invalid SenderCompID (checking for rejection)..."
);
let mut rejected = false;
let monitor_timeout = Duration::from_secs(5);
let _monitor_result = timeout(monitor_timeout, async {
loop {
match client.receive_message().await {
Ok(Some(message)) => {
if let Some(msg_type) = message.get_field(35)
&& msg_type == "5"
{
info!("๐ค Received Logout message (invalid SenderCompID)");
if let Some(text) = message.get_field(58) {
info!("Logout reason: {}", text);
}
rejected = true;
return Ok::<(), DeribitFixError>(());
}
}
Ok(None) => sleep(Duration::from_millis(100)).await,
Err(_) => break,
}
}
Ok(())
})
.await;
client.disconnect().await.ok();
if rejected {
info!("โ
Invalid SenderCompID properly rejected");
} else {
info!("โ ๏ธ Test server accepted invalid SenderCompID (test environment behavior)");
}
Ok(())
}
}
}
#[tokio::test]
async fn test_authentication_with_invalid_username() -> Result<()> {
setup_logger();
info!("=== Integration Test: Authentication with Invalid Username ===");
check_env_file()?;
let mut config = DeribitFixConfig::new();
config.username = "invalid_username_12345".to_string();
config.validate()?;
info!(
"โ
Configuration created with invalid username: {}",
config.username
);
let mut client = DeribitFixClient::new(&config).await?;
info!("๐ Attempting to connect with invalid username...");
let connect_result = client.connect().await;
match connect_result {
Err(DeribitFixError::Authentication(_)) => {
info!("โ
Authentication failed as expected with invalid username");
Ok(())
}
Err(other_error) => {
info!("โ
Connection failed as expected: {}", other_error);
Ok(())
}
Ok(_) => {
info!("โ ๏ธ Connection succeeded despite invalid username (checking for rejection)...");
let mut authentication_issue = false;
let monitor_duration = Duration::from_secs(3);
let start_time = std::time::Instant::now();
while start_time.elapsed() < monitor_duration {
match timeout(Duration::from_millis(200), client.receive_message()).await {
Ok(Ok(Some(message))) => {
if let Some(msg_type) = message.get_field(35) {
match msg_type.as_str() {
"5" => {
info!("๐ค Received Logout message");
if let Some(text) = message.get_field(58) {
info!("Logout reason: {}", text);
}
authentication_issue = true;
break;
}
"3" => {
info!("โ Received Reject message");
authentication_issue = true;
break;
}
_ => {
debug!("๐จ Received other message: {}", msg_type);
}
}
}
}
_ => {
sleep(Duration::from_millis(100)).await;
}
}
}
client.disconnect().await.ok();
if authentication_issue {
info!("โ
Authentication issue detected and handled properly");
} else {
info!("โ ๏ธ Test server may be permissive with credentials (test environment)");
}
Ok(())
}
}
}
#[tokio::test]
async fn test_multiple_failed_authentication_attempts() -> Result<()> {
setup_logger();
info!("=== Integration Test: Multiple Failed Authentication Attempts ===");
check_env_file()?;
let invalid_credentials = [
("wrong_user1", "wrong_pass1"),
("wrong_user2", "wrong_pass2"),
("", ""), ];
for (i, (username, password)) in invalid_credentials.iter().enumerate() {
info!(
"๐งช Testing authentication attempt {} with credentials: '{}'/'{}'",
i + 1,
username,
if password.is_empty() {
"<empty>"
} else {
"<hidden>"
}
);
let mut config = DeribitFixConfig::new();
config.username = username.to_string();
config.password = password.to_string();
let client_result = if username.is_empty() || password.is_empty() {
DeribitFixClient::new(&config).await
} else {
config.validate()?;
DeribitFixClient::new(&config).await
};
let mut client = match client_result {
Ok(client) => client,
Err(_) => {
info!("โ
Attempt {} failed as expected (validation error)", i + 1);
continue;
}
};
let connect_result = client.connect().await;
match connect_result {
Err(_) => {
info!("โ
Attempt {} failed as expected", i + 1);
}
Ok(_) => {
info!(
"โ ๏ธ Attempt {} unexpectedly succeeded (test server permissive)",
i + 1
);
if let Ok(Ok(Some(message))) =
timeout(Duration::from_millis(500), client.receive_message()).await
&& let Some(msg_type) = message.get_field(35)
{
debug!("Server responded with message type: {}", msg_type);
}
client.disconnect().await.ok();
}
}
sleep(Duration::from_millis(100)).await;
}
info!("โ
Multiple authentication failure test completed");
Ok(())
}