#![cfg(feature = "state_machine")]
#[cfg(feature = "legacy-heuristics")]
use cqlite_core::Value;
use cqlite_core::{Config, Database};
use tempfile::TempDir;
#[tokio::test]
#[cfg(all(feature = "legacy-heuristics", feature = "experimental"))]
async fn test_database_lifecycle_with_cassandra_tables() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config.clone())
.await
.expect("database should open");
db.execute(
"CREATE TABLE IF NOT EXISTS ks_metrics (
id INT PRIMARY KEY,
reading DOUBLE
)",
)
.await
.expect("create table should succeed");
db.execute("INSERT INTO ks_metrics (id, reading) VALUES (1, 42.0)")
.await
.expect("insert row");
db.execute("INSERT INTO ks_metrics (id, reading) VALUES (2, 21.0)")
.await
.expect("insert second row");
db.execute("SELECT reading FROM ks_metrics WHERE id = 1")
.await
.expect("point lookup should succeed");
let prepared = db
.prepare("SELECT reading FROM ks_metrics WHERE id = ?")
.await
.expect("prepare query");
prepared
.execute(&[Value::Integer(2)])
.await
.expect("execute prepared");
let explain = db
.explain("SELECT reading FROM ks_metrics WHERE id = 1")
.await
.expect("explain query");
assert_eq!(explain.query_type, "Select");
let _stats = db.stats().await.expect("stats");
assert_eq!(db.config().storage.block_size, config.storage.block_size);
db.flush().await.expect("flush");
db.compact().await.expect("compact noop");
}
#[tokio::test]
async fn test_database_open_failure_on_unwritable_path() {
let temp_dir = TempDir::new().expect("temp dir");
let file_path = temp_dir.path().join("cassandra-data-marker");
std::fs::write(&file_path, b"occupied").expect("write marker file");
let result = Database::open(&file_path, Config::default()).await;
assert!(result.is_err(), "expected open to fail on file path");
}
#[tokio::test]
async fn test_database_initialization_success() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config.clone())
.await
.expect("Database creation should succeed");
assert_eq!(db.config().storage.block_size, config.storage.block_size);
let result = db
.execute("CREATE TABLE test_init (id INT PRIMARY KEY)")
.await
.expect("Query execution should work");
assert_eq!(result.rows_affected, 0);
}
#[tokio::test]
#[cfg(feature = "experimental")]
async fn test_database_initialization_failures() {
let _invalid_config = Config {
storage: cqlite_core::config::StorageConfig {
max_sstable_size: 0, ..Default::default()
},
..Config::default()
};
let temp_dir = TempDir::new().expect("temp dir");
let file_path = temp_dir.path().join("not_a_directory.txt");
std::fs::write(&file_path, b"invalid").expect("write file");
let result = Database::open(&file_path, Config::default()).await;
assert!(result.is_err(), "Database open should fail on invalid path");
let readonly_dir = temp_dir.path().join("readonly");
std::fs::create_dir(&readonly_dir).expect("create readonly dir");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&readonly_dir).unwrap().permissions();
perms.set_mode(0o444); std::fs::set_permissions(&readonly_dir, perms).expect("set permissions");
let result = Database::open(&readonly_dir, Config::default()).await;
assert!(
result.is_err(),
"Database open should fail on read-only directory"
);
}
}
#[tokio::test]
#[cfg(feature = "experimental")]
async fn test_database_clone_functionality() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config)
.await
.expect("Database creation should succeed");
let db_clone = db.clone();
assert_eq!(
db.config().storage.block_size,
db_clone.config().storage.block_size
);
assert_eq!(
db.config().memory.max_memory,
db_clone.config().memory.max_memory
);
db.execute("CREATE TABLE clone_test (id INT PRIMARY KEY, name TEXT)")
.await
.expect("Original DB should work");
db_clone
.execute("INSERT INTO clone_test (id, name) VALUES (1, 'test')")
.await
.expect("Cloned DB should work");
let result = db
.execute("SELECT * FROM clone_test WHERE id = 1")
.await
.expect("Query should succeed");
assert_eq!(result.rows.len(), 1);
let result_clone = db_clone
.execute("SELECT * FROM clone_test WHERE id = 1")
.await
.expect("Clone query should succeed");
assert_eq!(result_clone.rows.len(), 1);
}
#[tokio::test]
#[cfg(feature = "experimental")]
async fn test_database_stats_retrieval() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config)
.await
.expect("Database creation should succeed");
let stats = db.stats().await.expect("Stats retrieval should succeed");
let _total_size = stats.storage_stats.sstables.total_size;
let _memory_used = stats.memory_stats.total_memory_used;
assert_eq!(stats.query_stats.total_queries, 0);
db.execute("CREATE TABLE stats_test (id INT PRIMARY KEY)")
.await
.expect("Create table should succeed");
db.execute("INSERT INTO stats_test (id) VALUES (1)")
.await
.expect("Insert should succeed");
let updated_stats = db.stats().await.expect("Updated stats should succeed");
assert!(updated_stats.query_stats.total_queries > 0);
}
#[tokio::test]
#[cfg(feature = "legacy-heuristics")]
async fn test_database_component_interactions() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config)
.await
.expect("Database creation should succeed");
db.execute("CREATE TABLE component_test (id INT PRIMARY KEY, data TEXT, timestamp BIGINT)")
.await
.expect("Schema creation should succeed");
for i in 1..=10 {
db.execute(&format!(
"INSERT INTO component_test (id, data, timestamp) VALUES ({}, 'data{}', {})",
i,
i,
i * 1000
))
.await
.expect("Insert should succeed");
}
let _stats = db.stats().await.expect("Stats should work");
let stats_before = db.stats().await.expect("Stats should work");
for i in 11..=100 {
db.execute(&format!(
"INSERT INTO component_test (id, data, timestamp) VALUES ({}, 'large_data_{}', {})",
i,
i,
i * 1000
))
.await
.expect("Bulk insert should succeed");
}
let stats_after = db.stats().await.expect("Stats should work");
assert!(
stats_after.memory_stats.total_memory_used >= stats_before.memory_stats.total_memory_used
);
assert!(stats_after.query_stats.total_queries > stats_before.query_stats.total_queries);
let _prepared = db
.prepare(
"INSERT INTO component_test (id, data, timestamp) VALUES (101, 'prepared', 101000)",
)
.await
.expect("Prepare INSERT should succeed");
db.flush().await.expect("Flush should succeed");
db.compact().await.expect("Compact should succeed");
}
#[tokio::test]
#[cfg(feature = "experimental")]
async fn test_database_error_recovery() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config)
.await
.expect("Database creation should succeed");
let result = db.execute("INVALID SQL STATEMENT").await;
assert!(result.is_err(), "Invalid SQL should fail");
db.execute("CREATE TABLE error_recovery (id INT PRIMARY KEY)")
.await
.expect("Database should recover from error");
let result = db
.execute("INSERT INTO nonexistent_table (id) VALUES (1)")
.await;
if result.is_ok() {
println!("Note: Database allows insertion to nonexistent tables (current behavior)");
} else {
println!("Database properly rejects insertion to nonexistent tables");
}
db.execute("INSERT INTO error_recovery (id) VALUES (1)")
.await
.expect("Database should work after error");
let stats = db.stats().await.expect("Stats should work after errors");
assert!(stats.query_stats.error_queries > 0);
}
#[tokio::test]
#[cfg(feature = "experimental")]
async fn test_database_concurrent_operations() {
let temp_dir = TempDir::new().expect("temp dir");
let config = Config::default();
let db = Database::open(temp_dir.path(), config)
.await
.expect("Database creation should succeed");
db.execute("CREATE TABLE concurrent_test (id INT PRIMARY KEY, thread_id INT)")
.await
.expect("Table creation should succeed");
let db1 = db.clone();
let db2 = db.clone();
let db3 = db.clone();
let handles = vec![
tokio::spawn(async move {
for i in 1..=10 {
db1.execute(&format!(
"INSERT INTO concurrent_test (id, thread_id) VALUES ({}, 1)",
i * 100 + i
))
.await
.expect("Concurrent insert 1 should succeed");
}
}),
tokio::spawn(async move {
for i in 1..=10 {
db2.execute(&format!(
"INSERT INTO concurrent_test (id, thread_id) VALUES ({}, 2)",
i * 200 + i
))
.await
.expect("Concurrent insert 2 should succeed");
}
}),
tokio::spawn(async move {
for i in 1..=10 {
db3.execute(&format!(
"INSERT INTO concurrent_test (id, thread_id) VALUES ({}, 3)",
i * 300 + i
))
.await
.expect("Concurrent insert 3 should succeed");
}
}),
];
for handle in handles {
handle.await.expect("Concurrent operation should complete");
}
let _stats = db.stats().await.expect("Stats should work");
}