Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/vil_db_cassandra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ vil_connector_macros = { workspace = true }
tokio = { workspace = true }
serde = { workspace = true }
scylla = "0.14"

[dev-dependencies]
serde_json = "1"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
25 changes: 25 additions & 0 deletions crates/vil_db_cassandra/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Integration tests for `vil_db_cassandra` — require a live Cassandra/ScyllaDB.
//!
//! These tests are `#[ignore]` by default. To run:
//! ```bash
//! docker run -d --name scylla -p 9042:9042 scylladb/scylla
//! VIL_CASSANDRA_ADDR=127.0.0.1:9042 VIL_CASSANDRA_KEYSPACE=system \
//! cargo test -p vil_db_cassandra --test integration -- --ignored
//! ```

use vil_db_cassandra::{CassandraClient, CassandraConfig};

fn addr() -> String {
std::env::var("VIL_CASSANDRA_ADDR").unwrap_or_else(|_| "127.0.0.1:9042".to_string())
}
fn keyspace() -> String {
std::env::var("VIL_CASSANDRA_KEYSPACE").unwrap_or_else(|_| "system".to_string())
}

#[tokio::test]
#[ignore = "requires a live Cassandra/ScyllaDB node"]
async fn connects_to_cluster() {
let cfg = CassandraConfig::new(addr(), keyspace());
let client = CassandraClient::new(cfg).await;
assert!(client.is_ok(), "expected a successful connection");
}
46 changes: 46 additions & 0 deletions crates/vil_db_cassandra/tests/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Pure unit tests for `vil_db_cassandra` — no live cluster required.
//! Run: `cargo test -p vil_db_cassandra`

use vil_db_cassandra::{CassandraConfig, CassandraFault};

#[test]
fn config_new_sets_single_contact_point_and_defaults() {
let cfg = CassandraConfig::new("127.0.0.1:9042", "myapp");
assert_eq!(cfg.contact_points, vec!["127.0.0.1:9042".to_string()]);
assert_eq!(cfg.keyspace, "myapp");
assert_eq!(cfg.pool_id, 0);
}

#[test]
fn config_struct_literal_supports_multiple_contact_points() {
let cfg = CassandraConfig {
contact_points: vec!["a:9042".into(), "b:9042".into()],
keyspace: "k".into(),
pool_id: 7,
};
assert_eq!(cfg.contact_points.len(), 2);
assert_eq!(cfg.pool_id, 7);
}

#[test]
fn config_serde_round_trip() {
let cfg = CassandraConfig::new("10.0.0.1:9042", "ks");
let json = serde_json::to_string(&cfg).expect("serialize");
let back: CassandraConfig = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.contact_points, cfg.contact_points);
assert_eq!(back.keyspace, cfg.keyspace);
assert_eq!(back.pool_id, cfg.pool_id);
}

#[test]
fn fault_metadata_is_consistent() {
let conn = CassandraFault::ConnectionFailed { uri_hash: 1, reason_code: 2 };
assert_eq!(conn.kind(), "ConnectionFailed");
assert!(conn.is_retryable());
assert!(conn.error_code() >= 1);

let q = CassandraFault::QueryFailed { query_hash: 9, reason_code: 7 };
assert_eq!(q.kind(), "QueryFailed");
assert!(!q.is_retryable());
assert_ne!(conn.error_code(), q.error_code());
}
3 changes: 3 additions & 0 deletions crates/vil_db_clickhouse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ vil_connector_macros = { workspace = true }
clickhouse = { version = "0.12", features = ["inserter"] }
tokio = { workspace = true }
serde = { workspace = true }

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
22 changes: 22 additions & 0 deletions crates/vil_db_clickhouse/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Integration tests for `vil_db_clickhouse` — require a live ClickHouse.
//!
//! `#[ignore]` by default. To run:
//! ```bash
//! docker run -d --name clickhouse -p 8123:8123 clickhouse/clickhouse-server
//! VIL_CLICKHOUSE_URL=http://localhost:8123 \
//! cargo test -p vil_db_clickhouse --test integration -- --ignored
//! ```

use vil_db_clickhouse::{ChClient, ClickHouseConfig};

#[tokio::test]
#[ignore = "requires a live ClickHouse server"]
async fn connects_and_executes() {
let mut cfg = ClickHouseConfig::default();
if let Ok(url) = std::env::var("VIL_CLICKHOUSE_URL") {
cfg.url = url;
}
let client = ChClient::new(cfg);
let res = client.execute("SELECT 1").await;
assert!(res.is_ok(), "expected query to succeed against live ClickHouse");
}
37 changes: 37 additions & 0 deletions crates/vil_db_clickhouse/tests/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! Pure unit tests for `vil_db_clickhouse` — no live server required.
//! Run: `cargo test -p vil_db_clickhouse`

use vil_db_clickhouse::{ChFault, ClickHouseConfig};

#[test]
fn default_config_values() {
let cfg = ClickHouseConfig::default();
assert_eq!(cfg.url, "http://localhost:8123");
assert_eq!(cfg.database, "default");
assert!(cfg.username.is_none());
assert!(cfg.password.is_none());
}

#[test]
fn struct_literal_with_auth() {
let cfg = ClickHouseConfig {
url: "http://ch:8123".into(),
database: "analytics".into(),
username: Some("default".into()),
password: None,
};
assert_eq!(cfg.database, "analytics");
assert_eq!(cfg.username.as_deref(), Some("default"));
}

#[test]
fn fault_metadata_is_consistent() {
let to = ChFault::Timeout { operation_hash: 1, elapsed_ms: 10 };
assert_eq!(to.kind(), "Timeout");
assert!(to.is_retryable());

let qf = ChFault::QueryFailed { query_hash: 2, reason_code: 3 };
assert_eq!(qf.kind(), "QueryFailed");
assert!(!qf.is_retryable());
assert_ne!(to.error_code(), qf.error_code());
}
4 changes: 4 additions & 0 deletions crates/vil_db_dynamodb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ tokio = { workspace = true }
serde = { workspace = true }
aws-sdk-dynamodb = "1.55"
aws-config = { version = "1.5", features = ["behavior-version-latest"] }

[dev-dependencies]
serde_json = "1"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
21 changes: 21 additions & 0 deletions crates/vil_db_dynamodb/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! Integration tests for `vil_db_dynamodb` — require a DynamoDB-compatible endpoint.
//!
//! `#[ignore]` by default. To run against LocalStack:
//! ```bash
//! docker run -d --name localstack -p 4566:4566 localstack/localstack
//! VIL_DYNAMO_REGION=us-east-1 VIL_DYNAMO_ENDPOINT=http://localhost:4566 \
//! cargo test -p vil_db_dynamodb --test integration -- --ignored
//! ```

use vil_db_dynamodb::{DynamoClient, DynamoConfig};

#[tokio::test]
#[ignore = "requires a live DynamoDB/LocalStack endpoint"]
async fn builds_client() {
let region = std::env::var("VIL_DYNAMO_REGION").unwrap_or_else(|_| "us-east-1".to_string());
let endpoint =
std::env::var("VIL_DYNAMO_ENDPOINT").unwrap_or_else(|_| "http://localhost:4566".to_string());
let cfg = DynamoConfig::new(region).with_endpoint(endpoint);
let client = DynamoClient::new(cfg).await;
assert!(client.is_ok(), "expected client construction to succeed");
}
40 changes: 40 additions & 0 deletions crates/vil_db_dynamodb/tests/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! Pure unit tests for `vil_db_dynamodb` — no AWS endpoint required.
//! Run: `cargo test -p vil_db_dynamodb`

use vil_db_dynamodb::{DynamoConfig, DynamoFault};

#[test]
fn config_new_defaults() {
let cfg = DynamoConfig::new("us-east-1");
assert_eq!(cfg.region, "us-east-1");
assert!(cfg.endpoint_url.is_none());
assert_eq!(cfg.pool_id, 0);
}

#[test]
fn with_endpoint_overrides_url() {
let cfg = DynamoConfig::new("us-west-2").with_endpoint("http://localhost:4566");
assert_eq!(cfg.region, "us-west-2");
assert_eq!(cfg.endpoint_url.as_deref(), Some("http://localhost:4566"));
}

#[test]
fn config_serde_round_trip() {
let cfg = DynamoConfig::new("eu-west-1").with_endpoint("http://ls:4566");
let json = serde_json::to_string(&cfg).expect("serialize");
let back: DynamoConfig = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.region, cfg.region);
assert_eq!(back.endpoint_url, cfg.endpoint_url);
assert_eq!(back.pool_id, cfg.pool_id);
}

#[test]
fn fault_metadata_is_consistent() {
let get = DynamoFault::GetFailed { table_hash: 1, reason_code: 2 };
assert_eq!(get.kind(), "GetFailed");
assert!(!get.is_retryable());

let cfg_f = DynamoFault::ConfigFailed { reason_code: 9 };
assert_eq!(cfg_f.kind(), "ConfigFailed");
assert_ne!(get.error_code(), cfg_f.error_code());
}
3 changes: 3 additions & 0 deletions crates/vil_db_elastic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ tokio = { workspace = true }
serde = { workspace = true }
serde_json = "1.0"
bytes = "1"

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
25 changes: 25 additions & 0 deletions crates/vil_db_elastic/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Integration tests for `vil_db_elastic` — require a live Elasticsearch/OpenSearch.
//!
//! `#[ignore]` by default. To run:
//! ```bash
//! docker run -d --name es -p 9200:9200 -e discovery.type=single-node \
//! docker.elastic.co/elasticsearch/elasticsearch:8.15.0
//! VIL_ELASTIC_URL=http://localhost:9200 \
//! cargo test -p vil_db_elastic --test integration -- --ignored
//! ```

use vil_db_elastic::{ElasticClient, ElasticConfig};

#[tokio::test]
#[ignore = "requires a live Elasticsearch/OpenSearch node"]
async fn connects_and_queries() {
let url = std::env::var("VIL_ELASTIC_URL").unwrap_or_else(|_| "http://localhost:9200".to_string());
let cfg = ElasticConfig { url, username: None, password: None };
let client = match ElasticClient::new(cfg) {
Ok(c) => c,
Err(_) => panic!("client init failed"),
};
// Touching the server requires a live node; a missing document simply
// returns an error, which is acceptable for this smoke test.
let _ = client.get("vil-test-index", "missing-doc").await;
}
41 changes: 41 additions & 0 deletions crates/vil_db_elastic/tests/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! Pure unit tests for `vil_db_elastic` — no live node required.
//! Run: `cargo test -p vil_db_elastic`

use vil_db_elastic::{ElasticConfig, ElasticFault};

#[test]
fn default_config_values() {
let cfg = ElasticConfig::default();
assert_eq!(cfg.url, "http://localhost:9200");
assert!(cfg.username.is_none());
assert!(cfg.password.is_none());
}

#[test]
fn config_serde_round_trip() {
let cfg = ElasticConfig {
url: "http://es:9200".into(),
username: Some("elastic".into()),
password: Some("changeme".into()),
};
let json = serde_json::to_string(&cfg).expect("serialize");
let back: ElasticConfig = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.url, cfg.url);
assert_eq!(back.username, cfg.username);
assert_eq!(back.password, cfg.password);
}

#[test]
fn fault_metadata_is_consistent() {
let conn = ElasticFault::ConnectionFailed { url_hash: 1, reason_code: 2 };
assert_eq!(conn.kind(), "ConnectionFailed");
assert!(conn.is_retryable());

let nf = ElasticFault::NotFound { index_hash: 1, id_hash: 2 };
assert_eq!(nf.kind(), "NotFound");
assert!(!nf.is_retryable());

let to = ElasticFault::Timeout { operation_hash: 1, elapsed_ms: 5 };
assert!(to.is_retryable());
assert_ne!(conn.error_code(), nf.error_code());
}
3 changes: 3 additions & 0 deletions crates/vil_db_mongo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ serde = { workspace = true }
serde_json = { workspace = true }
bson = "2.13"
futures-util = "0.3"

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
19 changes: 19 additions & 0 deletions crates/vil_db_mongo/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Integration tests for `vil_db_mongo` — require a live MongoDB.
//!
//! `#[ignore]` by default. To run:
//! ```bash
//! docker run -d --name mongo -p 27017:27017 mongo:7
//! VIL_MONGO_URI=mongodb://localhost:27017 VIL_MONGO_DB=vil_test \
//! cargo test -p vil_db_mongo --test integration -- --ignored
//! ```

use vil_db_mongo::{MongoClient, MongoConfig};

#[tokio::test]
#[ignore = "requires a live MongoDB instance"]
async fn connects_to_server() {
let uri = std::env::var("VIL_MONGO_URI").unwrap_or_else(|_| "mongodb://localhost:27017".to_string());
let db = std::env::var("VIL_MONGO_DB").unwrap_or_else(|_| "vil_test".to_string());
let client = MongoClient::new(MongoConfig::new(uri, db)).await;
assert!(client.is_ok(), "expected a successful connection");
}
43 changes: 43 additions & 0 deletions crates/vil_db_mongo/tests/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Pure unit tests for `vil_db_mongo` — no live MongoDB required.
//! Run: `cargo test -p vil_db_mongo`

use vil_db_mongo::{MongoConfig, MongoFault};

#[test]
fn config_new_defaults() {
let cfg = MongoConfig::new("mongodb://localhost:27017", "myapp");
assert_eq!(cfg.uri, "mongodb://localhost:27017");
assert_eq!(cfg.database, "myapp");
assert!(cfg.min_pool.is_none());
assert!(cfg.max_pool.is_none());
}

#[test]
fn config_serde_round_trip() {
let mut cfg = MongoConfig::new("mongodb://h:27017", "db");
cfg.min_pool = Some(2);
cfg.max_pool = Some(16);
let json = serde_json::to_string(&cfg).expect("serialize");
let back: MongoConfig = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.min_pool, Some(2));
assert_eq!(back.max_pool, Some(16));
}

#[test]
fn deserialize_minimal_json_defaults_pool_to_none() {
let back: MongoConfig =
serde_json::from_str(r#"{"uri":"mongodb://x","database":"d"}"#).expect("deserialize");
assert!(back.min_pool.is_none());
assert!(back.max_pool.is_none());
}

#[test]
fn fault_metadata_is_consistent() {
let conn = MongoFault::ConnectionFailed { uri_hash: 1, reason_code: 2 };
assert_eq!(conn.kind(), "ConnectionFailed");
assert!(conn.is_retryable());

let ins = MongoFault::InsertFailed { collection_hash: 3, reason_code: 4 };
assert_eq!(ins.kind(), "InsertFailed");
assert!(!ins.is_retryable());
}
4 changes: 4 additions & 0 deletions crates/vil_db_neo4j/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ vil_connector_macros = { workspace = true }
tokio = { workspace = true }
serde = { workspace = true }
neo4rs = "0.8"

[dev-dependencies]
serde_json = "1"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
20 changes: 20 additions & 0 deletions crates/vil_db_neo4j/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! Integration tests for `vil_db_neo4j` — require a live Neo4j.
//!
//! `#[ignore]` by default. To run:
//! ```bash
//! docker run -d --name neo4j -p 7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:5
//! VIL_NEO4J_URI=bolt://localhost:7687 VIL_NEO4J_USER=neo4j VIL_NEO4J_PASSWORD=password \
//! cargo test -p vil_db_neo4j --test integration -- --ignored
//! ```

use vil_db_neo4j::{Neo4jClient, Neo4jConfig};

#[tokio::test]
#[ignore = "requires a live Neo4j instance"]
async fn connects_to_server() {
let uri = std::env::var("VIL_NEO4J_URI").unwrap_or_else(|_| "bolt://localhost:7687".to_string());
let user = std::env::var("VIL_NEO4J_USER").unwrap_or_else(|_| "neo4j".to_string());
let password = std::env::var("VIL_NEO4J_PASSWORD").unwrap_or_else(|_| "password".to_string());
let client = Neo4jClient::new(Neo4jConfig::new(uri, user, password)).await;
assert!(client.is_ok(), "expected a successful connection");
}
Loading