use fraiseql_wire::connection::TlsConfig;
fn install_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
#[test]
fn test_tls_config_builder() {
install_crypto_provider();
let config = TlsConfig::builder()
.verify_hostname(true)
.build()
.expect("TLS config builder should create valid config");
drop(config);
}
#[test]
fn test_tls_config_cloneable() {
install_crypto_provider();
let config = TlsConfig::builder()
.verify_hostname(true)
.build()
.expect("Failed to build TLS config");
let cloned = config.clone();
drop(config);
drop(cloned);
}
#[test]
fn test_tls_hostname_verification_setting() {
install_crypto_provider();
let _strict_config = TlsConfig::builder()
.verify_hostname(true)
.build()
.expect("Strict TLS config should be valid");
let _dev_config = TlsConfig::builder()
.verify_hostname(false)
.build()
.expect("Dev TLS config should be valid");
}
#[cfg(test)]
mod tls_integration {
use super::*;
use fraiseql_wire::FraiseClient;
use futures::StreamExt;
use serde_json::Value;
use std::env;
fn require_tls_test_config() -> Option<(String, Option<String>)> {
let db_url = env::var("TLS_DATABASE_URL")
.or_else(|_| env::var("DATABASE_URL"))
.ok()?;
let ca_cert_path = env::var("TLS_TEST_CA_CERT").ok();
Some((db_url, ca_cert_path))
}
fn build_tls_config(ca_cert_path: Option<&str>) -> TlsConfig {
let mut builder = TlsConfig::builder();
if let Some(path) = ca_cert_path {
builder = builder.ca_cert_path(path);
}
builder.build().expect("Failed to build TLS config")
}
#[tokio::test]
async fn test_tls_connection_succeeds() {
install_crypto_provider();
let Some((db_url, ca_cert_path)) = require_tls_test_config() else {
eprintln!("Skipping: TLS_DATABASE_URL/DATABASE_URL not set");
return;
};
let tls_config = build_tls_config(ca_cert_path.as_deref());
let client = FraiseClient::connect_tls(&db_url, tls_config)
.await
.expect("Failed to connect with TLS");
let mut stream = client
.query::<Value>("v_test_entity")
.execute()
.await
.expect("Failed to execute query with TLS connection");
let result = stream.next().await;
assert!(result.is_some(), "Should receive at least one row");
}
#[tokio::test]
async fn test_tls_with_password_auth() {
install_crypto_provider();
let Some((db_url, ca_cert_path)) = require_tls_test_config() else {
eprintln!("Skipping: TLS_DATABASE_URL/DATABASE_URL not set");
return;
};
let tls_config = build_tls_config(ca_cert_path.as_deref());
let client = FraiseClient::connect_tls(&db_url, tls_config)
.await
.expect("TLS connection with password auth should succeed");
let mut stream = client
.query::<Value>("v_test_entity")
.execute()
.await
.expect("Query execution should succeed after TLS auth");
let first = stream.next().await;
assert!(
first.is_some(),
"Should receive at least one row over TLS with password auth"
);
}
#[tokio::test]
async fn test_multiple_tls_connections() {
install_crypto_provider();
let Some((db_url, ca_cert_path)) = require_tls_test_config() else {
eprintln!("Skipping: TLS_DATABASE_URL/DATABASE_URL not set");
return;
};
let tls_config = build_tls_config(ca_cert_path.as_deref());
let mut connections = Vec::new();
for i in 0..3 {
let client = FraiseClient::connect_tls(&db_url, tls_config.clone())
.await
.unwrap_or_else(|e| panic!("Failed to create TLS connection {}: {}", i + 1, e));
connections.push(client);
}
assert_eq!(
connections.len(),
3,
"Should have created 3 TLS connections"
);
for (i, client) in connections.into_iter().enumerate() {
let mut stream = client
.query::<Value>("v_test_entity")
.execute()
.await
.unwrap_or_else(|e| panic!("TLS connection {} query failed: {}", i + 1, e));
let result = stream.next().await;
assert!(
result.is_some(),
"TLS connection {} should return at least one row",
i + 1
);
}
}
#[tokio::test]
async fn test_tls_streaming() {
install_crypto_provider();
let Some((db_url, ca_cert_path)) = require_tls_test_config() else {
eprintln!("Skipping: TLS_DATABASE_URL/DATABASE_URL not set");
return;
};
let tls_config = build_tls_config(ca_cert_path.as_deref());
let client = FraiseClient::connect_tls(&db_url, tls_config)
.await
.expect("Failed to connect with TLS");
let mut stream = client
.query::<Value>("v_test_entity")
.execute()
.await
.expect("Failed to execute streaming query over TLS");
let mut count = 0;
while let Some(result) = stream.next().await {
result.unwrap_or_else(|e| panic!("Stream row {} failed: {}", count + 1, e));
count += 1;
if count >= 10 {
break;
}
}
assert!(count >= 10, "Should receive at least 10 rows, got {count}");
}
}