use exarrow_rs::adbc::{Connection, Driver};
use std::env;
use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
pub const DEFAULT_HOST: &str = "localhost";
pub const DEFAULT_PORT: u16 = 8563;
pub const DEFAULT_USER: &str = "sys";
pub const DEFAULT_PASSWORD: &str = "exasol";
const ENV_EXASOL_HOST: &str = "EXASOL_HOST";
const ENV_EXASOL_PORT: &str = "EXASOL_PORT";
const ENV_EXASOL_USER: &str = "EXASOL_USER";
const ENV_EXASOL_PASSWORD: &str = "EXASOL_PASSWORD";
pub fn get_host() -> String {
env::var(ENV_EXASOL_HOST).unwrap_or_else(|_| DEFAULT_HOST.to_string())
}
pub fn get_port() -> u16 {
env::var(ENV_EXASOL_PORT)
.ok()
.and_then(|p| p.parse().ok())
.unwrap_or(DEFAULT_PORT)
}
pub fn get_user() -> String {
env::var(ENV_EXASOL_USER).unwrap_or_else(|_| DEFAULT_USER.to_string())
}
pub fn get_password() -> String {
env::var(ENV_EXASOL_PASSWORD).unwrap_or_else(|_| DEFAULT_PASSWORD.to_string())
}
pub fn get_test_connection_string() -> String {
connection_string(&get_user(), &get_password(), &get_host(), get_port())
}
pub fn connection_string(user: &str, password: &str, host: &str, port: u16) -> String {
format!("exasol://{user}:{password}@{host}:{port}?tls=true&validateservercertificate=0")
}
#[allow(dead_code)]
pub fn get_test_connection_string_with_transport(transport: &str) -> String {
format!("{}&transport={}", get_test_connection_string(), transport)
}
#[allow(dead_code)]
pub async fn get_test_connection() -> Result<Connection, exarrow_rs::error::ExasolError> {
let driver = Driver::new();
let conn_string = get_test_connection_string();
let mut last_error = None;
for attempt in 1..=5u32 {
let database = driver.open(&conn_string)?;
match database.connect().await {
Ok(conn) => return Ok(conn),
Err(e) => {
eprintln!("Connection attempt {}/5 failed: {}", attempt, e);
last_error = Some(e);
if attempt < 5 {
tokio::time::sleep(Duration::from_secs(2)).await;
}
}
}
}
Err(exarrow_rs::error::ExasolError::Connection(
last_error.unwrap(),
))
}
#[allow(dead_code)]
pub async fn get_test_connection_with_transport(
transport: &str,
) -> Result<Connection, exarrow_rs::error::ExasolError> {
let driver = Driver::new();
let conn_string = get_test_connection_string_with_transport(transport);
let mut last_error = None;
for attempt in 1..=5u32 {
let database = driver.open(&conn_string)?;
match database.connect().await {
Ok(conn) => return Ok(conn),
Err(e) => {
eprintln!(
"Connection attempt {}/5 ({}) failed: {}",
attempt, transport, e
);
last_error = Some(e);
if attempt < 5 {
tokio::time::sleep(Duration::from_secs(2)).await;
}
}
}
}
Err(exarrow_rs::error::ExasolError::Connection(
last_error.unwrap(),
))
}
pub fn is_exasol_available() -> bool {
let host = get_host();
let port = get_port();
let addr = format!("{}:{}", host, port);
let socket_addrs: Vec<_> = match addr.to_socket_addrs() {
Ok(addrs) => addrs.collect(),
Err(_) => return false,
};
for socket_addr in socket_addrs {
if TcpStream::connect_timeout(&socket_addr, Duration::from_secs(2)).is_ok() {
return true;
}
}
false
}
#[macro_export]
macro_rules! skip_if_no_exasol {
() => {
if !$crate::common::is_exasol_available() {
if std::env::var("REQUIRE_EXASOL").is_ok() {
panic!(
"REQUIRE_EXASOL is set but Exasol is not available at {}:{}",
$crate::common::get_host(),
$crate::common::get_port()
);
}
eprintln!(
"Skipping test: Exasol not available at {}:{}",
$crate::common::get_host(),
$crate::common::get_port()
);
return;
}
};
}
pub fn generate_unique_test_name(prefix: &str) -> String {
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos();
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
format!("{}_{}_{}_{}", prefix, pid, timestamp, counter)
}
pub fn generate_test_schema_name() -> String {
generate_unique_test_name("TEST_INTEGRATION")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_constants() {
assert_eq!(DEFAULT_HOST, "localhost");
assert_eq!(DEFAULT_PORT, 8563);
assert_eq!(DEFAULT_USER, "sys");
assert_eq!(DEFAULT_PASSWORD, "exasol");
}
#[test]
fn test_connection_string_format() {
let conn_str = connection_string("sys", "exasol", "localhost", 8563);
assert_eq!(
conn_str,
"exasol://sys:exasol@localhost:8563?tls=true&validateservercertificate=0"
);
}
#[test]
fn test_generate_test_schema_name() {
let schema1 = generate_test_schema_name();
let schema2 = generate_test_schema_name();
assert!(schema1.starts_with("TEST_INTEGRATION_"));
assert!(schema2.starts_with("TEST_INTEGRATION_"));
assert_ne!(schema1, schema2);
assert!(schema1.len() > 17); }
}